├── .bumpversion.cfg ├── .ci-package-locks ├── code-quality │ ├── python3.12.txt │ └── python3.9.txt ├── integration-vue3 │ └── osubuntu-ipywidgets8.txt ├── integration │ ├── osubuntu-python3.9-ipywidgets7.txt │ ├── osubuntu-python3.9-ipywidgets8.txt │ ├── oswindows-python3.9-ipywidgets7.txt │ └── oswindows-python3.9-ipywidgets8.txt ├── pyinstaller │ ├── osmacos-python3.10.txt │ ├── osubuntu-python3.10.txt │ └── oswindows-python3.10.txt └── unit │ ├── osmacos-python3.12-ipywidgets7.7.txt │ ├── osmacos-python3.12-ipywidgets8.0.txt │ ├── osmacos-python3.6-ipywidgets7.7.txt │ ├── osmacos-python3.7-ipywidgets7.7.txt │ ├── osmacos-python3.9-ipywidgets7.7.txt │ ├── osmacos-python3.9-ipywidgets8.0.txt │ ├── osubuntu-python3.12-ipywidgets7.7.txt │ ├── osubuntu-python3.12-ipywidgets8.0.txt │ ├── osubuntu-python3.6-ipywidgets7.7.txt │ ├── osubuntu-python3.7-ipywidgets7.7.txt │ ├── osubuntu-python3.9-ipywidgets7.7.txt │ ├── osubuntu-python3.9-ipywidgets8.0.txt │ ├── oswindows-python3.12-ipywidgets7.7.txt │ ├── oswindows-python3.12-ipywidgets8.0.txt │ ├── oswindows-python3.9-ipywidgets7.7.txt │ └── oswindows-python3.9-ipywidgets8.0.txt ├── .flake8 ├── .github ├── ISSUE_TEMPLATE │ ├── bug.md │ └── feature_request.md ├── dependabot.yml ├── pull_request_template.md ├── pycafe-create-status.py └── workflows │ ├── pycafe.yml │ ├── release_solara_vuetify_app-v3.yaml │ ├── release_solara_vuetify_app.yaml │ ├── test.yaml │ └── webdeploy.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── Procfile ├── README.md ├── SECURITY.md ├── mypy.ini ├── packages ├── pytest-ipywidgets │ ├── LICENSE │ ├── README.md │ └── pyproject.toml ├── solara-assets │ ├── .gitignore │ ├── LICENSE │ ├── RELEASE.md │ ├── download_cdn_test.py │ ├── hatch_build.py │ ├── pyproject.toml │ └── solara_assets │ │ └── __init__.py ├── solara-enterprise │ ├── LICENSE │ ├── RELEASE.md │ ├── pyproject.toml │ └── solara_enterprise │ │ ├── __init__.py │ │ ├── auth │ │ ├── __init__.py │ │ ├── components.py │ │ ├── flask.py │ │ ├── middleware.py │ │ ├── starlette.py │ │ └── utils.py │ │ ├── cache │ │ ├── __init__.py │ │ ├── base.py │ │ ├── disk.py │ │ ├── memory_size.py │ │ ├── multi_level.py │ │ └── redis.py │ │ ├── license.py │ │ ├── search │ │ ├── __init__.py │ │ ├── index.py │ │ ├── search.py │ │ └── search.vue │ │ └── ssg.py ├── solara-meta │ ├── LICENSE │ ├── README.md │ ├── main.README.md │ └── pyproject.toml ├── solara-milkdown │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.js │ └── webpack.config.js ├── solara-server │ ├── LICENSE │ ├── README.md │ └── pyproject.toml ├── solara-vuetify-app │ ├── .bumpversion.cfg │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── release.sh │ ├── src │ │ ├── empty.js │ │ ├── fonts.js │ │ ├── solara-vuetify-app.js │ │ └── solara.js │ └── webpack.config.js ├── solara-vuetify3-app │ ├── .bumpversion.cfg │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── release.sh │ ├── src │ │ ├── fonts.js │ │ ├── solara-vuetify-app.js │ │ └── solara.js │ └── webpack.config.js ├── solara-widget-manager │ ├── LICENSE.voila.txt │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── katex.ts │ │ ├── kernel.ts │ │ ├── loader.ts │ │ ├── manager.ts │ │ ├── rendermime.ts │ │ └── typings.d.ts │ ├── style │ │ └── index.css │ └── tsconfig.json └── solara-widget-manager8 │ ├── package-lock.json │ ├── package.json │ ├── patches │ ├── @jupyterlab+outputarea+3.6.4.patch │ └── @jupyterlab+rendermime+3.6.4.patch │ ├── src │ ├── style │ └── tsconfig.json ├── prefix └── etc │ └── jupyter │ ├── jupyter_notebook_config.d │ └── solara.json │ └── jupyter_server_config.d │ └── solara.json ├── pyinstaller ├── entitlements.plist ├── minimal │ ├── sample_app.py │ ├── solara-app-entrypoint.py │ └── solara.spec └── solara.icns ├── pyproject.toml ├── release.md ├── release.sh ├── requirements-dev.txt ├── requirements.txt ├── runtime.txt ├── solara ├── __init__.py ├── __main__.py ├── _stores.py ├── alias.py ├── autorouting.py ├── cache.py ├── checks.html ├── checks.py ├── comm.py ├── components │ ├── __init__.py │ ├── alert.py │ ├── applayout.py │ ├── button.py │ ├── card.py │ ├── checkbox.py │ ├── code_highlight_css.py │ ├── code_highlight_css.vue │ ├── columns.py │ ├── component_vue.py │ ├── cross_filter.py │ ├── dataframe.py │ ├── datatable.py │ ├── datatable.vue │ ├── details.py │ ├── download.vue │ ├── echarts.py │ ├── echarts.vue │ ├── figure_altair.py │ ├── file_browser.py │ ├── file_download.py │ ├── file_drop.py │ ├── file_drop.vue │ ├── file_list_widget.vue │ ├── head.py │ ├── head_tag.py │ ├── head_tag.vue │ ├── image.py │ ├── input.py │ ├── input_text_area.py │ ├── link.py │ ├── markdown.py │ ├── markdown_editor.py │ ├── markdown_editor.vue │ ├── matplotlib.py │ ├── meta.py │ ├── misc.py │ ├── pivot_table.py │ ├── pivot_table.vue │ ├── progress.py │ ├── select.py │ ├── select.vue │ ├── slider.py │ ├── slider_date.vue │ ├── spinner-solara.vue │ ├── spinner.py │ ├── sql_code.py │ ├── sql_code.vue │ ├── style.py │ ├── switch.py │ ├── tab_navigation.py │ ├── title.py │ ├── title.vue │ ├── togglebuttons.py │ └── tooltip.py ├── core.py ├── datatypes.py ├── express.py ├── hooks │ ├── __init__.py │ ├── dataframe.py │ ├── misc.py │ ├── use_reactive.py │ └── use_thread.py ├── kitchensink.py ├── lab │ ├── __init__.py │ ├── components │ │ ├── __init__.py │ │ ├── chat.py │ │ ├── confirmation_dialog.py │ │ ├── cross_filter.py │ │ ├── input_date.py │ │ ├── input_time.py │ │ ├── menu.py │ │ ├── menu.vue │ │ ├── tabs.py │ │ ├── theming.py │ │ └── theming.vue │ ├── hooks │ │ ├── __init__.py │ │ └── dataframe.py │ ├── toestand.py │ └── utils │ │ ├── __init__.py │ │ ├── cookies.py │ │ ├── dataframe.py │ │ └── headers.py ├── layout.py ├── lifecycle.py ├── minisettings.py ├── py.typed ├── reactive.py ├── routing.py ├── scope │ ├── __init__.py │ └── types.py ├── server │ ├── __init__.py │ ├── app.py │ ├── assets │ │ ├── custom.css │ │ ├── custom.js │ │ ├── favicon.png │ │ ├── favicon.svg │ │ ├── style.css │ │ ├── theme-dark.css │ │ ├── theme-light.css │ │ └── theme.js │ ├── cdn_helper.py │ ├── esm.py │ ├── fastapi.py │ ├── flask.py │ ├── jupyter │ │ ├── __init__.py │ │ ├── cdn_handler.py │ │ ├── server_extension.py │ │ └── solara.py │ ├── jupytertools.py │ ├── kernel.py │ ├── kernel_context.py │ ├── patch.py │ ├── pyinstaller │ │ ├── __init__.py │ │ ├── hook-ipyreact.py │ │ ├── hook-ipyvuetify.py │ │ └── hook-solara.py │ ├── qt.py │ ├── reload.py │ ├── server.py │ ├── settings.py │ ├── shell.py │ ├── starlette.py │ ├── static │ │ ├── ansi.js │ │ ├── highlight-dark.css │ │ ├── highlight.css │ │ ├── main-vuetify.js │ │ ├── main.js │ │ ├── solara_bootstrap.py │ │ ├── sun.svg │ │ └── webworker.js │ ├── telemetry.py │ ├── templates │ │ ├── index.html.j2 │ │ ├── loader-plain.css │ │ ├── loader-plain.html │ │ ├── loader-solara.css │ │ ├── loader-solara.html │ │ ├── plain.html │ │ └── solara.html.j2 │ ├── threaded.py │ ├── utils.py │ └── websocket.py ├── settings.py ├── tasks.py ├── template │ ├── button.py │ ├── markdown.py │ └── portal │ │ ├── .flake8 │ │ ├── .pre-commit-config.yaml │ │ ├── LICENSE │ │ ├── Procfile │ │ ├── mypy.ini │ │ ├── pyproject.toml │ │ └── solara_portal │ │ ├── __init__.py │ │ ├── components │ │ ├── __init__.py │ │ ├── article.py │ │ ├── data.py │ │ ├── header.py │ │ └── layout.py │ │ ├── content │ │ └── articles │ │ │ ├── equis-in-vidi.md │ │ │ └── substiterat-vati.md │ │ ├── data.py │ │ └── pages │ │ ├── __init__.py │ │ ├── article │ │ └── __init__.py │ │ ├── tabular.py │ │ └── viz │ │ ├── __init__.py │ │ └── overview.py ├── test │ ├── __init__.py │ └── pytest_plugin.py ├── toestand.py ├── util.py ├── validate_hooks.py ├── website │ ├── __init__.py │ ├── assets │ │ ├── custom.css │ │ ├── images │ │ │ ├── logo-small.png │ │ │ ├── logo.svg │ │ │ └── logo_white.svg │ │ └── theme.js │ ├── components │ │ ├── __init__.py │ │ ├── algolia.py │ │ ├── algolia.vue │ │ ├── algolia_api.vue │ │ ├── breadcrumbs.py │ │ ├── contact.py │ │ ├── docs.py │ │ ├── header.py │ │ ├── mailchimp.py │ │ ├── mailchimp.vue │ │ ├── markdown.py │ │ ├── markdown_nav.vue │ │ ├── notebook.py │ │ └── sidebar.py │ ├── pages │ │ ├── __init__.py │ │ ├── about │ │ │ ├── __init__.py │ │ │ └── about.md │ │ ├── apps │ │ │ ├── __init__.py │ │ │ ├── authorization │ │ │ │ ├── __init__.py │ │ │ │ ├── admin.py │ │ │ │ └── users.py │ │ │ ├── jupyter-dashboard-1.py │ │ │ ├── layout-demo.py │ │ │ ├── multipage │ │ │ │ ├── __init__.py │ │ │ │ ├── page1.py │ │ │ │ └── page2.py │ │ │ ├── scatter.py │ │ │ ├── scrolling.py │ │ │ └── tutorial-streamlit.py │ │ ├── careers │ │ │ └── __init__.py │ │ ├── changelog │ │ │ ├── __init__.py │ │ │ └── changelog.md │ │ ├── contact │ │ │ └── __init__.py │ │ ├── doc_use_download.py │ │ ├── documentation │ │ │ ├── __init__.py │ │ │ ├── advanced │ │ │ │ ├── __init__.py │ │ │ │ └── content │ │ │ │ │ ├── 00-overview.md │ │ │ │ │ ├── 10-howto │ │ │ │ │ ├── 00-overview.md │ │ │ │ │ ├── 10-multipage.md │ │ │ │ │ ├── 20-layout.md │ │ │ │ │ ├── 30-testing.md │ │ │ │ │ ├── 31-debugging.md │ │ │ │ │ ├── 40-embed.md │ │ │ │ │ └── 50-ipywidget_libraries.md │ │ │ │ │ ├── 15-reference │ │ │ │ │ ├── 00-overview.md │ │ │ │ │ ├── 40-static_files.md │ │ │ │ │ ├── 41-asset-files.md │ │ │ │ │ ├── 60-static-site-generation.md │ │ │ │ │ ├── 70-search.md │ │ │ │ │ ├── 80-reloading.md │ │ │ │ │ ├── 90-notebook-support.md │ │ │ │ │ └── 95-caching.md │ │ │ │ │ ├── 20-understanding │ │ │ │ │ ├── 00-introduction.md │ │ │ │ │ ├── 05-ipywidgets.md │ │ │ │ │ ├── 06-ipyvuetify.md │ │ │ │ │ ├── 10-reacton.md │ │ │ │ │ ├── 12-reacton-basics.md │ │ │ │ │ ├── 15-anatomy.md │ │ │ │ │ ├── 17-rules-of-hooks.md │ │ │ │ │ ├── 18-containers.md │ │ │ │ │ ├── 20-solara.md │ │ │ │ │ ├── 40-routing.md │ │ │ │ │ ├── 50-solara-server.md │ │ │ │ │ └── 60-voila.md │ │ │ │ │ ├── 30-enterprise │ │ │ │ │ ├── 00-overview.md │ │ │ │ │ └── 10-oauth.md │ │ │ │ │ └── 40-development │ │ │ │ │ ├── 00-overview.md │ │ │ │ │ ├── 01-contribute.md │ │ │ │ │ └── 10-setup.md │ │ │ ├── api │ │ │ │ ├── __init__.py │ │ │ │ ├── cross_filter │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── cross_filter_dataframe.py │ │ │ │ │ ├── cross_filter_report.py │ │ │ │ │ ├── cross_filter_select.py │ │ │ │ │ └── cross_filter_slider.py │ │ │ │ ├── hooks │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── use_cross_filter.py │ │ │ │ │ ├── use_dark_effective.py │ │ │ │ │ ├── use_effect.md │ │ │ │ │ ├── use_effect.py │ │ │ │ │ ├── use_exception.py │ │ │ │ │ ├── use_memo.md │ │ │ │ │ ├── use_memo.py │ │ │ │ │ ├── use_previous.py │ │ │ │ │ ├── use_reactive.py │ │ │ │ │ ├── use_state.py │ │ │ │ │ ├── use_state_or_update.py │ │ │ │ │ ├── use_thread.md │ │ │ │ │ ├── use_thread.py │ │ │ │ │ └── use_trait_observe.py │ │ │ │ ├── routing │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── generate_routes.py │ │ │ │ │ ├── generate_routes_directory.py │ │ │ │ │ ├── resolve_path.py │ │ │ │ │ ├── route.py │ │ │ │ │ ├── use_route.py │ │ │ │ │ └── use_router.py │ │ │ │ └── utilities │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── component_vue.py │ │ │ │ │ ├── computed.py │ │ │ │ │ ├── display.py │ │ │ │ │ ├── get_kernel_id.py │ │ │ │ │ ├── get_session_id.py │ │ │ │ │ ├── memoize.py │ │ │ │ │ ├── on_kernel_start.py │ │ │ │ │ ├── reactive.py │ │ │ │ │ └── widget.py │ │ │ ├── components │ │ │ │ ├── __init__.py │ │ │ │ ├── advanced │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── link.py │ │ │ │ │ ├── meta.py │ │ │ │ │ └── style.py │ │ │ │ ├── common.py │ │ │ │ ├── data │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── dataframe.py │ │ │ │ │ └── pivot_table.py │ │ │ │ ├── enterprise │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── avatar.py │ │ │ │ │ └── avatar_menu.py │ │ │ │ ├── input │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── button.py │ │ │ │ │ ├── checkbox.py │ │ │ │ │ ├── file_browser.py │ │ │ │ │ ├── file_drop.py │ │ │ │ │ ├── input.py │ │ │ │ │ ├── select.py │ │ │ │ │ ├── slider.py │ │ │ │ │ ├── switch.py │ │ │ │ │ └── togglebuttons.py │ │ │ │ ├── lab │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── chat.py │ │ │ │ │ ├── confirmation_dialog.py │ │ │ │ │ ├── cookies_headers.py │ │ │ │ │ ├── input_date.py │ │ │ │ │ ├── input_time.py │ │ │ │ │ ├── menu.py │ │ │ │ │ ├── tab.py │ │ │ │ │ ├── tabs.py │ │ │ │ │ ├── task.py │ │ │ │ │ ├── theming.py │ │ │ │ │ └── use_task.py │ │ │ │ ├── layout │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── app_bar.py │ │ │ │ │ ├── app_bar_title.py │ │ │ │ │ ├── app_layout.py │ │ │ │ │ ├── card.py │ │ │ │ │ ├── card_actions.py │ │ │ │ │ ├── column.py │ │ │ │ │ ├── columns.py │ │ │ │ │ ├── columns_responsive.py │ │ │ │ │ ├── details.py │ │ │ │ │ ├── griddraggable.py │ │ │ │ │ ├── gridfixed.py │ │ │ │ │ ├── hbox.py │ │ │ │ │ ├── row.py │ │ │ │ │ ├── sidebar.py │ │ │ │ │ └── vbox.py │ │ │ │ ├── output │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── file_download.py │ │ │ │ │ ├── html.py │ │ │ │ │ ├── image.py │ │ │ │ │ ├── markdown.py │ │ │ │ │ ├── markdown_editor.py │ │ │ │ │ ├── sql_code.py │ │ │ │ │ └── tooltip.py │ │ │ │ ├── page │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── head.py │ │ │ │ │ └── title.py │ │ │ │ ├── status │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── error.py │ │ │ │ │ ├── info.py │ │ │ │ │ ├── progress.py │ │ │ │ │ ├── spinner.py │ │ │ │ │ ├── success.py │ │ │ │ │ └── warning.py │ │ │ │ └── viz │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── altair.py │ │ │ │ │ ├── echarts.py │ │ │ │ │ ├── matplotlib.py │ │ │ │ │ ├── plotly.py │ │ │ │ │ └── plotly_express.py │ │ │ ├── examples │ │ │ │ ├── __init__.py │ │ │ │ ├── ai │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── chatbot.py │ │ │ │ │ └── tokenizer.py │ │ │ │ ├── basics │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── sine.py │ │ │ │ ├── fullscreen │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── authorization.py │ │ │ │ │ ├── layout_demo.py │ │ │ │ │ ├── multipage.py │ │ │ │ │ ├── scatter.py │ │ │ │ │ ├── scrolling.py │ │ │ │ │ └── tutorial_streamlit.py │ │ │ │ ├── general │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── custom_storage.py │ │ │ │ │ ├── deploy_model.py │ │ │ │ │ ├── live_update.py │ │ │ │ │ ├── login_oauth.py │ │ │ │ │ ├── mycard.vue │ │ │ │ │ ├── pokemon_search.py │ │ │ │ │ └── vue_component.py │ │ │ │ ├── ipycanvas.py │ │ │ │ ├── libraries │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── altair.py │ │ │ │ │ ├── bqplot.py │ │ │ │ │ ├── ipyleaflet.py │ │ │ │ │ └── ipyleaflet_advanced.py │ │ │ │ ├── utilities │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── calculator.py │ │ │ │ │ ├── countdown_timer.py │ │ │ │ │ └── todo.py │ │ │ │ └── visualization │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── annotator.py │ │ │ │ │ ├── linked_views.py │ │ │ │ │ └── plotly.py │ │ │ ├── faq │ │ │ │ ├── __init__.py │ │ │ │ └── content │ │ │ │ │ └── 99-faq.md │ │ │ └── getting_started │ │ │ │ ├── __init__.py │ │ │ │ └── content │ │ │ │ ├── 00-quickstart.md │ │ │ │ ├── 01-introduction.md │ │ │ │ ├── 02-installing.md │ │ │ │ ├── 04-tutorials │ │ │ │ ├── 00-overview.md │ │ │ │ ├── 10_data_science.py │ │ │ │ ├── 20-web-app.md │ │ │ │ ├── 30-ipywidgets.md │ │ │ │ ├── 40-streamlit.md │ │ │ │ ├── 50-dash.md │ │ │ │ ├── 60-jupyter-dashboard-part1.py │ │ │ │ ├── SF_crime_sample.csv.gz │ │ │ │ ├── _data_science.ipynb │ │ │ │ └── _jupyter_dashboard_1.ipynb │ │ │ │ ├── 05-fundamentals │ │ │ │ ├── 00-overview.md │ │ │ │ ├── 10-components.md │ │ │ │ └── 50-state-management.md │ │ │ │ ├── 07-deploying │ │ │ │ ├── 00-overview.md │ │ │ │ ├── 10-self-hosted.md │ │ │ │ └── 20-cloud-hosted.md │ │ │ │ ├── 80-what-is-lab.md │ │ │ │ └── 90-troubleshoot.md │ │ ├── docutils.py │ │ ├── home.vue │ │ ├── our_team │ │ │ └── __init__.py │ │ ├── pricing │ │ │ └── __init__.py │ │ ├── roadmap │ │ │ ├── __init__.py │ │ │ └── roadmap.md │ │ ├── scale_ipywidgets.py │ │ └── showcase │ │ │ ├── __init__.py │ │ │ ├── domino_code_assist.py │ │ │ ├── planeto_tessa.py │ │ │ ├── solara_dev.py │ │ │ ├── solarathon_2023_team_2.py │ │ │ ├── solarathon_2023_team_4.py │ │ │ ├── solarathon_2023_team_5.py │ │ │ ├── solarathon_2023_team_6.py │ │ │ └── wanderlust.py │ ├── public │ │ ├── beach.jpeg │ │ ├── logo.svg │ │ ├── social │ │ │ ├── discord.svg │ │ │ ├── github.svg │ │ │ └── twitter.svg │ │ └── success.html │ ├── templates │ │ └── index.html.j2 │ └── utils.py └── widgets │ ├── __init__.py │ ├── vue │ ├── gridlayout.vue │ ├── html.vue │ ├── navigator.vue │ └── vegalite.vue │ └── widgets.py ├── tests ├── __init__.py ├── conftest.py ├── docs │ ├── docs_howto_event_with_future_test.py │ ├── docs_howto_event_with_polling_test.py │ ├── docs_howto_no_browser_api_find_testing_test.py │ ├── docs_howto_no_browser_testing_test.py │ └── docs_howto_no_browser_threaded_test.py ├── integration │ ├── __init__.py │ ├── api_test.py │ ├── app_widget.py │ ├── apps │ │ ├── not-allowed │ │ ├── public │ │ │ └── test.txt │ │ └── secure │ │ │ └── app.py │ ├── assets │ │ ├── assets1 │ │ │ ├── common.js │ │ │ └── unique1.js │ │ └── assets2 │ │ │ ├── common.js │ │ │ ├── custom.css │ │ │ ├── custom.js │ │ │ └── unique2.js │ ├── assets_test.py │ ├── async_test.py │ ├── cdn_test.py │ ├── cmdline_test.py │ ├── conftest.py │ ├── create_test.py │ ├── enterprise │ │ ├── __init__.py │ │ └── oauth_test.py │ ├── error_test.py │ ├── esm_test.py │ ├── file_download_test.py │ ├── flask_test.py │ ├── input_date_test.py │ ├── latex_test.py │ ├── lifecycle_test.py │ ├── markdown_test.py │ ├── memleak_test.py │ ├── menu_test.py │ ├── popout_test.py │ ├── reconnect_test.py │ ├── reload │ │ └── apps.py │ ├── router_test.py │ ├── server_test.py │ ├── ssg_test.py │ ├── starlette_test.py │ ├── test.vue │ ├── testapp.py │ └── widget_test.py ├── pyinstaller │ ├── __init__.py │ └── pyinstaller_test.py ├── titanic.csv ├── ui │ └── snapshots │ │ └── tests │ │ └── integration │ │ ├── latex_test.py │ │ ├── test_widget_latex_solara-flask-chromium-linux-ipywidgets-7-changed-reference.png │ │ ├── test_widget_latex_solara-flask-chromium-linux-ipywidgets-7-reference.png │ │ ├── test_widget_latex_solara-flask-chromium-linux-ipywidgets-8-changed-reference.png │ │ ├── test_widget_latex_solara-flask-chromium-linux-ipywidgets-8-reference.png │ │ ├── test_widget_latex_solara-flask-chromium-win32-ipywidgets-7-changed-reference.png │ │ ├── test_widget_latex_solara-flask-chromium-win32-ipywidgets-7-reference.png │ │ ├── test_widget_latex_solara-flask-chromium-win32-ipywidgets-8-changed-reference.png │ │ └── test_widget_latex_solara-flask-chromium-win32-ipywidgets-8-reference.png │ │ └── widget_test.py │ │ ├── test_slider_all-flask-jupyter_lab-chromium-linux-ipywidgets-7-reference.png │ │ ├── test_slider_all-flask-jupyter_lab-chromium-linux-ipywidgets-8-reference.png │ │ ├── test_slider_all-flask-jupyter_lab-chromium-win32-ipywidgets-7-reference.png │ │ ├── test_slider_all-flask-jupyter_lab-chromium-win32-ipywidgets-8-reference.png │ │ ├── test_slider_all-flask-jupyter_notebook-chromium-linux-ipywidgets-7-reference.png │ │ ├── test_slider_all-flask-jupyter_notebook-chromium-linux-ipywidgets-8-reference.png │ │ ├── test_slider_all-flask-jupyter_notebook-chromium-win32-ipywidgets-7-reference.png │ │ ├── test_slider_all-flask-jupyter_notebook-chromium-win32-ipywidgets-8-reference.png │ │ ├── test_slider_all-flask-solara-chromium-linux-ipywidgets-7-reference.png │ │ ├── test_slider_all-flask-solara-chromium-linux-ipywidgets-8-reference.png │ │ ├── test_slider_all-flask-solara-chromium-win32-ipywidgets-7-reference.png │ │ ├── test_slider_all-flask-solara-chromium-win32-ipywidgets-8-reference.png │ │ ├── test_slider_all-flask-voila-chromium-linux-ipywidgets-7-reference.png │ │ ├── test_slider_all-flask-voila-chromium-linux-ipywidgets-8-reference.png │ │ ├── test_slider_all-flask-voila-chromium-win32-ipywidgets-7-reference.png │ │ ├── test_slider_all-flask-voila-chromium-win32-ipywidgets-8-reference.png │ │ ├── test_slider_all-starlette-solara-chromium-linux-ipywidgets-7-reference.png │ │ ├── test_slider_all-starlette-solara-chromium-linux-ipywidgets-8-reference.png │ │ ├── test_slider_all-starlette-solara-chromium-win32-ipywidgets-7-reference.png │ │ ├── test_slider_all-starlette-solara-chromium-win32-ipywidgets-8-reference.png │ │ ├── test_solara_button_all-flask-jupyter_lab-chromium-linux-reference.png │ │ ├── test_solara_button_all-flask-jupyter_lab-chromium-win32-reference.png │ │ ├── test_solara_button_all-flask-jupyter_notebook-chromium-linux-reference.png │ │ ├── test_solara_button_all-flask-jupyter_notebook-chromium-win32-reference.png │ │ ├── test_solara_button_all-flask-solara-chromium-linux-reference.png │ │ ├── test_solara_button_all-flask-solara-chromium-win32-reference.png │ │ ├── test_solara_button_all-flask-voila-chromium-linux-reference.png │ │ ├── test_solara_button_all-flask-voila-chromium-win32-reference.png │ │ ├── test_solara_button_all-starlette-solara-chromium-linux-reference.png │ │ ├── test_solara_button_all-starlette-solara-chromium-win32-reference.png │ │ ├── test_widget_button_solara-flask-chromium-linux-ipyvuetify3-reference.png │ │ ├── test_widget_button_solara-flask-chromium-linux-reference.png │ │ ├── test_widget_button_solara-flask-chromium-win32-reference.png │ │ ├── test_widget_button_solara-starlette-chromium-linux-ipyvuetify3-reference.png │ │ ├── test_widget_button_solara-starlette-chromium-linux-reference.png │ │ └── test_widget_button_solara-starlette-chromium-win32-reference.png └── unit │ ├── __init__.py │ ├── app_test.py │ ├── applayout_test.py │ ├── autorouting_test.py │ ├── cache_test.py │ ├── cdn_helper_test.py │ ├── chat_test.py │ ├── checkbox_test.py │ ├── common.py │ ├── component_frontend_test.py │ ├── component_vue_test.vue │ ├── confirmation_dialog_test.py │ ├── conftest.py │ ├── cross_filter_component_test.py │ ├── cross_filter_test.py │ ├── dataframe_test.py │ ├── datatable_test.py │ ├── display_test.py │ ├── express_test.py │ ├── file_browser_test.py │ ├── file_download_test.py │ ├── hook_use_invalid_test.py │ ├── hooks_test.py │ ├── input_date_test.py │ ├── input_test.py │ ├── input_time_test.py │ ├── kernel_test.py │ ├── lab │ └── __init__.py │ ├── lifecycle_test.py │ ├── matplotlib_test.py │ ├── no_solara_test.py │ ├── output_widget_test.py │ ├── patch_test.py │ ├── pivottable_test.py │ ├── reload_test.py │ ├── router_test.py │ ├── select_test.py │ ├── settings_test.py │ ├── shell_test.py │ ├── slider_test.py │ ├── solara_test_apps │ ├── __init__.py │ ├── food │ │ ├── __init__.py │ │ ├── food.py │ │ └── index.py │ ├── kernel_start.py │ ├── multipage-widgets │ │ ├── 00-overview.md │ │ ├── 01-views.py │ │ ├── 02-likes.py │ │ ├── 03-volume.py │ │ └── 04-color.ipynb │ ├── multipage │ │ ├── 01-home.py │ │ ├── 02-my_fruit.py │ │ ├── 03-some-markdown.md │ │ ├── 04-a_directory │ │ │ ├── 00-another-markdown.md │ │ │ ├── 01-not-an-app.py │ │ │ └── __init__.py │ │ ├── 05-and-notebooks.ipynb │ │ ├── 06-custom-routes.py │ │ ├── 10-single-file-directory │ │ │ └── single-file.md │ │ ├── 99-some_other_python_script.py │ │ └── some_other_file.txt │ ├── notebookapp_component.ipynb │ ├── notebookapp_element.ipynb │ ├── notebookapp_widget.ipynb │ ├── single_file.py │ ├── single_file_multiple_routes.py │ └── single_file_routes.py │ ├── switch_test.py │ ├── tabs_test.py │ ├── task_test.py │ ├── telemetry_tests.py │ ├── toestand_computed_reload.py │ ├── toestand_test.py │ └── toggle_button_test.py └── tsconfigbase.json /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.48.0 3 | commit = True 4 | tag = True 5 | parse = (?P\d+)(\.(?P\d+))(\.(?P\d+))((?P.)(?P\d+))? 6 | serialize = 7 | {major}.{minor}.{patch}{release}{build} 8 | {major}.{minor}.{patch} 9 | 10 | [bumpversion:part:release] 11 | optional_value = g 12 | first_value = g 13 | values = 14 | a 15 | b 16 | g 17 | 18 | [bumpversion:file:.github/pycafe-create-status.py] 19 | 20 | [bumpversion:file:solara/__init__.py] 21 | 22 | [bumpversion:file:packages/solara-assets/solara_assets/__init__.py] 23 | 24 | [bumpversion:file:packages/solara-enterprise/solara_enterprise/__init__.py] 25 | 26 | [bumpversion:file:packages/solara-enterprise/pyproject.toml] 27 | 28 | [bumpversion:file:packages/solara-server/pyproject.toml] 29 | 30 | [bumpversion:file:packages/solara-meta/pyproject.toml] 31 | 32 | [bumpversion:file:packages/pytest-ipywidgets/pyproject.toml] 33 | 34 | [bumpversion:file:solara/server/static/solara_bootstrap.py] 35 | 36 | [bumpversion:file:release.md] 37 | -------------------------------------------------------------------------------- /.ci-package-locks/code-quality/python3.12.txt: -------------------------------------------------------------------------------- 1 | cfgv==3.4.0 2 | distlib==0.3.9 3 | filelock==3.18.0 4 | identify==2.6.12 5 | nodeenv==1.9.1 6 | platformdirs==4.3.8 7 | pre_commit==4.2.0 8 | PyYAML==6.0.2 9 | virtualenv==20.31.2 10 | -------------------------------------------------------------------------------- /.ci-package-locks/code-quality/python3.9.txt: -------------------------------------------------------------------------------- 1 | cfgv==3.4.0 2 | distlib==0.3.9 3 | filelock==3.18.0 4 | identify==2.6.12 5 | nodeenv==1.9.1 6 | platformdirs==4.3.8 7 | pre_commit==4.2.0 8 | PyYAML==6.0.2 9 | virtualenv==20.31.2 10 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E203 W503 3 | max-line-length = 160 4 | per-file-ignores = 5 | # line too long (170 > 160 characters) 6 | ipywidgets.py: E501 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Report a Bug \U0001F41B" 3 | about: "Is Solara not working the way you expect it to?" 4 | --- 5 | 6 | 7 | 8 | ## Expected Behavior 9 | 10 | 11 | ## Current Behavior 12 | 13 | 14 | ## Steps to Reproduce the Problem 15 | 16 | 17 | 18 | 1. 19 | 2. 20 | 3. 21 | 22 | ## Specifications 23 | 24 | - Solara Version: 25 | - Platform: 26 | - Affected Python Versions: 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Feature Request \U0001F680" 3 | about: Suggest an improvement! 4 | title: 'Feature Request:' 5 | labels: 'enhancement' 6 | assignees: '' 7 | --- 8 | 9 | ## Feature Request 10 | 11 | - [ ] The requested feature would break current behaviour 12 | - [ ] I would be interested in opening a PR for this feature 13 | 14 | ### What problem would this feature solve? Please describe. 15 | A clear and concise description of what the problem is. Ex. I have an issue when [...] 16 | 17 | ### Describe the solution you'd like 18 | A clear and concise description of the feature, and how it would solve the problem. Add any considered drawbacks. 19 | 20 | ### Documentation, Migration Strategy 21 | If you can, explain how users will be able to use this and possibly write a draft version for related documentation. 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### All Submissions: 2 | 3 | 4 | 5 | * [ ] I installed `pre-commit` prior to committing my changes (see [development setup docs](https://solara.dev/documentation/advanced/development/setup#contributing)). 6 | * [ ] My commit messages conform to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). 7 | * [ ] My PR title conforms to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). 8 | * [ ] I linked to any relevant issues. 9 | 10 | ### Changes to / New Features: 11 | 12 | * [ ] I included docs for (the changes to) my feature. 13 | * [ ] I wrote tests for (the changes to) my feature. 14 | 15 | ### Description of changes 16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/workflows/pycafe.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: PyCafe Playground Link 3 | on: 4 | workflow_run: 5 | workflows: [Test] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | create-status: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | - name: Create PyCafe status link 16 | run: | 17 | pip install PyGithub 18 | python .github/pycafe-create-status.py ${{ github.event.workflow_run.head_sha }} ${{ github.event.workflow_run.id }} 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please go to https://solara.dev/documentation/advanced/development/contribute for more information. 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Maarten A. Breddels 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | # heroku by default sets WEB_CONCURRENCY=2 2 | # see: https://devcenter.heroku.com/changelog-items/618 3 | # which uvicorn picks up, unless we explicitly set --workers --1 4 | # see https://www.uvicorn.org/deployment/ 5 | # we also need to bind to 0.0.0.0 otherwise heroku cannot route to our server 6 | # for playwright: run $ heroku buildpacks:add https://github.com/mxschmitt/heroku-playwright-buildpack --app solara-dem 7 | # also add PLAYWRIGHT_BUILDPACK_BROWSERS chromium to your env vars in the Settings in heroku 8 | web: (playwright install && solara run solara.website.pages --port=$PORT --no-open --host=0.0.0.0 --workers 1 --ssg --production) 9 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please contact security@widgetti to report vulnerabilities 6 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | check_untyped_defs = True 3 | ignore_missing_imports = True 4 | exclude=04-a_directory|nogit|^pyinstaller 5 | no_implicit_optional = False 6 | -------------------------------------------------------------------------------- /packages/pytest-ipywidgets/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Maarten A. Breddels 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/solara-assets/.gitignore: -------------------------------------------------------------------------------- 1 | # unignore build and dist, so hatch includes these dirs in shared-data 2 | !build 3 | !dist/ 4 | -------------------------------------------------------------------------------- /packages/solara-assets/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Maarten A. Breddels 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/solara-assets/RELEASE.md: -------------------------------------------------------------------------------- 1 | # Stable 2 | 3 | Version of solara-assets is locked to solara, and released in CI. 4 | 5 | # Test a release 6 | 7 | e.g. to manually make a release with a new solara-vuetify-app 8 | 9 | 10 | ``` 11 | $ cd packages/solara-vuetify-app 12 | $ bump2version --verbose major 13 | $ cd ../assets 14 | $ batch build 15 | # will fail downloading from cdn, so we fill the directory manually 16 | $ tar zxfv ../solara-vuetify-app/widgetti-solara-vuetify-app-2.0.0.tgz --strip-components=1 --directory cdn/@widgetti/solara-vuetify-app@2.0.0/ 17 | # build again 18 | $ batch build 19 | ``` 20 | -------------------------------------------------------------------------------- /packages/solara-assets/download_cdn_test.py: -------------------------------------------------------------------------------- 1 | from packages.assets.hatch_build import npm_pack 2 | 3 | 4 | def test_npm_pack(tmp_path_factory): 5 | base_cache_dir = tmp_path_factory.mktemp("cdn") 6 | 7 | package = "@widgetti/solara-vuetify-app" 8 | version = "0.0.1-alpha.1" 9 | npm_pack(base_cache_dir, package, version) 10 | 11 | some_file = base_cache_dir / f"{package}@{version}" / "dist" / "037d830416495def72b7881024c14b7b.woff2" 12 | assert some_file.is_file() 13 | assert len(some_file.read_bytes()) == 15436 14 | -------------------------------------------------------------------------------- /packages/solara-assets/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling==1.26.3"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "solara-assets" 7 | authors = [{name = "Maarten A. Breddels", email = "maartenbreddels@gmail.com"}] 8 | license = {file = "LICENSE"} 9 | requires-python = ">=3.7" 10 | 11 | classifiers = ["License :: OSI Approved :: MIT License"] 12 | dynamic = ["version", "description"] 13 | dependencies = [ 14 | "solara", 15 | ] 16 | 17 | [project.urls] 18 | Home = "https://www.github.com/widgetti/solara" 19 | 20 | [tool.hatch.version] 21 | path = "solara_assets/__init__.py" 22 | 23 | # Used to call hatch_build.py 24 | [tool.hatch.build.hooks.custom] 25 | 26 | [tool.hatch.build.targets.wheel.shared-data] 27 | "cdn" = "share/solara/cdn" 28 | 29 | [tool.black] 30 | line-length = 160 31 | 32 | [tool.isort] 33 | profile = "black" 34 | -------------------------------------------------------------------------------- /packages/solara-assets/solara_assets/__init__.py: -------------------------------------------------------------------------------- 1 | "CDN assets for Solara" 2 | 3 | __version__ = "1.48.0" 4 | -------------------------------------------------------------------------------- /packages/solara-enterprise/LICENSE: -------------------------------------------------------------------------------- 1 | Not open source, contact contact@solara.dev for licencing. 2 | -------------------------------------------------------------------------------- /packages/solara-enterprise/RELEASE.md: -------------------------------------------------------------------------------- 1 | # Stable 2 | 3 | Version of solara-enterprise is locked to solara, and released in CI. 4 | 5 | # Test a release 6 | 7 | e.g. to manually make a release with a new solara-enterprise 8 | 9 | 10 | ``` 11 | $ cd packages/solara-enterprise 12 | $ bump2version --verbose major 13 | $ hatch build 14 | ``` 15 | -------------------------------------------------------------------------------- /packages/solara-enterprise/solara_enterprise/__init__.py: -------------------------------------------------------------------------------- 1 | "Enterprise features for Solara" 2 | 3 | __version__ = "1.48.0" 4 | -------------------------------------------------------------------------------- /packages/solara-enterprise/solara_enterprise/auth/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional, cast 2 | 3 | from solara.lab import Reactive 4 | 5 | from .components import Avatar, AvatarMenu 6 | from .utils import get_login_url, get_logout_url 7 | 8 | __all__ = ["user", "get_login_url", "get_logout_url", "Avatar", "AvatarMenu"] 9 | 10 | # the current way of generating a key is based in the default value 11 | # which may collide after a hot reload, since solara itself is not reloaded 12 | # if we give a fixed key, we can avoid this 13 | user = Reactive(cast(Optional[Dict], None), key="solara-enterprise.auth.user") 14 | -------------------------------------------------------------------------------- /packages/solara-enterprise/solara_enterprise/cache/__init__.py: -------------------------------------------------------------------------------- 1 | from .. import license 2 | 3 | license.check("cache") 4 | -------------------------------------------------------------------------------- /packages/solara-enterprise/solara_enterprise/cache/memory_size.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pickle 3 | from typing import Any, Callable, MutableMapping 4 | 5 | import solara.settings 6 | import solara.util 7 | from cachetools import LRUCache 8 | 9 | from solara_enterprise.cache.base import Base, make_key 10 | 11 | logger = logging.getLogger("solara-enterprise.cache.memory") 12 | 13 | 14 | def sizeof(obj): 15 | size = len(pickle.dumps(obj)) 16 | logger.debug("size of %s: %s", obj, size) 17 | return size 18 | 19 | 20 | class MemorySize(Base): 21 | def __init__( 22 | self, 23 | max_size=solara.settings.cache.memory_max_size, 24 | make_key: Callable[[Any], bytes] = make_key, 25 | sizeof: Callable[[Any], int] = sizeof, 26 | ): 27 | maxsize = solara.util.parse_size(max_size) 28 | _wrapper_dict: MutableMapping[bytes, bytes] = LRUCache(maxsize=maxsize, getsizeof=sizeof) 29 | super().__init__(_wrapper_dict, make_key=make_key) 30 | -------------------------------------------------------------------------------- /packages/solara-enterprise/solara_enterprise/cache/multi_level.py: -------------------------------------------------------------------------------- 1 | from typing import ChainMap 2 | 3 | 4 | class MultiLevel(ChainMap): 5 | """Use multiple caches, where we assume the first is the fastest""" 6 | 7 | def __getitem__(self, key): 8 | for level, mapping in enumerate(self.maps): 9 | try: 10 | value = mapping[key] 11 | # write back to lower levels 12 | for i in range(level): 13 | self.maps[i][key] = value 14 | return value 15 | except KeyError: 16 | pass 17 | return self.__missing__(key) 18 | 19 | def __setitem__(self, key, value): 20 | for cache in self.maps: 21 | cache[key] = value 22 | 23 | def __delitem__(self, key): 24 | for cache in self.maps: 25 | del cache[key] 26 | -------------------------------------------------------------------------------- /packages/solara-enterprise/solara_enterprise/cache/redis.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Callable, Optional 2 | 3 | import redis 4 | import solara.settings 5 | 6 | from solara_enterprise.cache.base import Base, make_key 7 | 8 | 9 | class Redis(Base): 10 | """Wraps a client such that the values are pickled/unpickled""" 11 | 12 | def __init__( 13 | self, client: Optional[redis.Redis] = None, clear=solara.settings.cache.clear, prefix=b"solara:cache:", make_key: Callable[[Any], bytes] = make_key 14 | ): 15 | self.client = client or redis.Redis() 16 | super().__init__(self.client, prefix=prefix, clear=clear, make_key=make_key) 17 | 18 | def clear(self): 19 | with self.client.lock(b"lock:" + self.prefix): 20 | keys = self.keys() 21 | for key in keys: 22 | del self[key] 23 | 24 | def keys(self): 25 | return self.client.keys(self.prefix + b"*") 26 | -------------------------------------------------------------------------------- /packages/solara-enterprise/solara_enterprise/license.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Dict 3 | 4 | from rich import print 5 | 6 | warned: Dict[str, bool] = {} 7 | 8 | 9 | def check(name): 10 | if name in warned: 11 | return 12 | warned[name] = True 13 | print( # noqa: T201 14 | f"[bold yellow]Using the enterprise {name} feature requires a license, unless used for non-commerical use. " 15 | "Please contact us at contact@solara.dev to get a license.", 16 | file=sys.stderr, 17 | ) 18 | -------------------------------------------------------------------------------- /packages/solara-enterprise/solara_enterprise/search/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/packages/solara-enterprise/solara_enterprise/search/__init__.py -------------------------------------------------------------------------------- /packages/solara-enterprise/solara_enterprise/search/search.py: -------------------------------------------------------------------------------- 1 | import ipyvue 2 | import solara 3 | import traitlets 4 | 5 | 6 | class SearchWidget(ipyvue.VueTemplate): 7 | template_file = (__file__, "search.vue") 8 | forceUpdateList = traitlets.Int(0).tag(sync=True) 9 | item = traitlets.Any().tag(sync=True) 10 | query = traitlets.Unicode("", allow_none=True).tag(sync=True) 11 | search_open = traitlets.Bool(False).tag(sync=True) 12 | failed = traitlets.Bool(False).tag(sync=True) 13 | cdn = traitlets.Unicode(None, allow_none=True).tag(sync=True) 14 | 15 | @traitlets.default("cdn") 16 | def _cdn(self): 17 | import solara.settings 18 | 19 | if not solara.settings.assets.proxy: 20 | return solara.settings.assets.cdn 21 | 22 | 23 | @solara.component 24 | def Search(): 25 | return SearchWidget.element() 26 | -------------------------------------------------------------------------------- /packages/solara-meta/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Maarten A. Breddels 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/solara-meta/README.md: -------------------------------------------------------------------------------- 1 | A meta packages that installs all the necessary dependencies to get started with Solara. 2 | 3 | See https://solara.dev/documentation/getting_started/installing for more information. 4 | -------------------------------------------------------------------------------- /packages/solara-milkdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@widgetti/solara-milkdown", 3 | "version": "6.3.0", 4 | "description": "Milkdown bundle for use with Solara", 5 | "main": "dist/index.min.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "webpack" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "css-loader": "^2.1.1", 15 | "file-loader": "^3.0.1", 16 | "html-webpack-plugin": "^5.5.0", 17 | "style-loader": "^0.23.1", 18 | "webpack": "^5.73.0", 19 | "webpack-cli": "^4.10.0", 20 | "webpack-dev-server": "^4.9.3" 21 | }, 22 | "dependencies": { 23 | "@babel/core": "^7.18.5", 24 | "@milkdown/core": "^6.3.0", 25 | "@milkdown/plugin-menu": "^6.3.0", 26 | "@milkdown/preset-commonmark": "^6.3.0", 27 | "@milkdown/prose": "^6.3.0", 28 | "@milkdown/theme-nord": "^6.3.0", 29 | "@milkdown/plugin-listener": "^6.3.0", 30 | "@milkdown/preset-gfm": "^6.3.0", 31 | "@milkdown/plugin-history": "^6.3.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/solara-milkdown/src/index.js: -------------------------------------------------------------------------------- 1 | export { defaultValueCtx, Editor, hex2rgb, rootCtx, ThemeColor, themeFactory, ThemeGlobal, ThemeIcon, ThemeSize } from '@milkdown/core'; 2 | export { history } from '@milkdown/plugin-history'; 3 | export { listener, listenerCtx } from '@milkdown/plugin-listener'; 4 | export { menu, menuPlugin } from '@milkdown/plugin-menu'; 5 | export { commonmark } from '@milkdown/preset-commonmark'; 6 | export { gfm } from '@milkdown/preset-gfm'; 7 | export { nord } from '@milkdown/theme-nord'; 8 | -------------------------------------------------------------------------------- /packages/solara-milkdown/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var path = require('path'); 3 | 4 | var rules = [ 5 | { test: /\.css$/, use: ['style-loader', 'css-loader'] }, 6 | { 7 | test: /\.(woff|woff2|eot|ttf|otf)$/, 8 | loader: 'file-loader', 9 | } 10 | ] 11 | 12 | 13 | module.exports = [ 14 | { 15 | entry: './src/index.js', 16 | output: { 17 | filename: 'index.js', 18 | path: path.resolve(__dirname, 'dist'), 19 | libraryTarget: 'umd', 20 | devtoolModuleFilenameTemplate: `webpack://@widgetti/solara-milkdown` 21 | }, 22 | module: { 23 | rules: rules 24 | }, 25 | devtool: 'source-map', 26 | mode: 'development', 27 | }, 28 | { 29 | entry: './src/index.js', 30 | output: { 31 | filename: 'index.min.js', 32 | path: path.resolve(__dirname, 'dist'), 33 | libraryTarget: 'umd', 34 | devtoolModuleFilenameTemplate: `webpack://@widgetti/solara-milkdown` 35 | }, 36 | module: { 37 | rules: rules 38 | }, 39 | mode: 'production', 40 | } 41 | ]; 42 | -------------------------------------------------------------------------------- /packages/solara-server/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Maarten A. Breddels 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/solara-server/README.md: -------------------------------------------------------------------------------- 1 | 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. 2 | 3 | See https://solara.dev/documentation/advanced/understanding/solara-server for more details. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | pip install solara-server[starlette,dev] 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```bash 14 | $ solara run myapp.py 15 | ``` 16 | -------------------------------------------------------------------------------- /packages/solara-vuetify-app/.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 10.0.3 3 | commit = True 4 | tag = True 5 | tag_name = @widgetti/solara-vuetify-app@{new_version} 6 | tag_message = Bump version solara-vuetify-app: {current_version} → {new_version} 7 | message = Bump version solara-vuetify-app: {current_version} → {new_version} 8 | 9 | [bumpversion:file:package.json] 10 | 11 | [bumpversion:file:../../solara/server/templates/solara.html.j2] 12 | 13 | [bumpversion:file:../../release.sh] 14 | 15 | [bumpversion:file:../solara-assets/hatch_build.py] 16 | -------------------------------------------------------------------------------- /packages/solara-vuetify-app/README.md: -------------------------------------------------------------------------------- 1 | A bundle that is used for solara-server that includes all static assets we need. 2 | 3 | For a dev install, use something like so that the Solara CDN proxy picks up the bundle locally: 4 | 5 | $ npm install 6 | $ npm run build 7 | $ npm run devlink 8 | -------------------------------------------------------------------------------- /packages/solara-vuetify-app/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -o pipefail 3 | # usage: ./release minor -n 4 | version=$(bump2version --dry-run --list $* | grep new_version= | sed -r s,"^.*=",,) 5 | echo Version tag @widgetti/solara-vuetify-app@$version 6 | bumpversion $* --verbose && git push upstream master @widgetti/solara-vuetify-app@$version 7 | -------------------------------------------------------------------------------- /packages/solara-vuetify-app/src/empty.js: -------------------------------------------------------------------------------- 1 | // used as alias to avoid bundling unused code 2 | -------------------------------------------------------------------------------- /packages/solara-vuetify-app/src/fonts.js: -------------------------------------------------------------------------------- 1 | import 'typeface-roboto'; 2 | import 'material-design-icons-iconfont/dist/material-design-icons.css'; 3 | import '@mdi/font/css/materialdesignicons.css'; 4 | -------------------------------------------------------------------------------- /packages/solara-vuetify-app/src/solara-vuetify-app.js: -------------------------------------------------------------------------------- 1 | 2 | import Vue from 'vue'; 3 | import Vuetify from 'vuetify'; 4 | import 'vuetify/dist/vuetify.min.css'; 5 | import { addCompiler } from '@mariobuikhuizen/vue-compiler-addon'; 6 | 7 | addCompiler(Vue); 8 | 9 | Vue.use(Vuetify); 10 | 11 | import * as solara from './solara'; 12 | export { solara }; 13 | 14 | export { Vue }; 15 | export { Vuetify }; 16 | -------------------------------------------------------------------------------- /packages/solara-vuetify-app/src/solara.js: -------------------------------------------------------------------------------- 1 | export { RenderMimeRegistry, WidgetManager, connectKernel, extendedRendererFactories, KatexTypesetter, renderKatex, shutdownKernel } from '@widgetti/solara-widget-manager'; 2 | -------------------------------------------------------------------------------- /packages/solara-vuetify3-app/.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 5.0.2 3 | commit = True 4 | tag = True 5 | tag_name = @widgetti/solara-vuetify3-app@{new_version} 6 | tag_message = Bump version solara-vuetify3-app: {current_version} → {new_version} 7 | message = Bump version solara-vuetify3-app: {current_version} → {new_version} 8 | 9 | [bumpversion:file:package.json] 10 | 11 | [bumpversion:file:../../solara/server/templates/solara.html.j2] 12 | 13 | [bumpversion:file:../../release.sh] 14 | 15 | [bumpversion:file:../solara-assets/hatch_build.py] 16 | -------------------------------------------------------------------------------- /packages/solara-vuetify3-app/README.md: -------------------------------------------------------------------------------- 1 | A bundle that is used for solara-server that includes all static assets we need (for vue/vuetify 3). 2 | 3 | For a dev install, use something like so that the Solara CDN proxy picks up the bundle locally: 4 | 5 | $ npm install 6 | $ npm run build 7 | $ npm run devlink 8 | -------------------------------------------------------------------------------- /packages/solara-vuetify3-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@widgetti/solara-vuetify3-app", 3 | "version": "5.0.2", 4 | "description": "Solara Vuetify App", 5 | "main": "dist/solara-vuetify-app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "webpack", 9 | "watch": "webpack --watch --mode=development", 10 | "devlink": "TARGET_DIR=`python -c \"import sys; print(sys.prefix)\"`/share/solara/cdn/@widgetti/solara-vuetify3-app@5.0.2/; mkdir -p $TARGET_DIR && rm -rf $TARGET_DIR/dist && ln -sf $PWD/dist $TARGET_DIR/dist" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "dependencies": { 15 | "@mdi/font": "^4.9.95", 16 | "@widgetti/solara-widget-manager": "file:../solara-widget-manager", 17 | "@widgetti/solara-widget-manager8": "file:../solara-widget-manager8", 18 | "material-design-icons-iconfont": "^5.0.1", 19 | "typeface-roboto": "0.0.54", 20 | "vue": "^3.3.4", 21 | "vuetify": "~3.3.17" 22 | }, 23 | "devDependencies": { 24 | "css-loader": "^6.9.1", 25 | "file-loader": "^6.2.0", 26 | "mini-css-extract-plugin": "^2.7.7", 27 | "style-loader": "^3.3.4", 28 | "webpack": "^5.90.0", 29 | "webpack-cli": "^5.1.4" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/solara-vuetify3-app/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -o pipefail 3 | # usage: ./release minor -n 4 | version=$(bump2version --dry-run --list $* | grep new_version= | sed -r s,"^.*=",,) 5 | echo Version tag @widgetti/solara-vuetify3-app@$version 6 | bumpversion $* --verbose && git push upstream master @widgetti/solara-vuetify3-app@$version 7 | -------------------------------------------------------------------------------- /packages/solara-vuetify3-app/src/fonts.js: -------------------------------------------------------------------------------- 1 | import '@mdi/font/css/materialdesignicons.css'; 2 | import 'material-design-icons-iconfont/dist/material-design-icons.css'; 3 | import 'typeface-roboto'; 4 | -------------------------------------------------------------------------------- /packages/solara-vuetify3-app/src/solara-vuetify-app.js: -------------------------------------------------------------------------------- 1 | import * as Vue from 'vue'; 2 | import * as Vuetify from 'vuetify'; 3 | import 'vuetify/dist/vuetify.min.css'; 4 | 5 | import * as components from 'vuetify/components'; 6 | import * as directives from 'vuetify/directives'; 7 | 8 | const vuetifyPlugin = Vuetify.createVuetify({ 9 | components, 10 | directives, 11 | }); 12 | 13 | 14 | import * as solara from './solara'; 15 | export { solara }; 16 | 17 | export { Vue, Vuetify, vuetifyPlugin }; 18 | -------------------------------------------------------------------------------- /packages/solara-vuetify3-app/src/solara.js: -------------------------------------------------------------------------------- 1 | export { RenderMimeRegistry, WidgetManager, connectKernel, extendedRendererFactories, KatexTypesetter, renderKatex, shutdownKernel } from '@widgetti/solara-widget-manager'; 2 | -------------------------------------------------------------------------------- /packages/solara-widget-manager/src/index.ts: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (c) 2018, Voilà contributors * 3 | * Copyright (c) 2018, QuantStack * 4 | * * 5 | * Distributed under the terms of the BSD 3-Clause License. * 6 | * * 7 | * The full license is in the file LICENSE, distributed with this software. * 8 | ****************************************************************************/ 9 | 10 | import '../style/index.css'; 11 | 12 | export { 13 | RenderMimeRegistry, 14 | standardRendererFactories 15 | } from '@jupyterlab/rendermime'; 16 | export { connectKernel, shutdownKernel } from './kernel'; 17 | export { WidgetManager } from './manager'; 18 | export { KatexTypesetter, renderKatex } from './katex'; 19 | export { extendedRendererFactories } from './rendermime'; 20 | -------------------------------------------------------------------------------- /packages/solara-widget-manager/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/naming-convention 2 | declare interface Window { 3 | define: any; 4 | requirejs: any; 5 | } 6 | -------------------------------------------------------------------------------- /packages/solara-widget-manager/style/index.css: -------------------------------------------------------------------------------- 1 | @import "../node_modules/katex/dist/katex.min.css"; 2 | -------------------------------------------------------------------------------- /packages/solara-widget-manager/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2015", 5 | "declaration": true, 6 | "outDir": "./lib", 7 | "rootDir": "src", 8 | "sourceMap": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true 11 | }, 12 | "include": [ 13 | "src/**/*" 14 | ], 15 | } 16 | -------------------------------------------------------------------------------- /packages/solara-widget-manager8/patches/@jupyterlab+outputarea+3.6.4.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@jupyterlab/outputarea/lib/widget.js b/node_modules/@jupyterlab/outputarea/lib/widget.js 2 | index b3fb52a..8497376 100644 3 | --- a/node_modules/@jupyterlab/outputarea/lib/widget.js 4 | +++ b/node_modules/@jupyterlab/outputarea/lib/widget.js 5 | @@ -1,6 +1,6 @@ 6 | // Copyright (c) Jupyter Development Team. 7 | // Distributed under the terms of the Modified BSD License. 8 | -import { WidgetTracker } from '@jupyterlab/apputils'; 9 | +import { WidgetTracker } from '@jupyterlab/apputils/lib/widgettracker'; 10 | import { KernelMessage } from '@jupyterlab/services'; 11 | import { PromiseDelegate, UUID } from '@lumino/coreutils'; 12 | import { AttachedProperty } from '@lumino/properties'; 13 | -------------------------------------------------------------------------------- /packages/solara-widget-manager8/patches/@jupyterlab+rendermime+3.6.4.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@jupyterlab/rendermime/lib/registry.js b/node_modules/@jupyterlab/rendermime/lib/registry.js 2 | index 2ee4d74..acf0615 100644 3 | --- a/node_modules/@jupyterlab/rendermime/lib/registry.js 4 | +++ b/node_modules/@jupyterlab/rendermime/lib/registry.js 5 | @@ -2,7 +2,7 @@ 6 | | Copyright (c) Jupyter Development Team. 7 | | Distributed under the terms of the Modified BSD License. 8 | |----------------------------------------------------------------------------*/ 9 | -import { defaultSanitizer } from '@jupyterlab/apputils'; 10 | +import { defaultSanitizer } from '@jupyterlab/apputils/lib/sanitizer'; 11 | import { PathExt, URLExt } from '@jupyterlab/coreutils'; 12 | import { nullTranslator } from '@jupyterlab/translation'; 13 | import { MimeModel } from './mimemodel'; 14 | -------------------------------------------------------------------------------- /packages/solara-widget-manager8/src: -------------------------------------------------------------------------------- 1 | ../solara-widget-manager/src -------------------------------------------------------------------------------- /packages/solara-widget-manager8/style: -------------------------------------------------------------------------------- 1 | ../solara-widget-manager/style -------------------------------------------------------------------------------- /packages/solara-widget-manager8/tsconfig.json: -------------------------------------------------------------------------------- 1 | ../solara-widget-manager/tsconfig.json -------------------------------------------------------------------------------- /prefix/etc/jupyter/jupyter_notebook_config.d/solara.json: -------------------------------------------------------------------------------- 1 | { 2 | "NotebookApp": { 3 | "nbserver_extensions": { 4 | "solara.server.jupyter.server_extension": true 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /prefix/etc/jupyter/jupyter_server_config.d/solara.json: -------------------------------------------------------------------------------- 1 | { 2 | "ServerApp": { 3 | "jpserver_extensions": { 4 | "solara.server.jupyter.server_extension": true 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /pyinstaller/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.apple.security.cs.allow-jit 7 | 8 | com.apple.security.cs.allow-unsigned-executable-memory 9 | 10 | com.apple.security.cs.disable-library-validation 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pyinstaller/minimal/sample_app.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | clicks = solara.reactive(0) 4 | 5 | 6 | @solara.component 7 | def Page(): 8 | color = "green" 9 | if clicks.value >= 5: 10 | color = "red" 11 | 12 | def increment(): 13 | clicks.value += 1 14 | print("clicks", clicks) # noqa 15 | 16 | solara.Button(label=f"Clicked: {clicks}", on_click=increment, color=color) 17 | -------------------------------------------------------------------------------- /pyinstaller/minimal/solara-app-entrypoint.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | # make sure pyinstaller picks it up 4 | import sample_app # noqa: F401 5 | import os 6 | 7 | 8 | @click.command() 9 | @click.option( 10 | "--port", 11 | default=int(os.environ.get("PORT", 0)), 12 | help="Port to run the server on, 0 for a random free port", 13 | ) 14 | @click.option( 15 | "--webview", 16 | default=False, 17 | is_flag=True, 18 | help="Run the app in a webview window", 19 | ) 20 | def run(port: int, webview: bool = False): 21 | if "SOLARA_APP" not in os.environ: 22 | os.environ["SOLARA_APP"] = "sample_app" 23 | 24 | import solara.server.starlette 25 | 26 | server = solara.server.starlette.ServerStarlette(host="localhost", port=port) 27 | print(f"Starting server on {server.base_url}") 28 | server.serve_threaded() 29 | server.wait_until_serving() 30 | server.join() 31 | 32 | 33 | if __name__ == "__main__": 34 | run() 35 | -------------------------------------------------------------------------------- /pyinstaller/solara.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/pyinstaller/solara.icns -------------------------------------------------------------------------------- /release.md: -------------------------------------------------------------------------------- 1 | 2 | # Fully automated 3 | 4 | $ ./release.sh patch 5 | 6 | 7 | ## Making an alpha release 8 | 9 | 10 | $ ./release.sh patch --new-version 1.48.0a1 11 | 12 | 13 | # semi automated 14 | To make a new release 15 | ``` 16 | # update solara/__init__.py 17 | $ git add -u && git commit -m 'Release v1.48.0' && git tag v1.48.0 && git push upstream master v1.48.0 18 | ``` 19 | 20 | 21 | If a problem happens, and you want to keep the history clean 22 | ``` 23 | # do fix 24 | $ git rebase -i HEAD~3 25 | $ git tag v1.48.0 -f && git push upstream master v1.48.0 -f 26 | ``` 27 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -e .[all] 2 | -e ./packages/solara-server[all] 3 | -e ./packages/solara-enterprise[all] 4 | -e ./packages/pytest-ipywidgets[all] 5 | -e ./packages/solara-meta[all] 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | .[all] 2 | ./packages/solara-server[all] 3 | ./packages/solara-enterprise[all] 4 | ./packages/solara-meta[all] 5 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.9.13 2 | -------------------------------------------------------------------------------- /solara/alias.py: -------------------------------------------------------------------------------- 1 | import reacton # noqa: F401 2 | from reacton import ipyvue as rvue # noqa: F401 3 | from reacton import ipyvuetify as rv # noqa: F401 4 | from reacton import ipywidgets as rw # noqa: F401 5 | 6 | import solara as sol # noqa: F401 7 | -------------------------------------------------------------------------------- /solara/comm.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | from typing import Any, Dict 3 | 4 | try: 5 | import comm 6 | except ImportError: 7 | comm = None # type: ignore 8 | 9 | orphan_comm_stacks: Dict[Any, str] = {} 10 | 11 | 12 | if comm is not None and comm.create_comm is comm._create_comm: 13 | # only when nobody else has monkey-patched comm.create_comm 14 | class DummyComm(comm.base_comm.BaseComm): # type: ignore 15 | def publish_msg(self, msg_type, data=None, metadata=None, buffers=None, **keys): 16 | pass 17 | 18 | def create_dummy_comm(*args, **kwargs): 19 | comm = DummyComm(*args, **kwargs) 20 | stacktrace = "".join(traceback.format_stack()) 21 | orphan_comm_stacks[comm] = stacktrace 22 | return comm 23 | 24 | comm.create_comm = create_dummy_comm 25 | else: 26 | 27 | class DummyComm: # type: ignore 28 | pass 29 | -------------------------------------------------------------------------------- /solara/components/code_highlight_css.py: -------------------------------------------------------------------------------- 1 | import ipyvuetify as vy 2 | import solara 3 | 4 | 5 | class CodeHighlightCssWidget(vy.VuetifyTemplate): 6 | template_file = (__file__, "code_highlight_css.vue") 7 | 8 | 9 | @solara.component 10 | def CodeHighlightCss(): 11 | return CodeHighlightCssWidget.element() 12 | -------------------------------------------------------------------------------- /solara/components/download.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 30 | 31 | 36 | -------------------------------------------------------------------------------- /solara/components/head.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import reacton 4 | import solara 5 | 6 | 7 | @reacton.component 8 | def Head(children: List[reacton.core.Element] = []): 9 | """A component that manager the "head" tag of the page to avoid duplicate tags, such as titles. 10 | 11 | Currently only supports the [title](/documentation/components/page/title) tag as child, e.g.: 12 | 13 | ```python 14 | import solara 15 | 16 | @solara.component 17 | def Page(): 18 | with solara.VBox() as main: 19 | MyAwesomeComponent() 20 | with solara.Head(): 21 | solara.Title("My page title") 22 | return main 23 | 24 | ``` 25 | 26 | """ 27 | return solara.Div(children=children, style="display; none") 28 | -------------------------------------------------------------------------------- /solara/components/markdown_editor.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | import ipyvuetify 4 | import traitlets 5 | 6 | import solara 7 | 8 | 9 | class MarkdownEditorWidget(ipyvuetify.VuetifyTemplate): 10 | template_file = (__file__, "markdown_editor.vue") 11 | 12 | value = traitlets.Unicode("").tag(sync=True) 13 | height = traitlets.Unicode("180px").tag(sync=True) 14 | cdn = traitlets.Unicode(None, allow_none=True).tag(sync=True) 15 | 16 | @traitlets.default("cdn") 17 | def _cdn(self): 18 | import solara.settings 19 | 20 | if not solara.settings.assets.proxy: 21 | return solara.settings.assets.cdn 22 | 23 | 24 | @solara.component 25 | def MarkdownEditor(value: str = "", on_value: Callable[[str], None] = None): 26 | """WYSIWYG (visual) Markdown editor. 27 | 28 | ## Arguments 29 | 30 | * value: Markdown text 31 | * on_value: Callback function that is called when the text is changed 32 | """ 33 | return MarkdownEditorWidget.element(value=value, on_value=on_value) 34 | -------------------------------------------------------------------------------- /solara/components/select.vue: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /solara/components/tab_navigation.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.alias import rv, rvue 3 | 4 | 5 | @solara.component 6 | def LinkTab(path, label): 7 | # both adding href to tab or adding Link makes vuetify buggy 8 | with rv.Tab() as tab: 9 | # with solara.Link(path): 10 | solara.Text(label) 11 | router = solara.use_router() 12 | 13 | def go(*ignore): 14 | router.push(path) 15 | 16 | rvue.use_event(tab, "click.prevent.stop", go) 17 | return tab 18 | 19 | 20 | @solara.component 21 | def TabNavigation(children=[], vertical=False, **kwargs): 22 | children = children or [] 23 | route_current, all_routes = solara.use_route() 24 | 25 | tab_index = all_routes.index(route_current) if route_current is not None else 0 26 | 27 | with rv.Tabs(v_model=tab_index, vertical=vertical, **kwargs) as main: 28 | for i, route in enumerate(all_routes): 29 | path = solara.resolve_path(route) 30 | LinkTab(path, route.label or "No title") 31 | if route_current is None: 32 | return solara.Error("Page does not exist") 33 | 34 | with rv.TabsItems(v_model=tab_index, children=children): 35 | pass 36 | 37 | return main 38 | -------------------------------------------------------------------------------- /solara/components/title.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 39 | -------------------------------------------------------------------------------- /solara/hooks/__init__.py: -------------------------------------------------------------------------------- 1 | from .dataframe import * # noqa: F401 F403 2 | from .misc import * # noqa: F401 F403 3 | from .use_reactive import use_reactive # noqa: F401 F403 4 | from .use_thread import use_thread # noqa: F401 F403 5 | -------------------------------------------------------------------------------- /solara/kitchensink.py: -------------------------------------------------------------------------------- 1 | import reacton # noqa: F401 2 | from reacton import ipyvue as vue # noqa: F401 3 | from reacton import ipyvuetify as v # noqa: F401 4 | from reacton import ipywidgets as w # noqa: F401 5 | 6 | import solara as sol # noqa: F401 7 | 8 | from . import layout # noqa: F401 9 | -------------------------------------------------------------------------------- /solara/lab/components/__init__.py: -------------------------------------------------------------------------------- 1 | from .chat import ChatBox, ChatInput, ChatMessage # noqa: F401 2 | from .confirmation_dialog import ConfirmationDialog # noqa: F401 3 | from .input_date import InputDate, InputDateRange # noqa: F401 4 | from .input_time import InputTime as InputTime 5 | from .menu import ClickMenu, ContextMenu, Menu # noqa: F401 F403 6 | from .tabs import Tab, Tabs # noqa: F401 7 | from .theming import ThemeToggle, theme, use_dark_effective # noqa: F401 8 | -------------------------------------------------------------------------------- /solara/lab/components/cross_filter.py: -------------------------------------------------------------------------------- 1 | # for backwards compatibility 2 | from solara.components.cross_filter import ( # noqa: F401 3 | CrossFilterDataFrame, 4 | CrossFilterReport, 5 | CrossFilterSelect, 6 | CrossFilterSlider, 7 | ) 8 | -------------------------------------------------------------------------------- /solara/lab/hooks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/lab/hooks/__init__.py -------------------------------------------------------------------------------- /solara/lab/hooks/dataframe.py: -------------------------------------------------------------------------------- 1 | from ..utils.dataframe import df_columns as use_df_column_names # noqa: F401 2 | from ..utils.dataframe import df_row_names as df_row_names 3 | -------------------------------------------------------------------------------- /solara/lab/toestand.py: -------------------------------------------------------------------------------- 1 | # for backwards compatibility 2 | from solara.toestand import KernelStoreValue as ConnectionStore # noqa: F401 3 | from solara.toestand import Reactive, Ref, State, use_sync_external_store # noqa: F401 4 | -------------------------------------------------------------------------------- /solara/lab/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .cookies import cookies # noqa: F401 2 | from .headers import headers # noqa: F401 3 | -------------------------------------------------------------------------------- /solara/lab/utils/cookies.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional, cast 2 | 3 | from solara.toestand import Reactive 4 | 5 | cookies: Reactive[Optional[Dict[str, str]]] = Reactive(cast(Optional[Dict[str, str]], None), key="solara.lab.cookies") 6 | -------------------------------------------------------------------------------- /solara/lab/utils/headers.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Optional, cast 2 | 3 | from solara.toestand import Reactive 4 | 5 | headers: Reactive[Optional[Dict[str, List[str]]]] = Reactive(cast(Optional[Dict[str, List[str]]], None), key="solara.lab.headers") 6 | -------------------------------------------------------------------------------- /solara/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/py.typed -------------------------------------------------------------------------------- /solara/server/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/server/__init__.py -------------------------------------------------------------------------------- /solara/server/assets/custom.css: -------------------------------------------------------------------------------- 1 | /* not empty * 2 | -------------------------------------------------------------------------------- /solara/server/assets/custom.js: -------------------------------------------------------------------------------- 1 | /* not empty */ 2 | -------------------------------------------------------------------------------- /solara/server/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/server/assets/favicon.png -------------------------------------------------------------------------------- /solara/server/assets/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /solara/server/assets/theme.js: -------------------------------------------------------------------------------- 1 | vuetifyThemes = { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /solara/server/fastapi.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | 3 | from . import starlette 4 | 5 | app = FastAPI(routes=starlette.routes) 6 | -------------------------------------------------------------------------------- /solara/server/jupyter/__init__.py: -------------------------------------------------------------------------------- 1 | from .server_extension import _load_jupyter_server_extension # noqa 2 | from .server_extension import load_jupyter_server_extension # noqa 3 | -------------------------------------------------------------------------------- /solara/server/jupyter/cdn_handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import mimetypes 3 | 4 | import tornado.web 5 | from jupyter_server.base.handlers import JupyterHandler 6 | 7 | from solara.server import settings 8 | from solara.server.cdn_helper import get_data 9 | 10 | logger = logging.getLogger("Solara.cdn") 11 | 12 | 13 | class CdnHandler(JupyterHandler): 14 | def initialize(self, cache_directory=settings.assets.proxy_cache_dir): 15 | self.cache_directory = cache_directory 16 | logging.info("Using %r as cache directory", self.cache_directory) 17 | 18 | async def get(self, path=None): 19 | try: 20 | content = get_data(self.cache_directory, path) 21 | except Exception as e: 22 | logger.warning(e) 23 | raise tornado.web.HTTPError(500) 24 | 25 | mime = mimetypes.guess_type(path) 26 | if mime[0] is not None: 27 | self.set_header("Content-Type", mime[0]) 28 | self.write(content) 29 | -------------------------------------------------------------------------------- /solara/server/pyinstaller/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | 4 | HERE = Path(__file__).parent 5 | 6 | 7 | def get_hook_dirs(): 8 | # used for pyinstaller 9 | return [str(HERE)] 10 | -------------------------------------------------------------------------------- /solara/server/pyinstaller/hook-ipyreact.py: -------------------------------------------------------------------------------- 1 | from PyInstaller.utils.hooks import collect_data_files, copy_metadata, collect_submodules 2 | 3 | hiddenimports = collect_submodules("ipyreact") 4 | datas = collect_data_files("ipyreact") # codespell:ignore datas 5 | datas += copy_metadata("ipyreact") # codespell:ignore datas 6 | -------------------------------------------------------------------------------- /solara/server/pyinstaller/hook-ipyvuetify.py: -------------------------------------------------------------------------------- 1 | from PyInstaller.utils.hooks import collect_data_files, copy_metadata, collect_submodules 2 | 3 | hiddenimports = collect_submodules("ipyvuetify") 4 | datas = collect_data_files("ipyvuetify") # codespell:ignore datas 5 | datas += copy_metadata("ipyvuetify") # codespell:ignore datas 6 | -------------------------------------------------------------------------------- /solara/server/pyinstaller/hook-solara.py: -------------------------------------------------------------------------------- 1 | from PyInstaller.utils.hooks import collect_data_files, collect_submodules 2 | 3 | hiddenimports = collect_submodules("solara") 4 | datas = collect_data_files("solara") # codespell:ignore datas 5 | datas += collect_data_files("solara-ui") # codespell:ignore datas 6 | 7 | # this makes sure that inspect.getfile works, which is used for the 8 | # vue component decorator 9 | module_collection_mode = "pyz+py" 10 | -------------------------------------------------------------------------------- /solara/server/templates/index.html.j2: -------------------------------------------------------------------------------- 1 | {% extends "solara.html.j2" %} 2 | -------------------------------------------------------------------------------- /solara/server/templates/loader-plain.css: -------------------------------------------------------------------------------- 1 | #loader-container { 2 | width: 100vw; 3 | height: 100vh; 4 | position: absolute; 5 | margin: 0; 6 | padding: 0; 7 | z-index: 998; 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | } 12 | -------------------------------------------------------------------------------- /solara/server/templates/loader-plain.html: -------------------------------------------------------------------------------- 1 | {% raw -%} 2 | 3 |
4 | 5 | 7 | 8 | 9 | 10 |

{{ loading_text }}

11 |
12 |
13 |
14 |
15 | 16 | A widget with mount-id="solara-main" should go here 17 | 18 |
19 |
20 | {% endraw -%} 21 | -------------------------------------------------------------------------------- /solara/template/button.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | clicks = solara.reactive(0) 4 | 5 | 6 | @solara.component 7 | def Page(): 8 | color = "green" 9 | if clicks.value >= 5: 10 | color = "red" 11 | 12 | def increment(): 13 | clicks.value += 1 14 | print("clicks", clicks) # noqa 15 | 16 | solara.Button(label=f"Clicked: {clicks}", on_click=increment, color=color) 17 | -------------------------------------------------------------------------------- /solara/template/portal/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E203 W503 3 | max-line-length = 160 4 | per-file-ignores = 5 | # line too long (170 > 160 characters) 6 | ipywidgets.py: E501 7 | -------------------------------------------------------------------------------- /solara/template/portal/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v2.3.0 4 | hooks: 5 | - id: check-yaml 6 | - id: end-of-file-fixer 7 | - id: trailing-whitespace 8 | - repo: https://github.com/psf/black 9 | rev: 22.3.0 10 | hooks: 11 | - id: black 12 | language_version: python3.8 13 | - repo: https://gitlab.com/pycqa/flake8 14 | rev: 4.0.1 15 | hooks: 16 | - id: flake8 17 | - repo: https://github.com/PyCQA/isort 18 | rev: 5.10.1 19 | hooks: 20 | - id: isort 21 | files: \.py$ 22 | args: [--profile=black] 23 | - repo: https://github.com/pre-commit/mirrors-mypy 24 | rev: "v0.942" # Use the sha / tag you want to point at 25 | hooks: 26 | - id: mypy 27 | args: [--no-strict-optional, --ignore-missing-imports] 28 | additional_dependencies: [types-requests, types-markdown] 29 | -------------------------------------------------------------------------------- /solara/template/portal/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /solara/template/portal/Procfile: -------------------------------------------------------------------------------- 1 | # heroku by default sets WEB_CONCURRENCY=2 2 | # see: https://devcenter.heroku.com/changelog-items/618 3 | # which uvicorn picks up, unless we explicitly set --workers --1 4 | # see https://www.uvicorn.org/deployment/ 5 | # we do not support multiple workers yet 6 | # we also need to bind to 0.0.0.0 otherwise heroku cannot route to our server 7 | web: solara run solara_portal.pages --port=$PORT --no-open --host=0.0.0.0 --workers 1 8 | -------------------------------------------------------------------------------- /solara/template/portal/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | check_untyped_defs = True 3 | ignore_missing_imports = True 4 | -------------------------------------------------------------------------------- /solara/template/portal/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling==1.26.3"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "solara-portal" 7 | license = {file = "LICENSE"} 8 | classifiers = ["License :: OSI Approved :: MIT License"] 9 | dynamic = ["version", "description"] 10 | dependencies = [ 11 | "solara", 12 | ] 13 | 14 | [tool.hatch.version] 15 | path = "solara_portal/__init__.py" 16 | 17 | 18 | 19 | [project.urls] 20 | Home = "https://www.github.com/widgetti/solara" 21 | 22 | [tool.black] 23 | line-length = 160 24 | 25 | [tool.isort] 26 | profile = "black" 27 | -------------------------------------------------------------------------------- /solara/template/portal/solara_portal/__init__.py: -------------------------------------------------------------------------------- 1 | """Example Solara app as python packages""" 2 | 3 | __title__ = "Solara example app" 4 | __version__ = "0.0.1" 5 | -------------------------------------------------------------------------------- /solara/template/portal/solara_portal/components/__init__.py: -------------------------------------------------------------------------------- 1 | from .header import Header # noqa 2 | from .layout import Layout # noqa 3 | -------------------------------------------------------------------------------- /solara/template/portal/solara_portal/components/article.py: -------------------------------------------------------------------------------- 1 | import reacton.ipyvuetify as rv 2 | import solara 3 | 4 | from ..data import articles 5 | 6 | 7 | @solara.component 8 | def ArticleCard(name): 9 | article = articles[name] 10 | with rv.Card(max_width="400px") as main: 11 | with solara.Link(f"/article/{name}"): 12 | rv.Img(height="250", src=article.image_url) 13 | rv.CardTitle(children=[article.title]) 14 | with rv.CardText(): 15 | solara.Markdown(article.description) 16 | with solara.Link(f"/article/{name}"): 17 | solara.Button("Read article", text=True, icon_name="mdi-book-open") 18 | return main 19 | 20 | 21 | @solara.component 22 | def Overview(): 23 | with solara.ColumnsResponsive(12) as main: 24 | with solara.Card("Company articles"): 25 | with solara.ColumnsResponsive(12, small=6, large=4): 26 | for name in articles: 27 | ArticleCard(name) 28 | return main 29 | -------------------------------------------------------------------------------- /solara/template/portal/solara_portal/components/data.py: -------------------------------------------------------------------------------- 1 | import reacton.ipyvuetify as rv 2 | import solara 3 | 4 | from ..data import dfs 5 | 6 | 7 | @solara.component 8 | def DataCard(name): 9 | df = dfs[name].df 10 | with rv.Card(max_width="400px") as main: 11 | with solara.Link(f"/tabular/{name}"): 12 | rv.Img(height="250", src=dfs[name].image_url) 13 | rv.CardTitle(children=[dfs[name].title]) 14 | with rv.CardText(): 15 | solara.Markdown(f"*{len(df):,} rows*") 16 | with solara.Link(f"/tabular/{name}"): 17 | solara.Button("Open table view", text=True, icon_name="mdi-table") 18 | return main 19 | 20 | 21 | @solara.component 22 | def Overview(): 23 | with solara.ColumnsResponsive(12) as main: 24 | with solara.Card("Datasets"): 25 | with solara.ColumnsResponsive(12, small=6, large=4): 26 | for name in dfs: 27 | DataCard(name) 28 | return main 29 | -------------------------------------------------------------------------------- /solara/template/portal/solara_portal/components/header.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Header(): 6 | pass 7 | -------------------------------------------------------------------------------- /solara/template/portal/solara_portal/components/layout.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Layout(children=[]): 6 | return solara.VBox(children=children) 7 | -------------------------------------------------------------------------------- /solara/template/portal/solara_portal/pages/article/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import solara 4 | 5 | from ... import data 6 | from ...components.article import Overview 7 | 8 | 9 | @solara.component 10 | def Page(name: Optional[str] = None, page: int = 0, page_size=100): 11 | if name is None: 12 | with solara.Column() as main: 13 | solara.Title("Solara demo » Articles") 14 | Overview() 15 | return main 16 | if name not in data.articles: 17 | return solara.Error(f"No such article: {name!r}") 18 | article = data.articles[name] 19 | with solara.ColumnsResponsive(12) as main: 20 | solara.Title("Solara demo » Article » " + article.title) 21 | with solara.Link("/article"): 22 | solara.Text("« Back to overview") 23 | with solara.Card(): 24 | pre = f"# {article.title}\n\n" 25 | solara.Markdown(pre + article.markdown) 26 | return main 27 | -------------------------------------------------------------------------------- /solara/template/portal/solara_portal/pages/tabular.py: -------------------------------------------------------------------------------- 1 | """This Page takes an extra argument, meaning that it can cache urls like /tabular/titanic 2 | and pass the last part of the url as argument to the Page component, so we can render content 3 | dynamically. 4 | """ 5 | 6 | from typing import Optional 7 | 8 | import solara 9 | 10 | from .. import data 11 | from ..components import data as data_components 12 | 13 | 14 | @solara.component 15 | def Page(name: Optional[str] = None, page: int = 0, page_size=100): 16 | if name is None or name not in data.dfs: 17 | with solara.Column() as main: 18 | solara.Title("Solara demo » table view") 19 | data_components.Overview() 20 | return main 21 | 22 | df = data.dfs[name].df 23 | with solara.ColumnsResponsive(12) as main: 24 | with solara.Link("/tabular"): 25 | solara.Text("« Back to overview") 26 | solara.DataTable(df=df, page=page, items_per_page=page_size) 27 | with solara.Head(): 28 | solara.Title(f"Solara demo » table view » {name}") 29 | return main 30 | -------------------------------------------------------------------------------- /solara/template/portal/solara_portal/pages/viz/overview.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | from ... import data 4 | from ...components import Layout 5 | 6 | 7 | @solara.component 8 | def Page(name: str, page: int = 0, page_size=100): 9 | if name not in data.dfs: 10 | return solara.Error(f"No such dataframe: {name!r}") 11 | df = data.dfs[name] 12 | with Layout() as main: 13 | solara.DataTable(df=df, page=page, items_per_page=page_size) 14 | return main 15 | -------------------------------------------------------------------------------- /solara/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/test/__init__.py -------------------------------------------------------------------------------- /solara/website/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/website/__init__.py -------------------------------------------------------------------------------- /solara/website/assets/images/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/website/assets/images/logo-small.png -------------------------------------------------------------------------------- /solara/website/assets/theme.js: -------------------------------------------------------------------------------- 1 | vuetifyThemes = { 2 | light: { 3 | primary: '#ff991f', 4 | }, 5 | dark: { 6 | primary: '#f77e14', 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /solara/website/components/__init__.py: -------------------------------------------------------------------------------- 1 | from .docs import CategoryLayout, Gallery, NoPage, SubCategoryLayout, WithCode # noqa 2 | from .header import Header 3 | from .markdown import MarkdownWithMetadata 4 | 5 | __all__ = ["Header", "MarkdownWithMetadata", "CategoryLayout", "Gallery", "NoPage", "SubCategoryLayout", "WithCode"] 6 | -------------------------------------------------------------------------------- /solara/website/components/algolia.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component_vue("algolia_api.vue") 5 | def Algolia(): 6 | pass 7 | -------------------------------------------------------------------------------- /solara/website/components/algolia.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 25 | -------------------------------------------------------------------------------- /solara/website/components/breadcrumbs.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | import solara 3 | 4 | 5 | @solara.component 6 | def BreadCrumbs(): 7 | router = solara.use_router() 8 | routes = router.path_routes 9 | 10 | with solara.Row(style={"align-items": "center", "flex-wrap": "wrap"}) as main: 11 | for i, route in enumerate(routes): 12 | if i == len(routes) - 1: 13 | solara.Text(route.label or route.path, style={"color": "var(--color-text-fade)"}) 14 | else: 15 | with solara.Link(solara.resolve_path(route), style={"color": "var(--color-text-fade)"}): 16 | solara.Text(route.label or route.path) 17 | if i != len(routes) - 1: 18 | solara.Text("/", style={"font-size": "1.5rem", "color": "var(--color-text-fade)"}) 19 | return main 20 | 21 | 22 | def _resolve_path_to_route(path_to_find: List[str], all_routes: List[solara.Route], routes: List[solara.Route] = []): 23 | if len(path_to_find) == 0: 24 | return routes 25 | for route in all_routes: 26 | if path_to_find[0] == route.path: 27 | routes += [route] 28 | return _resolve_path_to_route(path_to_find[1:], route.children, routes) 29 | -------------------------------------------------------------------------------- /solara/website/components/mailchimp.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import solara 4 | 5 | HERE = Path(__file__).parent 6 | 7 | # html = Path(HERE / "mailchimp.v").read_text() 8 | 9 | 10 | @solara.component_vue("mailchimp.vue") 11 | def MailChimp(location: str): 12 | pass 13 | -------------------------------------------------------------------------------- /solara/website/components/markdown_nav.vue: -------------------------------------------------------------------------------- 1 | 17 | 35 | -------------------------------------------------------------------------------- /solara/website/pages/about/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from solara.website.components.markdown import MarkdownWithMetadata 4 | 5 | 6 | title = "About Us" 7 | HERE = Path(__file__) 8 | 9 | Page = MarkdownWithMetadata(Path(HERE.parent / "about.md").read_text()) 10 | -------------------------------------------------------------------------------- /solara/website/pages/about/about.md: -------------------------------------------------------------------------------- 1 | # About Us 2 | 3 | Solara is a framework created by Maarten Breddels, and developed by [Widgetti](https://widgetti.io). Widgetti is a small company based in the Netherlands, focusing on developing tools for the Jupyter ecosystem. We combine ipywidgets and python experience with expertise in front-end development to help data scientists and python app developers make the most of their time. If you could use our years of diverse industry experience, reach out [here](/contact). You can find out more about Solara [here](/documentation/getting_started/introduction), or get to know our team [here](/our_team). 4 | -------------------------------------------------------------------------------- /solara/website/pages/apps/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page(): 6 | solara.Markdown( 7 | """ 8 | Hi there 👋! All fullscreen apps are linked from [the examples page](/examples). 9 | """ 10 | ) 11 | 12 | 13 | @solara.component 14 | def Layout(children): 15 | route, routes = solara.use_route() 16 | return children[0] 17 | -------------------------------------------------------------------------------- /solara/website/pages/apps/authorization/admin.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | from . import user 4 | 5 | github_url = solara.util.github_url(__file__) 6 | 7 | 8 | @solara.component 9 | def Page(): 10 | assert user.value is not None 11 | solara.Markdown(f"Hi {user.value.username}, you are an admin") 12 | solara.Button(label="View source", icon_name="mdi-github-circle", attributes={"href": github_url, "target": "_blank"}, text=True, outlined=True) 13 | -------------------------------------------------------------------------------- /solara/website/pages/apps/authorization/users.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | from . import user 4 | 5 | github_url = solara.util.github_url(__file__) 6 | 7 | 8 | @solara.component 9 | def Page(): 10 | assert user.value is not None 11 | solara.Markdown(f"Hi {user.value.username}!") 12 | solara.Button(label="View source", icon_name="mdi-github-circle", attributes={"href": github_url, "target": "_blank"}, text=True, outlined=True) 13 | -------------------------------------------------------------------------------- /solara/website/pages/apps/multipage/page1.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | from . import SharedComponent 4 | 5 | github_url = solara.util.github_url(__file__) 6 | 7 | 8 | @solara.component 9 | def Sub(): 10 | with solara.Card("Sub component", margin=0, classes=["my-2"]): 11 | solara.Markdown("This sub component is the best") 12 | with solara.Sidebar(): 13 | with solara.Card("Sidebar of sub component", margin=0, elevation=0): 14 | solara.Markdown("*Markdown* **is** 👍") 15 | SharedComponent() 16 | 17 | 18 | @solara.component 19 | def Page(): 20 | with solara.Sidebar(): 21 | with solara.Card("Sidebar of page 1", margin=0, elevation=0): 22 | solara.Markdown("Hi there 👋!") 23 | solara.Button(label="View source", icon_name="mdi-github-circle", attributes={"href": github_url, "target": "_blank"}, text=True, outlined=True) 24 | with solara.Card("Page 1"): 25 | Sub() 26 | solara.Markdown("Page 1 is the best") 27 | -------------------------------------------------------------------------------- /solara/website/pages/apps/tutorial-streamlit.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page(): 6 | x, set_x = solara.use_state(2) 7 | x_squared = x**2 8 | 9 | with solara.Sidebar(): 10 | solara.Markdown("## My First Solara app ☀️") 11 | solara.SliderInt(label="x", value=x, on_value=set_x) 12 | solara.Markdown(f"{x} squared = {x_squared}") 13 | 14 | 15 | @solara.component 16 | def Layout(children): 17 | route, routes = solara.use_route() 18 | return solara.AppLayout(children=children) 19 | -------------------------------------------------------------------------------- /solara/website/pages/changelog/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from solara.website.components.sidebar import Sidebar 4 | from solara.website.components import MarkdownWithMetadata 5 | 6 | title = "Changelog" 7 | HERE = Path(__file__) 8 | 9 | Page = MarkdownWithMetadata(Path(HERE.parent / "changelog.md").read_text()) 10 | Sidebar = Sidebar 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from solara.autorouting import generate_routes_directory 4 | from solara.website.components.markdown import MarkdownWithMetadata 5 | 6 | HERE = Path(__file__) 7 | # if we didn't put the content in the subdirectory, but pointed to the current file 8 | # we would include the current file recursively, causing an infinite loop 9 | routes = generate_routes_directory(HERE.parent / "content", MarkdownWithMetadata) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/00-overview.md: -------------------------------------------------------------------------------- 1 | The entries are meant as deeper dives into specific topics. Although we try to write each as a standalone document, some parts may build on previous ones. 2 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/10-howto/00-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview of how-to articles 3 | description: The how-tos are meant as deeper dives into specific topics from various perspectives 4 | --- 5 | 6 | The how-tos are meant as deeper dives into specific topics. Although we try to write each how-to as a standalone document, some parts of a how-to may build on top of a previous one. 7 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/15-reference/00-overview.md: -------------------------------------------------------------------------------- 1 | # Reference documentation 2 | 3 | Our reference documentation informs you about how certain parts in Solara work. 4 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/15-reference/40-static_files.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using static files and resources in your Solara application 3 | description: When using Solara, you can host static files, like images, which will then be available at /static/public, in the ../public folder. 4 | --- 5 | # Static files 6 | 7 | Files located on your local filesystem at the `../public` directory will be served by the Solara server at `/static/public`. A typical directory layout looks like this: 8 | 9 | ``` 10 | ├── pages 11 | │ ├── 01-landing-page.md 12 | │ ├── 02-some_app.py 13 | └── public 14 | └── beach.jpeg 15 | ``` 16 | 17 | For instance, on this server, the `beach.jpeg` file will be available at `/static/public/beach.jpeg` and the full URL will be [`https://solara.dev/static/public/beach.jpeg`](https://solara.dev/static/public/beach.jpeg) 18 | 19 | Putting the `public` directory 1 level higher than the `pages` directory avoids name collision with pages. 20 | 21 | ```solara 22 | import solara 23 | 24 | 25 | @solara.component 26 | def Page(): 27 | image_url = "/static/public/beach.jpeg" 28 | with solara.Card(title="The following image is served from the ../public directory"): 29 | solara.Image(image_url) 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/15-reference/90-notebook-support.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using Solara within notebooks 3 | description: Solara can be used to generate an interactive app directly using your notebook file by running just one command. 4 | --- 5 | # Notebook support 6 | 7 | We also support notebooks, simply assign to the `Page` variable in a code cell, save your notebook, and run `$ solara run myapp.ipynb`. If you widget or component is called differently, run like `$ solara run myapp.ipynb:myobject.nested_widget`. 8 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/20-understanding/00-introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Understanding Solara's functionality 3 | description: Solara builds on existing technologies. If you are new to Solara or some of the underlying technologies, you may feel lost at times. 4 | These 'understanding' guides are meant to help you understand topics at a deeper level and how they connect to Solara. 5 | --- 6 | 7 | # Introduction 8 | 9 | Solara builds on existing technologies. If you are new to Solara or some of the underlying technologies, you may feel lost at times. 10 | These 'understanding' guides are meant to help you understand topics at a deeper level and how they connect to Solara. 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/20-understanding/20-solara.md: -------------------------------------------------------------------------------- 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. 19 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/20-understanding/60-voila.md: -------------------------------------------------------------------------------- 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. 13 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/30-enterprise/00-overview.md: -------------------------------------------------------------------------------- 1 | # Solara enterprise 2 | 3 | Solara enterprise is a not Open Source, but the source code is visible, is free for non-commercial use and installable from pypi. 4 | 5 | For commercial use, you require a license. Please see [pricing](/pricing) for more information. 6 | 7 | Solara-enterprise will stay free for non-commercial use and features that are in solara will **not be moved to solara-enterprise**. 8 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/advanced/content/40-development/00-overview.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/website/pages/documentation/advanced/content/40-development/00-overview.md -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Overview 3 | Click on one of the items on the left. 4 | """ 5 | 6 | import solara 7 | from solara.website.components import CategoryLayout, Gallery 8 | 9 | _title = "API" 10 | 11 | 12 | @solara.component 13 | def Page(route_external=None): 14 | Gallery(route_external) 15 | 16 | 17 | @solara.component 18 | def Layout(children=[]): 19 | CategoryLayout(children=children) 20 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/cross_filter/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/cross_filter/cross_filter_dataframe.py: -------------------------------------------------------------------------------- 1 | """# CrossFilterDataFrame""" 2 | 3 | import plotly 4 | 5 | import solara 6 | import solara.lab 7 | from solara.website.utils import apidoc 8 | 9 | title = "CrossFilterDataFrame" 10 | df = plotly.data.gapminder() 11 | 12 | 13 | @solara.component 14 | def Page(): 15 | solara.provide_cross_filter() 16 | 17 | solara.CrossFilterReport(df, classes=["py-2"]) 18 | solara.CrossFilterSelect(df, "country") 19 | solara.CrossFilterDataFrame(df) 20 | 21 | 22 | __doc__ += apidoc(solara.CrossFilterDataFrame.f) # type: ignore 23 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/cross_filter/cross_filter_report.py: -------------------------------------------------------------------------------- 1 | """# CrossFilterReport""" 2 | 3 | import plotly 4 | 5 | import solara 6 | import solara.lab 7 | from solara.website.utils import apidoc 8 | 9 | df = plotly.data.gapminder() 10 | 11 | 12 | @solara.component 13 | def Page(): 14 | solara.provide_cross_filter() 15 | solara.CrossFilterReport(df, classes=["py-2"]) 16 | solara.CrossFilterSelect(df, "country") 17 | solara.CrossFilterSlider(df, "gdpPercap", mode=">") 18 | 19 | 20 | __doc__ += apidoc(solara.CrossFilterReport.f) # type: ignore 21 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/cross_filter/cross_filter_select.py: -------------------------------------------------------------------------------- 1 | """# CrossFilterSelect""" 2 | 3 | import plotly 4 | 5 | import solara 6 | import solara.lab 7 | from solara.website.utils import apidoc 8 | 9 | df = plotly.data.tips() 10 | 11 | 12 | @solara.component 13 | def Page(): 14 | solara.provide_cross_filter() 15 | solara.CrossFilterReport(df, classes=["py-2"]) 16 | solara.CrossFilterSelect(df, "sex") 17 | solara.CrossFilterSelect(df, "time") 18 | 19 | 20 | __doc__ += apidoc(solara.CrossFilterSelect.f) # type: ignore 21 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/cross_filter/cross_filter_slider.py: -------------------------------------------------------------------------------- 1 | """# CrossFilterSlider""" 2 | 3 | import plotly 4 | 5 | import solara 6 | import solara.lab 7 | from solara.website.utils import apidoc 8 | 9 | df = plotly.data.gapminder() 10 | 11 | 12 | @solara.component 13 | def Page(): 14 | solara.provide_cross_filter() 15 | solara.CrossFilterReport(df, classes=["py-2"]) 16 | solara.CrossFilterSlider(df, "pop", mode=">") 17 | solara.CrossFilterSlider(df, "gdpPercap", mode="<") 18 | 19 | 20 | __doc__ += apidoc(solara.CrossFilterSlider.f) # type: ignore 21 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_cross_filter.py: -------------------------------------------------------------------------------- 1 | """# use_cross_filter""" 2 | 3 | import plotly 4 | 5 | import solara 6 | import solara.lab 7 | from solara.website.utils import apidoc 8 | 9 | title = "use_cross_filter" 10 | 11 | 12 | df = plotly.data.gapminder() 13 | 14 | 15 | @solara.component 16 | def Page(): 17 | solara.provide_cross_filter() 18 | solara.CrossFilterReport(df, classes=["py-2"]) 19 | solara.CrossFilterSelect(df, "continent") 20 | solara.CrossFilterSlider(df, "gdpPercap", mode=">") 21 | 22 | 23 | __doc__ += apidoc(solara.use_cross_filter) # type: ignore 24 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_dark_effective.py: -------------------------------------------------------------------------------- 1 | """# use_dark_effective""" 2 | 3 | import solara 4 | import solara.autorouting 5 | import solara.lab 6 | from solara.website.utils import apidoc 7 | 8 | from . import NoPage 9 | 10 | title = "use_dark_effective" 11 | Page = NoPage 12 | __doc__ += apidoc(solara.lab.use_dark_effective) # type: ignore 13 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_effect.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from solara.website.components import NoPage 4 | 5 | HERE = Path(__file__).parent 6 | __doc__ = open(HERE / "use_effect.md").read() 7 | 8 | Page = NoPage 9 | title = "use_effect" 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_exception.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | title = "use_exception" 4 | set_fail = None 5 | clear = None 6 | 7 | 8 | @solara.component 9 | def UnstableComponent(number: int): 10 | if number == 3: 11 | raise Exception("I do not like 3") 12 | return solara.Text(f"You picked {number}") 13 | 14 | 15 | @solara.component 16 | def Page(): 17 | value, set_value = solara.use_state(1) 18 | value_previous = solara.use_previous(value) 19 | exception, clear_exception = solara.use_exception() 20 | # print(exception) 21 | if exception: 22 | 23 | def reset(): 24 | set_value(value_previous) 25 | clear_exception() 26 | 27 | solara.Text("Exception: " + str(exception)) 28 | solara.Button(label="Go to previous state", on_click=reset) 29 | else: 30 | solara.IntSlider(value=value, min=0, max=10, on_value=set_value, label="Pick a number, except 3") 31 | UnstableComponent(value) 32 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_memo.md: -------------------------------------------------------------------------------- 1 | # use_memo 2 | 3 | ```python 4 | def use_memo( 5 | f: Any, 6 | dependencies: Any | None = None, 7 | debug_name: str = None 8 | ) -> Any: 9 | ... 10 | ``` 11 | 12 | `use_memo` stores ([memoize](https://en.wikipedia.org/wiki/Memoization)) the function return on first render, and then excludes it from being re-executed, except when one of the `dependencies` changes. `dependencies` can take the value `None`, in which case dependencies are automatically obtained from nonlocal variables. If an empty list is passed as `dependencies` instead, the function is only executed once over the entire lifetime of the component. 13 | 14 | Not to be confused with [memorize](https://solara.dev/documentation/api/utilities/memoize) which can cache multiple return values and which can be used outside of component. 15 | 16 | See also the [Reacton docs](https://reacton.solara.dev/en/latest/api/#use_memo). 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_memo.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from solara.website.components import NoPage 4 | 5 | HERE = Path(__file__).parent 6 | __doc__ = open(HERE / "use_memo.md").read() 7 | 8 | Page = NoPage 9 | title = "use_memo" 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_previous.py: -------------------------------------------------------------------------------- 1 | """ 2 | # use_previous 3 | 4 | ```python 5 | def use_previous(value: T) -> T: 6 | ... 7 | ``` 8 | 9 | Returns the value from a previous render phase, or the current value on the first render. 10 | 11 | 12 | """ 13 | 14 | import solara 15 | 16 | title = "use_previous" 17 | 18 | 19 | @solara.component 20 | def Page(): 21 | value, set_value = solara.use_state(4) 22 | value_previous = solara.use_previous(value) 23 | solara.IntSlider("value", value=value, on_value=set_value) 24 | solara.Markdown( 25 | f""" 26 | **Current**: `{value}` 27 | 28 | **Previous**: `{value_previous}` 29 | """ 30 | ) 31 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_reactive.py: -------------------------------------------------------------------------------- 1 | """ 2 | # use_reactive 3 | 4 | """ 5 | 6 | import solara 7 | from solara.website.components import NoPage 8 | from solara.website.utils import apidoc 9 | 10 | title = "use_reactive" 11 | 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.use_reactive) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_state.py: -------------------------------------------------------------------------------- 1 | """# use_state""" 2 | 3 | import solara 4 | import solara.autorouting 5 | from solara.website.components import NoPage 6 | from solara.website.utils import apidoc 7 | 8 | title = "use_state" 9 | Page = NoPage 10 | __doc__ += apidoc(solara.use_state) # type: ignore 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/hooks/use_trait_observe.py: -------------------------------------------------------------------------------- 1 | """# use_trait_observe""" 2 | 3 | import solara 4 | import solara.autorouting 5 | import solara.lab 6 | from solara.website.utils import apidoc 7 | 8 | from . import NoPage 9 | 10 | title = "use_trait_observe" 11 | Page = NoPage 12 | __doc__ += apidoc(solara.use_trait_observe) # type: ignore 13 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/routing/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/routing/generate_routes.py: -------------------------------------------------------------------------------- 1 | """# generate_routes""" 2 | 3 | import solara 4 | import solara.autorouting 5 | from solara.website.components import NoPage 6 | from solara.website.utils import apidoc 7 | 8 | title = "generate_routes" 9 | Page = NoPage 10 | __doc__ += apidoc(solara.autorouting.generate_routes) # type: ignore 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/routing/generate_routes_directory.py: -------------------------------------------------------------------------------- 1 | """# generate_routes_directory""" 2 | 3 | import solara 4 | import solara.autorouting 5 | from solara.website.components import NoPage 6 | from solara.website.utils import apidoc 7 | 8 | title = "generate_routes_directory" 9 | Page = NoPage 10 | __doc__ += apidoc(solara.autorouting.generate_routes_directory) # type: ignore 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/routing/route.py: -------------------------------------------------------------------------------- 1 | """# Route""" 2 | 3 | import solara 4 | from solara.website.utils import apidoc 5 | 6 | routes = [ 7 | solara.Route(path="/"), 8 | solara.Route(path="kiwi"), 9 | solara.Route(path="banana"), 10 | solara.Route(path="apple"), 11 | ] 12 | 13 | 14 | @solara.component 15 | def Page(): 16 | route_current, routes = solara.use_route() 17 | 18 | solara.Markdown("*Click on one of the links below to change the route and see the url in your browser change, and match the route.*") 19 | with solara.VBox(): 20 | for route in routes: 21 | with solara.Link(route): 22 | current = route_current is route 23 | if current: 24 | solara.Success(f"You are at solara.Route(path={route.path!r})") 25 | else: 26 | solara.Info(f"Go to solara.Route(path={route.path!r})") 27 | 28 | 29 | __doc__ += apidoc(solara.Route, full=True) # type: ignore 30 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/routing/use_router.py: -------------------------------------------------------------------------------- 1 | """ 2 | # use_router 3 | 4 | """ 5 | 6 | import solara 7 | from solara.website.components import NoPage 8 | from solara.website.utils import apidoc 9 | 10 | title = "use_router" 11 | 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.use_router) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/utilities/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/utilities/component_vue.py: -------------------------------------------------------------------------------- 1 | """# component_vue""" 2 | 3 | import solara 4 | import solara.autorouting 5 | from solara.website.components import NoPage 6 | from solara.website.utils import apidoc 7 | 8 | title = "component_vue" 9 | Page = NoPage 10 | __doc__ += apidoc(solara.component_vue) # type: ignore 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/utilities/computed.py: -------------------------------------------------------------------------------- 1 | """ 2 | # computed 3 | 4 | """ 5 | 6 | import solara 7 | from solara.website.components import NoPage 8 | from solara.website.utils import apidoc 9 | 10 | title = "computed" 11 | 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.lab.computed) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/utilities/display.py: -------------------------------------------------------------------------------- 1 | """ 2 | # display 3 | 4 | """ 5 | 6 | import solara 7 | from solara.website.components import NoPage 8 | from solara.website.utils import apidoc 9 | 10 | title = "display" 11 | 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.display) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/utilities/get_kernel_id.py: -------------------------------------------------------------------------------- 1 | """ 2 | # get_kernel_id 3 | 4 | """ 5 | 6 | import solara 7 | from solara.website.components import NoPage 8 | from solara.website.utils import apidoc 9 | 10 | title = "get_kernel_id" 11 | 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.get_kernel_id) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/utilities/get_session_id.py: -------------------------------------------------------------------------------- 1 | """ 2 | # get_session_id 3 | 4 | """ 5 | 6 | import solara 7 | from solara.website.components import NoPage 8 | from solara.website.utils import apidoc 9 | 10 | title = "get_session_id" 11 | 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.get_session_id) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/utilities/memoize.py: -------------------------------------------------------------------------------- 1 | """#Memoize""" 2 | 3 | import time 4 | 5 | import solara 6 | import solara.lab 7 | from solara.website.utils import apidoc 8 | 9 | # make sure the cache is only created once 10 | storage = solara.cache.Memory(max_items=2) 11 | 12 | 13 | @solara.component 14 | def Page(): 15 | x, set_x = solara.use_state(5) 16 | 17 | @solara.memoize(storage=storage) 18 | def long_running_function(x: int) -> int: 19 | """This function takes a long time to run.""" 20 | time.sleep(3) 21 | return x**2 22 | 23 | result = long_running_function.use_thread(x) 24 | 25 | with solara.Card("Expensive computation") as main: 26 | solara.Markdown("We cache 2 values. Each computation takes 3 seconds. If you go back to a cached value, you will see the result immediately.") 27 | solara.IntSlider("x", value=x, on_value=set_x) 28 | if result.state == solara.ResultState.FINISHED: 29 | solara.Markdown(f"Square of {x} is {result.value}") 30 | else: 31 | solara.Markdown("Running...") 32 | return main 33 | 34 | 35 | __doc__ += apidoc(solara.memoize) # type: ignore 36 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/api/utilities/reactive.py: -------------------------------------------------------------------------------- 1 | """ 2 | # reactive 3 | 4 | """ 5 | 6 | import solara 7 | from solara.website.components import NoPage 8 | from solara.website.utils import apidoc 9 | 10 | title = "reactive" 11 | 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.reactive) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import CategoryLayout, Gallery 3 | 4 | 5 | @solara.component 6 | def Page(route_external=None): 7 | Gallery(route_external) 8 | 9 | 10 | @solara.component 11 | def Layout(children=[]): 12 | CategoryLayout(children=children) 13 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/advanced/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/advanced/link.py: -------------------------------------------------------------------------------- 1 | """# Link""" 2 | 3 | import solara 4 | from solara.website.utils import apidoc 5 | 6 | routes = [ 7 | solara.Route(path="/"), 8 | solara.Route(path="kiwi"), 9 | solara.Route(path="banana"), 10 | solara.Route(path="apple"), 11 | ] 12 | 13 | 14 | @solara.component 15 | def Page(): 16 | route_current, routes = solara.use_route() 17 | solara.Info("Note the address bar in the browser. It should change to the path of the link.") 18 | with solara.Row(): 19 | for route in routes: 20 | with solara.Link(route): 21 | current = route_current is route 22 | solara.Button(f"Go to {route.path}", color="red" if current else None) 23 | 24 | 25 | __doc__ += apidoc(solara.Link.f) # type: ignore 26 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/advanced/meta.py: -------------------------------------------------------------------------------- 1 | """# Meta""" 2 | 3 | import solara 4 | from solara.website.utils import apidoc 5 | 6 | 7 | @solara.component 8 | def Page(): 9 | solara.Info("Nothing to see here, only in this page's source code, or by looking at the google search results for this page.") 10 | with solara.Head(): 11 | solara.Meta( 12 | name="description", 13 | content="The Meta component can be used to set the description of a page. This is useful for SEO, or crawlers that index your page.", 14 | ) 15 | 16 | 17 | __doc__ += apidoc(solara.Meta.f) # type: ignore 18 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/advanced/style.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Style 3 | 4 | """ 5 | 6 | import solara 7 | from solara.website.utils import apidoc 8 | 9 | 10 | @solara.component 11 | def Page(): 12 | insert_css, set_insert_css = solara.use_state(True) 13 | 14 | css = """ 15 | .mybutton { 16 | font-family: Serif; 17 | } 18 | 19 | /* this selector has to be very specific to override the vuetify style */ 20 | .v-btn.mybutton { 21 | color: #4CAF50; /* Green */ 22 | } 23 | /* vuetify's background color css has very high CSS-specificity, so we use !important */ 24 | .mybutton { 25 | background-color: #FF9800 !important; /* Orange */ 26 | } 27 | """ 28 | 29 | solara.Checkbox(label="Use CSS", value=insert_css, on_value=set_insert_css) 30 | solara.Markdown( 31 | f""" 32 | ## CSS Example that styles the button below 33 | ```css 34 | {css} 35 | ``` 36 | """ 37 | ) 38 | if insert_css: 39 | solara.Style(css) 40 | solara.Button(label="Advanced users might want to style this", icon_name="mdi-thumb-up", classes=["mybutton"]) 41 | 42 | 43 | __doc__ += apidoc(solara.Style.f) # type: ignore 44 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/common.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.alias import rv 3 | 4 | 5 | @solara.component 6 | def ColorCard(title, color): 7 | with rv.Card(style_=f"background-color: {color}; width: 100%; height: 100%") as main: 8 | rv.CardTitle(children=[title]) 9 | return main 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/data/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/enterprise/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/enterprise/avatar.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Avatar 3 | 4 | """ 5 | 6 | from types import ModuleType 7 | from typing import Optional 8 | 9 | auth: Optional[ModuleType] 10 | try: 11 | from solara_enterprise import auth 12 | except ImportError: 13 | auth = None 14 | from solara.website.components import NoPage 15 | from solara.website.utils import apidoc 16 | 17 | title = "Avatar" 18 | 19 | Page = NoPage 20 | 21 | if auth: 22 | __doc__ += apidoc(auth.Avatar.f) # type: ignore 23 | else: 24 | __doc__ += "solara-enterprise not installed." 25 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/enterprise/avatar_menu.py: -------------------------------------------------------------------------------- 1 | """ 2 | # AvatarMenu 3 | 4 | """ 5 | 6 | from types import ModuleType 7 | from typing import Optional 8 | 9 | auth: Optional[ModuleType] 10 | try: 11 | from solara_enterprise import auth 12 | except ImportError: 13 | auth = None 14 | from solara.website.components import NoPage 15 | from solara.website.utils import apidoc 16 | 17 | title = "AvatarMenu" 18 | 19 | Page = NoPage 20 | 21 | 22 | if auth: 23 | __doc__ += apidoc(auth.AvatarMenu.f) # type: ignore 24 | else: 25 | __doc__ += "solara-enterprise not installed." 26 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/input/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/input/button.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Button 3 | 4 | """ 5 | 6 | import solara 7 | from solara.website.utils import apidoc 8 | 9 | 10 | @solara.component 11 | def Page(): 12 | count, set_count = solara.use_state(0) 13 | 14 | def increment(): 15 | set_count(count + 1) 16 | 17 | with solara.VBox() as main: 18 | with solara.HBox(): 19 | solara.Button(label=f"Clicked {count} times", on_click=increment, icon_name="mdi-thumb-up") 20 | return main 21 | 22 | 23 | __doc__ += apidoc(solara.Button.f) # type: ignore 24 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/input/checkbox.py: -------------------------------------------------------------------------------- 1 | """# Checkbox""" 2 | 3 | import solara 4 | from solara.website.components import NoPage 5 | from solara.website.utils import apidoc 6 | 7 | Page = NoPage 8 | 9 | 10 | __doc__ += apidoc(solara.Checkbox.f) # type: ignore 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/input/file_browser.py: -------------------------------------------------------------------------------- 1 | """# FileBrowser""" 2 | 3 | from pathlib import Path 4 | from typing import Optional, cast 5 | 6 | import solara 7 | from solara.website.utils import apidoc 8 | 9 | 10 | @solara.component 11 | def Page(): 12 | file, set_file = solara.use_state(cast(Optional[Path], None)) 13 | path, set_path = solara.use_state(cast(Optional[Path], None)) 14 | directory, set_directory = solara.use_state(Path("~").expanduser()) 15 | 16 | can_select = solara.ui_checkbox("Enable select") 17 | 18 | def reset_path(): 19 | set_path(None) 20 | set_file(None) 21 | 22 | # reset path and file when can_select changes 23 | solara.use_memo(reset_path, [can_select]) 24 | solara.FileBrowser(directory, on_directory_change=set_directory, on_path_select=set_path, on_file_open=set_file, can_select=can_select) 25 | solara.Info(f"You are in directory: {directory}") 26 | solara.Info(f"You selected path: {path}") 27 | solara.Info(f"You opened file: {file}") 28 | 29 | 30 | __doc__ += apidoc(solara.FileBrowser.f) # type: ignore 31 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/input/select.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Select components 3 | 4 | Select comes in two flavours: 5 | 6 | * `Select` for a singular selection 7 | * `SelectMultiple` which allows for multiple selections 8 | 9 | 10 | """ 11 | 12 | import solara 13 | from solara.website.components import NoPage 14 | from solara.website.utils import apidoc 15 | 16 | Page = NoPage 17 | 18 | 19 | __doc__ += "# Select" 20 | __doc__ += apidoc(solara.Select.f) # type: ignore 21 | __doc__ += "# SelectMultiple" 22 | __doc__ += apidoc(solara.SelectMultiple.f) # type: ignore 23 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/input/slider.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Sliders 3 | 4 | To support proper typechecks, we have multiple slider (all wrapping the ipyvuetify sliders). 5 | 6 | 7 | """ 8 | 9 | import solara 10 | from solara.website.components import NoPage 11 | from solara.website.utils import apidoc 12 | 13 | Page = NoPage 14 | 15 | __doc__ += "# SliderInt" 16 | __doc__ += apidoc(solara.SliderInt.f) # type: ignore 17 | __doc__ += "# SliderRangeInt" 18 | __doc__ += apidoc(solara.SliderRangeInt.f) # type: ignore 19 | 20 | __doc__ += "# SliderFloat" 21 | __doc__ += apidoc(solara.SliderFloat.f) # type: ignore 22 | __doc__ += "# SliderRangeFloat" 23 | __doc__ += apidoc(solara.SliderRangeFloat.f) # type: ignore 24 | 25 | __doc__ += "# SliderValue" 26 | __doc__ += apidoc(solara.SliderValue.f) # type: ignore 27 | 28 | __doc__ += "# SliderDate" 29 | __doc__ += apidoc(solara.SliderDate.f) # type: ignore 30 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/input/switch.py: -------------------------------------------------------------------------------- 1 | """# Switch""" 2 | 3 | import solara 4 | from solara.website.components import NoPage 5 | from solara.website.utils import apidoc 6 | 7 | Page = NoPage 8 | 9 | 10 | __doc__ += apidoc(solara.Switch.f) # type: ignore 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/input/togglebuttons.py: -------------------------------------------------------------------------------- 1 | """ 2 | # ToggleButtons 3 | 4 | ToggleButtons are in two flavours, for single, and for multiple selections. 5 | 6 | 7 | """ 8 | 9 | import solara 10 | from solara.website.components import NoPage 11 | from solara.website.utils import apidoc 12 | 13 | title = "ToggleButtons" 14 | 15 | Page = NoPage 16 | 17 | 18 | __doc__ += "# ToggleButtonsSingle" 19 | __doc__ += apidoc(solara.ToggleButtonsSingle.f) # type: ignore 20 | __doc__ += "# ToggleButtonsMultiple" 21 | __doc__ += apidoc(solara.ToggleButtonsMultiple.f) # type: ignore 22 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/lab/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/lab/input_date.py: -------------------------------------------------------------------------------- 1 | """ 2 | # InputDate 3 | 4 | This page contains the two variants of datepickers available in solara 5 | 6 | # InputDate 7 | """ 8 | 9 | import solara 10 | from solara.website.components import NoPage 11 | from solara.website.utils import apidoc 12 | 13 | title = "InputDate" 14 | 15 | 16 | __doc__ += apidoc(solara.lab.components.input_date.InputDate.f) # type: ignore 17 | __doc__ += "# InputDateRange" 18 | __doc__ += apidoc(solara.lab.components.input_date.InputDateRange.f) # type: ignore 19 | 20 | Page = NoPage 21 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/lab/input_time.py: -------------------------------------------------------------------------------- 1 | """ 2 | # InputTime 3 | """ 4 | 5 | import solara 6 | from solara.website.components import NoPage 7 | from solara.website.utils import apidoc 8 | 9 | title = "InputTime" 10 | 11 | 12 | __doc__ += apidoc(solara.lab.components.input_time.InputTime.f) # type: ignore 13 | 14 | 15 | Page = NoPage 16 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/lab/menu.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Menus 3 | 4 | This page contains the various kinds of menu elements available to use in solara 5 | 6 | # Menu 7 | """ 8 | 9 | import solara 10 | from solara.website.components import NoPage 11 | from solara.website.utils import apidoc 12 | 13 | title = "Menus" 14 | 15 | 16 | __doc__ += apidoc(solara.lab.components.menu.Menu.f) # type: ignore 17 | __doc__ += "# ClickMenu" 18 | __doc__ += apidoc(solara.lab.components.menu.ClickMenu.f) # type: ignore 19 | __doc__ += "# ContextMenu" 20 | __doc__ += apidoc(solara.lab.components.menu.ContextMenu.f) # type: ignore 21 | 22 | Page = NoPage 23 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/lab/tab.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Tab 3 | 4 | """ 5 | 6 | import solara 7 | import solara.lab 8 | from solara.website.utils import apidoc 9 | 10 | disabled = solara.reactive(False) 11 | icon = solara.reactive(True) 12 | 13 | 14 | @solara.component 15 | def Page(): 16 | solara.Checkbox(label="Disable Tab 2", value=disabled) 17 | solara.Checkbox(label="Show icon", value=icon) 18 | with solara.lab.Tabs(): 19 | with solara.lab.Tab("Tab 1"): 20 | solara.Markdown("Hello") 21 | with solara.lab.Tab("Tab 2", disabled=disabled.value, icon_name="mdi-home" if icon.value else None): 22 | solara.Markdown("World") 23 | 24 | 25 | __doc__ += apidoc(solara.lab.Tab.f) # type: ignore 26 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/lab/task.py: -------------------------------------------------------------------------------- 1 | """# Task""" 2 | 3 | import solara 4 | import solara.autorouting 5 | import solara.lab 6 | from solara.website.components import NoPage 7 | from solara.website.utils import apidoc 8 | 9 | title = "Task" 10 | Page = NoPage 11 | __doc__ += apidoc(solara.lab.task) # type: ignore 12 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/lab/use_task.py: -------------------------------------------------------------------------------- 1 | """# use_task""" 2 | 3 | import solara 4 | import solara.autorouting 5 | import solara.lab 6 | from solara.website.components import NoPage 7 | from solara.website.utils import apidoc 8 | 9 | title = "use_task" 10 | Page = NoPage 11 | __doc__ += apidoc(solara.lab.use_task) # type: ignore 12 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/app_bar.py: -------------------------------------------------------------------------------- 1 | """ 2 | # AppBar 3 | 4 | """ 5 | 6 | import solara 7 | import solara.lab 8 | from solara.website.components import NoPage 9 | from solara.website.utils import apidoc 10 | 11 | title = "AppBar" 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.AppBar.f) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/app_bar_title.py: -------------------------------------------------------------------------------- 1 | """ 2 | # AppBarTitle 3 | 4 | """ 5 | 6 | import solara 7 | import solara.lab 8 | from solara.website.components import NoPage 9 | from solara.website.utils import apidoc 10 | 11 | title = "AppBarTitle" 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.AppBarTitle.f) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/app_layout.py: -------------------------------------------------------------------------------- 1 | """ 2 | # AppLayout 3 | 4 | """ 5 | 6 | import solara 7 | import solara.lab 8 | from solara.website.utils import apidoc 9 | 10 | title = "AppLayout" 11 | 12 | 13 | @solara.component 14 | def Page(): 15 | return solara.Markdown( 16 | """ 17 | An example cannot be shown embedded in this page, Visit the [AppLayout page](/apps/scatter) to see an example. 18 | 19 | [![AppLayout screenshot](https://dxhl76zpt6fap.cloudfront.net/public/docs/app-layout.webp)](/apps/scatter) 20 | """ 21 | ) 22 | 23 | 24 | __doc__ += apidoc(solara.AppLayout.f) # type: ignore 25 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/card.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Card 3 | 4 | """ 5 | 6 | import solara 7 | import solara.lab 8 | from solara.website.components import NoPage 9 | from solara.website.utils import apidoc 10 | 11 | title = "Card" 12 | 13 | Page = NoPage 14 | 15 | __doc__ += apidoc(solara.Card.f) # type: ignore 16 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/card_actions.py: -------------------------------------------------------------------------------- 1 | """ 2 | # CardActions 3 | 4 | """ 5 | 6 | import solara 7 | import solara.lab 8 | from solara.website.components import NoPage 9 | from solara.website.utils import apidoc 10 | 11 | title = "CardActions" 12 | 13 | Page = NoPage 14 | 15 | 16 | __doc__ += apidoc(solara.CardActions.f) # type: ignore 17 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/column.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Column 3 | """ 4 | 5 | import solara 6 | import solara.lab 7 | from solara.website.utils import apidoc 8 | 9 | gap_size = solara.reactive("12px") 10 | align = solara.reactive("stretch") 11 | 12 | 13 | @solara.component 14 | def Page(): 15 | with solara.Card("Column demo") as main: 16 | with solara.Column(): 17 | solara.Text("Align:") 18 | solara.ToggleButtonsSingle(align, values=["start", "center", "end", "stretch"]) 19 | solara.Select( 20 | label="Gap size", 21 | values=["0px", "4px", "8px", "12px", "16px", "20px", "24px"], 22 | ).connect(gap_size) 23 | with solara.Column(gap=gap_size.value, align=align.value): 24 | colors = "green red orange brown yellow pink".split() 25 | for color in colors: 26 | solara.Button("Solara", color=color) 27 | return main 28 | 29 | 30 | __doc__ += apidoc(solara.Column.f) # type: ignore 31 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/columns.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Columns 3 | 4 | """ 5 | 6 | import solara 7 | import solara.lab 8 | from solara.website.utils import apidoc 9 | 10 | gutters = solara.reactive(True) 11 | gutters_dense = solara.reactive(True) 12 | 13 | 14 | @solara.component 15 | def Page(): 16 | with solara.Columns([1, 2, 1], gutters=gutters.value, gutters_dense=gutters_dense.value) as main: 17 | with solara.Card("Left", margin=0): 18 | solara.Checkbox(label="Gutters").connect(gutters) 19 | solara.Checkbox(label="Dense gutters").connect(gutters_dense) 20 | with solara.Card("Middle", margin=0): 21 | solara.Markdown("This column has a relative width of 2, the columns to the left and right have a relative width of 1.") 22 | with solara.Card("Right", margin=0): 23 | solara.Markdown("This column has a relative width of 1, the columns to the left has a relative width of 2.") 24 | return main 25 | 26 | 27 | __doc__ += apidoc(solara.Columns.f) # type: ignore 28 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/details.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Details 3 | """ 4 | 5 | import solara 6 | from solara.website.components import NoPage 7 | from solara.website.utils import apidoc 8 | 9 | title = "Details" 10 | 11 | Page = NoPage 12 | 13 | __doc__ += apidoc(solara.Details.f) # type: ignore 14 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/gridfixed.py: -------------------------------------------------------------------------------- 1 | """ 2 | # GridFixed 3 | 4 | Lays out children in a grid with a fixed number of columns. 5 | """ 6 | 7 | import solara 8 | 9 | from ..common import ColorCard 10 | 11 | title = "GridFixed" 12 | 13 | 14 | @solara.component 15 | def Page(): 16 | colors = "green red orange brown yellow pink".split() 17 | with solara.GridFixed(columns=3): 18 | for color in colors: 19 | ColorCard(color, color) 20 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/hbox.py: -------------------------------------------------------------------------------- 1 | """ 2 | # HBox 3 | Lays out children in horizontal direction. 4 | """ 5 | 6 | import solara 7 | 8 | from ..common import ColorCard 9 | 10 | 11 | @solara.component 12 | def Page(): 13 | with solara.VBox() as main: 14 | colors = "green red orange brown yellow pink".split() 15 | with solara.HBox(): 16 | for color in colors: 17 | ColorCard(color, color) 18 | return main 19 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/row.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Row 3 | """ 4 | 5 | import solara 6 | import solara.lab 7 | from solara.website.utils import apidoc 8 | 9 | gap_size = solara.reactive("12px") 10 | justify = solara.reactive("space-around") 11 | 12 | 13 | @solara.component 14 | def Page(): 15 | with solara.Card("Row demo") as main: 16 | with solara.Column(): 17 | solara.Text("Justify:") 18 | solara.ToggleButtonsSingle(justify, values=["start", "center", "end", "space-around", "space-between", "space-evenly"]) 19 | solara.Select( 20 | label="Gap size", 21 | values=["0px", "4px", "8px", "12px", "16px", "20px", "24px"], 22 | ).connect(gap_size) 23 | with solara.Row(gap=gap_size.value, justify=justify.value): 24 | colors = "green red orange brown yellow pink".split() 25 | for color in colors: 26 | solara.Button(label="Solara", color=color) 27 | return main 28 | 29 | 30 | __doc__ += apidoc(solara.Row.f) # type: ignore 31 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/sidebar.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Sidebar 3 | 4 | """ 5 | 6 | import solara 7 | import solara.lab 8 | from solara.website.utils import apidoc 9 | 10 | 11 | @solara.component 12 | def Page(): 13 | return solara.Markdown( 14 | """ 15 | 16 | The sidebar can only be shown in embedded mode on this page. 17 | Visit the [Scatter app demo](/apps/scatter) to see an example of a full sidebar used in Soalra server. 18 | 19 | [![AppLayout screenshot](https://dxhl76zpt6fap.cloudfront.net/public/docs/app-layout.webp)](/apps/scatter) 20 | """ 21 | ) 22 | 23 | 24 | __doc__ += apidoc(solara.Sidebar.f) # type: ignore 25 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/layout/vbox.py: -------------------------------------------------------------------------------- 1 | """ 2 | # VBox 3 | 4 | Lays out children in a vertical direction. 5 | """ 6 | 7 | import solara 8 | 9 | from ..common import ColorCard 10 | 11 | 12 | @solara.component 13 | def Page(): 14 | with solara.VBox() as main: 15 | colors = "green red orange brown yellow pink".split() 16 | with solara.VBox(): 17 | for color in colors: 18 | ColorCard(color, color) 19 | return main 20 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/output/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/output/file_download.py: -------------------------------------------------------------------------------- 1 | """# FileDownload""" 2 | 3 | import solara 4 | from solara.website.components import NoPage 5 | from solara.website.utils import apidoc 6 | 7 | Page = NoPage 8 | title = "FileDownload" 9 | 10 | 11 | __doc__ += apidoc(solara.FileDownload.f) # type: ignore 12 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/output/html.py: -------------------------------------------------------------------------------- 1 | """# HTML""" 2 | 3 | import solara 4 | from solara.website.utils import apidoc 5 | 6 | 7 | @solara.component 8 | def Page(): 9 | html = """ 10 |

Custom html

11 |
    12 |
  • Item 1 13 |
  • Item 2 14 |
15 | """ 16 | solara.HTML(tag="div", unsafe_innerHTML=html) 17 | 18 | 19 | __doc__ += apidoc(solara.HTML.f) # type: ignore 20 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/output/image.py: -------------------------------------------------------------------------------- 1 | """# Image""" 2 | 3 | import solara 4 | from solara.website.components import NoPage 5 | from solara.website.utils import apidoc 6 | 7 | Page = NoPage 8 | title = "Image" 9 | 10 | 11 | __doc__ += apidoc(solara.Image.f) # type: ignore 12 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/output/tooltip.py: -------------------------------------------------------------------------------- 1 | """# Tooltip""" 2 | 3 | import solara 4 | from solara.website.components import NoPage 5 | from solara.website.utils import apidoc 6 | 7 | Page = NoPage 8 | title = "Tooltip" 9 | 10 | 11 | __doc__ += apidoc(solara.Tooltip.f) # type: ignore 12 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/page/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/page/head.py: -------------------------------------------------------------------------------- 1 | """# Head""" 2 | 3 | import solara 4 | from solara.website.utils import apidoc 5 | 6 | 7 | @solara.component 8 | def Page(): 9 | solara.Info("A Head component does not render somesome visual on the page, but it is used to avoid duplicate tags, such as titles.") 10 | with solara.Head(): 11 | # title should always occur inside a Head component 12 | solara.Title("Custom title") 13 | 14 | 15 | __doc__ += apidoc(solara.Head.f) # type: ignore 16 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/page/title.py: -------------------------------------------------------------------------------- 1 | """# Title""" 2 | 3 | from typing import Optional, cast 4 | 5 | import solara 6 | from solara.website.utils import apidoc 7 | 8 | 9 | @solara.component 10 | def Page(): 11 | title = solara.use_reactive(cast(Optional[str], "Custom title!")) 12 | 13 | solara.ToggleButtonsSingle(value=title, values=[None, "Custom title!", "Different custom title"]) 14 | 15 | if title is not None: 16 | # if the title is not set in a child component, the parent's title will be used 17 | with solara.Head(): 18 | # title should always occur inside a Head component 19 | solara.Title(title) 20 | solara.Info(f"Your browser tab title should say {title}", classes=["mt-4"]) 21 | else: 22 | solara.Warning("If no title is set, the parent title is used.", classes=["mt-4"]) 23 | 24 | 25 | __doc__ += apidoc(solara.Title.f) # type: ignore 26 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/status/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/status/progress.py: -------------------------------------------------------------------------------- 1 | """# ProgressLinear""" 2 | 3 | import solara 4 | from solara.website.components import NoPage 5 | from solara.website.utils import apidoc 6 | 7 | Page = NoPage 8 | 9 | 10 | __doc__ += apidoc(solara.ProgressLinear.f) # type: ignore 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/status/spinner.py: -------------------------------------------------------------------------------- 1 | """ 2 | # SpinnerSolara 3 | """ 4 | 5 | import solara 6 | from solara.website.components import NoPage 7 | from solara.website.utils import apidoc 8 | 9 | Page = NoPage 10 | 11 | __doc__ += apidoc(solara.SpinnerSolara.f) # type: ignore 12 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/viz/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | from solara.website.components import NoPage, SubCategoryLayout 3 | 4 | Page = NoPage 5 | 6 | 7 | @solara.component 8 | def Layout(children=[]): 9 | SubCategoryLayout(children=children) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/viz/altair.py: -------------------------------------------------------------------------------- 1 | """# FigureAltair""" 2 | 3 | import altair as alt 4 | import pandas as pd 5 | 6 | import solara 7 | from solara.website.utils import apidoc 8 | 9 | title = "FigureAltair" 10 | 11 | df = pd.DataFrame({"a": ["A", "B", "C", "D", "E", "F", "G", "H", "I"], "b": [28, 55, 43, 91, 81, 53, 19, 87, 52]}) 12 | 13 | 14 | @solara.component 15 | def Page(): 16 | click_data, set_click_data = solara.use_state(None) 17 | hover_data, set_hover_data = solara.use_state(None) 18 | 19 | chart = alt.Chart(df).mark_bar().encode(x="a", y="b") 20 | 21 | with solara.Div() as main: 22 | solara.AltairChart(chart, on_click=set_click_data, on_hover=set_hover_data) 23 | 24 | solara.Markdown( 25 | f""" 26 | Click data: 27 | 28 | ``` 29 | {click_data} 30 | ``` 31 | 32 | Hover data: 33 | ``` 34 | {hover_data} 35 | ``` 36 | """ 37 | ) 38 | 39 | return main 40 | 41 | 42 | __doc__ += apidoc(solara.FigureAltair.f) # type: ignore 43 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/viz/matplotlib.py: -------------------------------------------------------------------------------- 1 | """# Matplotlib""" 2 | 3 | import numpy as np 4 | from matplotlib.figure import Figure 5 | 6 | import solara 7 | from solara.website.utils import apidoc 8 | 9 | x = np.linspace(0, 2, 100) 10 | 11 | 12 | @solara.component 13 | def Page(): 14 | freq, set_freq = solara.use_state(2.0) 15 | phase, set_phase = solara.use_state(0.1) 16 | y = np.sin(x * freq + phase) 17 | 18 | fig = Figure() 19 | ax = fig.subplots() 20 | ax.plot(x, y) 21 | ax.set_ylim(-1.2, 1.2) 22 | 23 | with solara.VBox() as main: 24 | solara.FloatSlider("Frequency", value=freq, on_value=set_freq, min=0, max=10) 25 | solara.FloatSlider("Phase", value=phase, on_value=set_phase, min=0, max=np.pi, step=0.1) 26 | solara.FigureMatplotlib(fig, dependencies=[freq, phase]) 27 | return main 28 | 29 | 30 | __doc__ += apidoc(solara.FigureMatplotlib.f) # type: ignore 31 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/components/viz/plotly_express.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Wraps plotly express function and adds cross filtering support. 4 | 5 | Instead of `plotly.express' you can use `solara.express` instead. 6 | 7 | ```python 8 | # import plotly.express as px 9 | import solara.express as px 10 | 11 | df = px.data.iris() 12 | px.histogram(df, "species") 13 | px.scatter(df, x="sepal_width", y="sepal_length", color="species") 14 | ``` 15 | 16 | 17 | Click the lasso icon in the top scatter plot to select points, which should then be filtered out in the other 18 | plots. 19 | 20 | 21 | """ 22 | 23 | import plotly.express as px 24 | 25 | import solara 26 | import solara.express as spx 27 | 28 | df = px.data.iris() 29 | 30 | 31 | @solara.component 32 | def Page(): 33 | solara.provide_cross_filter() 34 | fig = px.histogram(df, "species") 35 | fig.update_layout(dragmode="select", selectdirection="h") 36 | 37 | with solara.VBox() as main: 38 | spx.scatter(df, x="sepal_width", y="sepal_length", color="species") 39 | spx.scatter_3d(df, x="sepal_width", y="sepal_length", z="petal_width") 40 | spx.CrossFilteredFigurePlotly(fig) 41 | return main 42 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/ai/__init__.py: -------------------------------------------------------------------------------- 1 | """Large Language Models and Generative AI examples.""" 2 | 3 | import solara 4 | 5 | title = "AI" 6 | redirect = None 7 | 8 | 9 | @solara.component 10 | def Page(): 11 | return solara.Markdown("Click an example on the left") 12 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/basics/__init__.py: -------------------------------------------------------------------------------- 1 | """Demonstrates very basic usage of Solara.""" 2 | 3 | import solara 4 | 5 | redirect = None 6 | 7 | 8 | @solara.component 9 | def Page(): 10 | return solara.Markdown("Should not see me") 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/basics/sine.py: -------------------------------------------------------------------------------- 1 | """# Interactive sine wave 2 | 3 | 4 | This example shows how to have two slider control a visualization. 5 | 6 | """ 7 | 8 | import numpy as np 9 | import plotly.express as px 10 | 11 | import solara 12 | 13 | x = np.linspace(0, 2, 100) 14 | 15 | title = "Interactive sine wave" 16 | freq = solara.reactive(2.0) 17 | phase = solara.reactive(0.1) 18 | 19 | 20 | @solara.component 21 | def Page(): 22 | y = np.sin(x * freq.value + phase.value) 23 | 24 | solara.FloatSlider("Frequency", value=freq, min=0, max=10) 25 | solara.FloatSlider("Phase", value=phase, min=0, max=np.pi, step=0.1) 26 | 27 | fig = px.line(x=x, y=y) 28 | solara.FigurePlotly(fig) 29 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/fullscreen/__init__.py: -------------------------------------------------------------------------------- 1 | """Apps that run fullscreen""" 2 | 3 | import solara 4 | 5 | redirect = None 6 | 7 | 8 | @solara.component 9 | def Page(): 10 | return solara.Markdown("Should not see me") 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/fullscreen/authorization.py: -------------------------------------------------------------------------------- 1 | redirect = "/apps/authorization" 2 | 3 | Page = True 4 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/fullscreen/layout_demo.py: -------------------------------------------------------------------------------- 1 | redirect = "/apps/layout-demo" 2 | 3 | Page = True 4 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/fullscreen/multipage.py: -------------------------------------------------------------------------------- 1 | redirect = "/apps/multipage" 2 | 3 | Page = True 4 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/fullscreen/scatter.py: -------------------------------------------------------------------------------- 1 | redirect = "/apps/scatter" 2 | 3 | Page = True 4 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/fullscreen/scrolling.py: -------------------------------------------------------------------------------- 1 | redirect = "/apps/scrolling" 2 | 3 | Page = True 4 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/fullscreen/tutorial_streamlit.py: -------------------------------------------------------------------------------- 1 | redirect = "/apps/tutorial-streamlit" 2 | 3 | Page = True 4 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/general/__init__.py: -------------------------------------------------------------------------------- 1 | """""" 2 | 3 | import solara 4 | 5 | redirect = None 6 | 7 | 8 | @solara.component 9 | def Page(): 10 | return solara.Markdown("Should not see me") 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/general/live_update.py: -------------------------------------------------------------------------------- 1 | from typing import cast, Optional 2 | import httpx 3 | import asyncio 4 | import solara 5 | import solara.lab 6 | 7 | 8 | @solara.component 9 | def Page(): 10 | btc = solara.use_reactive(cast(Optional[float], None)) 11 | 12 | async def fetch_btc_price(): 13 | while True: 14 | await asyncio.sleep(1) 15 | async with httpx.AsyncClient() as client: 16 | url = "https://api.binance.com/api/v1/ticker/price?symbol=BTCUSDT" 17 | response = await client.get(url) 18 | btc.value = float(response.json()["price"]) 19 | print("btc.value", btc.value) 20 | 21 | fetch_result = solara.lab.use_task(fetch_btc_price, dependencies=[]) 22 | # the task keeps running, so is always in the pending mode, so we combine it with the btc value being None 23 | if fetch_result.pending and btc.value is None: 24 | solara.Text("Fetching BTC price...") 25 | else: 26 | if fetch_result.error: 27 | solara.Error(f"Error fetching BTC price: {fetch_result.exception}") 28 | else: 29 | solara.Text(f"BTC price: ${btc.value}") 30 | 31 | 32 | Page() 33 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/libraries/__init__.py: -------------------------------------------------------------------------------- 1 | """ipywidget library example""" 2 | 3 | import solara 4 | 5 | redirect = None 6 | 7 | 8 | @solara.component 9 | def Page(): 10 | return solara.Markdown("Should not see me") 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/utilities/__init__.py: -------------------------------------------------------------------------------- 1 | """Utility apps, slightly more complex.""" 2 | 3 | import solara 4 | 5 | redirect = None 6 | 7 | 8 | @solara.component 9 | def Page(): 10 | return solara.Markdown("Should not see me") 11 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/examples/visualization/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page(): 6 | return solara.Markdown("Click an example on the left") 7 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/faq/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import solara 4 | from solara.website.components.markdown import MarkdownWithMetadata 5 | 6 | title = "FAQ" 7 | HERE = Path(__file__) 8 | 9 | 10 | @solara.component 11 | def Page(route_external=None): 12 | MarkdownWithMetadata(Path(HERE.parent / "content" / "99-faq.md").read_text()) 13 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/getting_started/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from solara.autorouting import generate_routes_directory 4 | from solara.website.components.markdown import MarkdownWithMetadata 5 | 6 | HERE = Path(__file__) 7 | # if we didn't put the content in the subdirectory, but pointed to the current file 8 | # we would include the current file recursively, causing an infinite loop 9 | routes = generate_routes_directory(HERE.parent / "content", MarkdownWithMetadata) 10 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/getting_started/content/04-tutorials/10_data_science.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import solara 4 | import solara.components.applayout 5 | from solara.website.components.notebook import Notebook 6 | 7 | HERE = Path(__file__).parent 8 | 9 | 10 | @solara.component 11 | def Page(): 12 | # only execute once, other 13 | Notebook(HERE / "_data_science.ipynb") 14 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/getting_started/content/04-tutorials/SF_crime_sample.csv.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/website/pages/documentation/getting_started/content/04-tutorials/SF_crime_sample.csv.gz -------------------------------------------------------------------------------- /solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Fundamentals of Solara 3 | description: These articles dive deeper into the fundamental aspects of Soalra, like using components, hooks, and managing state. 4 | --- 5 | # Fundamentals 6 | 7 | If you want to dive deeper into Solara, this section is for you. We will cover the following topics: 8 | 9 | * [Components](/documentation/getting_started/fundamentals/components) 10 | * Hooks (soon) 11 | * [State management](/documentation/getting_started/fundamentals/state-management) 12 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview of how to deploy your Solara application or dashboard 3 | description: Solara apps can be easily either self hosted, or hosted on a variety of cloud platforms. 4 | --- 5 | # Deploying a solara app 6 | 7 | A Solara app can be [self hosted](/documentation/getting_started/deploying/self-hosted) or [cloud hosted](/documentation/getting_started/deploying/cloud-hosted). 8 | -------------------------------------------------------------------------------- /solara/website/pages/documentation/getting_started/content/80-what-is-lab.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: What is Solara.lab? 3 | description: Solara lab is a subpackage in solara containing Components, hooks and parts of Solara that are slightly experimental. 4 | --- 5 | # What is Solara lab? 6 | 7 | Solara lab is a subpackage in solara containing Components, hooks and parts of Solara that are slightly experimental. 8 | -------------------------------------------------------------------------------- /solara/website/pages/docutils.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | import solara 4 | from solara.alias import rw 5 | from solara.components import Markdown 6 | 7 | 8 | @solara.component 9 | def Sample(code, component): 10 | locals = globals().copy() 11 | exec(code, locals) 12 | c = locals[component] 13 | with rw.VBox() as main: 14 | Markdown( 15 | f""" 16 | ```python 17 | {code} 18 | ``` 19 | """ 20 | ) 21 | c() 22 | return main 23 | 24 | 25 | @solara.component 26 | def IncludeComponent(component, pre="", highlight=[], **kwargs): 27 | code = inspect.getsource(component.f) 28 | with rw.VBox(layout={"padding": "20px", "max_width": "1024px", "border": "1px #333 solid"}) as main: 29 | Markdown( 30 | f""" 31 | ```python 32 | {pre}{code} 33 | ``` 34 | """, 35 | highlight=highlight, 36 | ) 37 | component(**kwargs) 38 | return main 39 | -------------------------------------------------------------------------------- /solara/website/pages/roadmap/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from solara.website.components.markdown import MarkdownWithMetadata 4 | from solara.website.components.sidebar import Sidebar 5 | 6 | 7 | title = "Roadmap" 8 | HERE = Path(__file__) 9 | Sidebar = Sidebar 10 | 11 | Page = MarkdownWithMetadata(Path(HERE.parent / "roadmap.md").read_text()) 12 | -------------------------------------------------------------------------------- /solara/website/pages/showcase/planeto_tessa.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page(): 6 | with solara.Link("/showcase"): 7 | solara.Text("« Back to Showcases") 8 | with solara.ColumnsResponsive(12, medium=6): 9 | solara.Markdown( 10 | """ 11 | # TESSA by Planeto 12 | 13 | [Planeto](https://planeto-energy.ch/) developed a tool called [TESSA](https://planeto-energy.ch/solution/) for district heating & cooling planning. 14 | 15 | TESSA was prototyped in the Jupyter notebook using ipywidgets. Using solara, they are able to bring TESSA into production using the 16 | same technology stack. 17 | """ 18 | ) 19 | solara.Image("https://dxhl76zpt6fap.cloudfront.net/public/showcase/tessa/thumbnail.png", width="100%", classes=["pt-12"]) 20 | -------------------------------------------------------------------------------- /solara/website/pages/showcase/solarathon_2023_team_2.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page(): 6 | with solara.Link("/showcase"): 7 | solara.Text("« Back to Showcases") 8 | with solara.ColumnsResponsive(12, medium=6): 9 | with solara.Column(align="end", style={"height": "100%", "justify-content": "center"}): 10 | solara.Markdown( 11 | """ 12 | Team 2 built a travel assistant, which utilized various third party APIs to display rich information about the desired destination 13 | during the dates selected for a visit. The assistant also provided a list of recommended activities, and a map of the area. 14 | 15 | Take a look at the project on [Ploomber](https://jolly-moon-5966.ploomberapp.io/) 16 | """ 17 | ) 18 | with solara.v.Html(tag="video", attributes={"controls": "controls", "autoplay": "autoplay"}, style_="width:100%;"): 19 | solara.v.Html( 20 | tag="source", 21 | attributes={"src": "https://dxhl76zpt6fap.cloudfront.net/public/showcase/solarathon_2023_team_2/preview.webm", "type": "video/webm"}, 22 | ) 23 | -------------------------------------------------------------------------------- /solara/website/pages/showcase/solarathon_2023_team_4.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page(): 6 | with solara.Link("/showcase"): 7 | solara.Text("« Back to Showcases") 8 | with solara.ColumnsResponsive(12, medium=6): 9 | with solara.Column(align="end", style={"height": "100%", "justify-content": "center"}): 10 | solara.Markdown( 11 | """ 12 | Team 4 built a live cryptocurrency dashboard, which displayed the latest prices for various cryptocurrencies, and versatile 13 | analysis tools to help users make informed decisions. 14 | 15 | Note: The dashboard is no longer live on Ploomber, but you can see a preview of it in the video, or run it by cloning the GitHub repository. 16 | """ 17 | ) 18 | with solara.v.Html(tag="video", attributes={"controls": "controls", "autoplay": "autoplay"}, style_="width:100%;"): 19 | solara.v.Html( 20 | tag="source", 21 | attributes={"src": "https://dxhl76zpt6fap.cloudfront.net/public/showcase/solarathon_2023_team_4/preview.webm", "type": "video/webm"}, 22 | ) 23 | -------------------------------------------------------------------------------- /solara/website/public/beach.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/solara/website/public/beach.jpeg -------------------------------------------------------------------------------- /solara/website/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /solara/website/public/social/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /solara/website/public/social/github.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /solara/website/public/social/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /solara/website/public/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | Used to detect failed Jupyter and Solara installations. 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /solara/website/utils.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import textwrap 3 | 4 | 5 | def sig(f): 6 | lines = inspect.getsourcelines(f)[0] 7 | end = [k.endswith(":\n") for k in lines].index(True) 8 | lines = lines[: end + 1] 9 | return "".join(lines) 10 | 11 | 12 | def code(f): 13 | lines = inspect.getsourcelines(f)[0] 14 | def_end = [k.endswith(":\n") for k in lines].index(True) 15 | doc_lines = len(f.__doc__.split("\n")) 16 | lines = lines[: def_end + 1] + lines[def_end + 1 + doc_lines :] 17 | 18 | return "".join(lines) 19 | 20 | 21 | def apidoc(f, full=False): 22 | if not f.__doc__: 23 | return "no docstring" 24 | 25 | doclines = f.__doc__.split("\n") 26 | first = doclines[0].strip() 27 | rest = "\n".join(doclines[1:]) # .strip() 28 | if full: 29 | return f""" 30 | {first} 31 | 32 | ```python 33 | {code(f)} 34 | ... 35 | ``` 36 | 37 | 38 | {textwrap.dedent(rest)} 39 | """ 40 | else: 41 | return f""" 42 | {first} 43 | 44 | ```python 45 | {sig(f)} 46 | ... 47 | ``` 48 | 49 | 50 | {textwrap.dedent(rest)} 51 | """ 52 | -------------------------------------------------------------------------------- /solara/widgets/__init__.py: -------------------------------------------------------------------------------- 1 | from .widgets import * # noqa: F401, F403 2 | -------------------------------------------------------------------------------- /solara/widgets/vue/html.vue: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import sys 3 | 4 | import pytest 5 | from dotenv import load_dotenv 6 | 7 | import solara.server.reload 8 | import solara.server.settings 9 | 10 | load_dotenv() # take environment variables from .env. should be in os.environ for the whole test 11 | 12 | solara.server.settings.telemetry.mixpanel_token = "adbf863d17cba80db608788e7fce9843" 13 | solara.server.settings.main.mode = "development" 14 | solara.server.reload.reloader.watcher = solara.server.reload.WatcherType([], solara.server.reload.reloader._on_change) 15 | 16 | 17 | @pytest.fixture 18 | def extra_include_path(): 19 | @contextlib.contextmanager 20 | def extra_include_path(path): 21 | sys.path.insert(0, str(path)) 22 | try: 23 | yield 24 | finally: 25 | sys.path[:] = sys.path[1:] 26 | 27 | return extra_include_path 28 | -------------------------------------------------------------------------------- /tests/docs/docs_howto_no_browser_testing_test.py: -------------------------------------------------------------------------------- 1 | import solara 2 | import ipyvuetify as v 3 | 4 | 5 | def test_docs_no_browser_simple(): 6 | clicks = solara.reactive(0) 7 | 8 | @solara.component 9 | def ClickButton(): 10 | def increment(): 11 | clicks.value += 1 12 | 13 | solara.Button(label=f"Clicked: {clicks}", on_click=increment) 14 | 15 | # rc is short for render context 16 | box, rc = solara.render(ClickButton(), handle_error=False) 17 | button = box.children[0] 18 | assert isinstance(button, v.Btn) 19 | assert button.children[0] == "Clicked: 0" 20 | # trigger the click event handler without a browser 21 | button.click() 22 | assert clicks.value == 1 23 | assert button.children[0] == "Clicked: 1" 24 | -------------------------------------------------------------------------------- /tests/docs/docs_howto_no_browser_threaded_test.py: -------------------------------------------------------------------------------- 1 | import solara 2 | import solara.lab 3 | import ipyvuetify as v 4 | import time 5 | 6 | 7 | def test_docs_no_browser_api_thread(): 8 | clicks = solara.reactive(0) 9 | 10 | @solara.component 11 | def ClickButton(): 12 | @solara.lab.task 13 | def increment(): 14 | # now we will wait for 0.3 seconds before updating the UI 15 | time.sleep(0.3) 16 | clicks.value += 1 17 | 18 | with solara.Card("Button in a card"): 19 | with solara.Column(): 20 | solara.Button(label=f"Clicked: {clicks}", on_click=increment) 21 | 22 | # rc is short for render context 23 | box, rc = solara.render(ClickButton(), handle_error=False) 24 | finder = rc.find(v.Btn) 25 | button = finder.widget 26 | finder.assert_single() 27 | finder.assert_not_empty() 28 | assert button.children[0] == "Clicked: 0" 29 | 30 | # clicking will now start a thread, so we have to wait/poll for the UI to update 31 | button.click() 32 | 33 | button_after_delayed_click = rc.find(v.Btn, children=["Clicked: 1"]) 34 | button_after_delayed_click.wait_for(timeout=2.5) 35 | -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/integration/__init__.py -------------------------------------------------------------------------------- /tests/integration/app_widget.py: -------------------------------------------------------------------------------- 1 | import ipywidgets as widgets 2 | 3 | clicks = 0 4 | 5 | 6 | def on_click(button): 7 | global clicks 8 | clicks += 1 9 | button.description = f"Clicked {clicks} times" 10 | 11 | 12 | button = widgets.Button(description="Clicked 0 times") 13 | button.on_click(on_click) 14 | -------------------------------------------------------------------------------- /tests/integration/apps/not-allowed: -------------------------------------------------------------------------------- 1 | not accessible 2 | -------------------------------------------------------------------------------- /tests/integration/apps/public/test.txt: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /tests/integration/apps/secure/app.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | page = solara.Button("Click me") 5 | -------------------------------------------------------------------------------- /tests/integration/assets/assets1/common.js: -------------------------------------------------------------------------------- 1 | content1 2 | -------------------------------------------------------------------------------- /tests/integration/assets/assets1/unique1.js: -------------------------------------------------------------------------------- 1 | unique1 2 | -------------------------------------------------------------------------------- /tests/integration/assets/assets2/common.js: -------------------------------------------------------------------------------- 1 | content2 2 | -------------------------------------------------------------------------------- /tests/integration/assets/assets2/custom.css: -------------------------------------------------------------------------------- 1 | .v-btn { 2 | color: #ff0000 !important; 3 | } 4 | -------------------------------------------------------------------------------- /tests/integration/assets/assets2/custom.js: -------------------------------------------------------------------------------- 1 | var a = 1; 2 | -------------------------------------------------------------------------------- /tests/integration/assets/assets2/unique2.js: -------------------------------------------------------------------------------- 1 | unique2 2 | -------------------------------------------------------------------------------- /tests/integration/async_test.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from pathlib import Path 3 | 4 | import playwright.sync_api 5 | 6 | import solara 7 | import solara.server.starlette 8 | 9 | HERE = Path(__file__).parent 10 | 11 | 12 | @solara.component 13 | def Page(): 14 | def run_async(): 15 | async def some_task(): 16 | await asyncio.sleep(0.01) 17 | label.value = "asyncio run" 18 | 19 | asyncio.create_task(some_task()) 20 | 21 | label = solara.use_reactive("initial") 22 | solara.Button(label.value, on_click=run_async) 23 | 24 | 25 | def test_async_callback(page_session: playwright.sync_api.Page, solara_app, extra_include_path, solara_server): 26 | with extra_include_path(HERE), solara_app("async_test"): 27 | page_session.goto(solara_server.base_url + "/") 28 | page_session.locator("text=initial").click() 29 | page_session.locator("text=asyncio run").wait_for() 30 | -------------------------------------------------------------------------------- /tests/integration/enterprise/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/integration/enterprise/__init__.py -------------------------------------------------------------------------------- /tests/integration/error_test.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import playwright 4 | import playwright.sync_api 5 | 6 | app_path = Path(__file__).parent / "testapp.py" 7 | 8 | 9 | def test_error_in_render(page_session: playwright.sync_api.Page, solara_server, solara_app, extra_include_path): 10 | with extra_include_path(app_path.parent), solara_app("testapp:clickboom"): 11 | page_session.goto(solara_server.base_url) 12 | page_session.locator("text=Boom").click() 13 | page_session.locator("text=I crash on 1").wait_for() 14 | -------------------------------------------------------------------------------- /tests/integration/file_download_test.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pandas as pd 4 | import playwright 5 | import playwright.sync_api 6 | 7 | 8 | def test_download(browser: playwright.sync_api.Browser, page_session: playwright.sync_api.Page, solara_server, solara_app, tmpdir: Path): 9 | with solara_app("solara.website.pages"): 10 | page_session.goto(solara_server.base_url + "/documentation/components/output/file_download") 11 | with page_session.expect_download() as download_info: 12 | page_session.locator('button:has-text("Download file")').click() 13 | target = tmpdir / "downloaded.txt" 14 | download = download_info.value 15 | download.save_as(target) 16 | assert target.read_text(encoding="utf8") == "This is the content of the file" 17 | 18 | with page_session.expect_download() as download_info: 19 | page_session.locator('button:has-text("Download: users.csv")').click() 20 | target = tmpdir / "downloaded.csv" 21 | download = download_info.value 22 | download.save_as(target) 23 | df = pd.read_csv(target) 24 | assert df.to_dict() == {"id": {0: 1, 1: 2, 2: 3}, "name": {0: "John", 1: "Mary", 2: "Bob"}} 25 | -------------------------------------------------------------------------------- /tests/integration/reload/apps.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | 3 | import solara 4 | from solara.alias import rw 5 | 6 | 7 | @dataclasses.dataclass 8 | class Clicks: 9 | value: int 10 | 11 | 12 | @solara.component 13 | def ButtonClick(): 14 | clicks, set_clicks = solara.use_state(Clicks(0)) 15 | return rw.Button(description=f"Clicked {clicks.value} times", on_click=lambda: set_clicks(Clicks(clicks.value + 1))) 16 | -------------------------------------------------------------------------------- /tests/integration/test.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /tests/integration/testapp.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | import os 3 | 4 | import ipyvue 5 | import traitlets 6 | 7 | import solara 8 | from solara.alias import rw 9 | 10 | 11 | @dataclasses.dataclass 12 | class Clicks: 13 | value: int 14 | 15 | 16 | @solara.component 17 | def ButtonClick(): 18 | clicks, set_clicks = solara.use_state(Clicks(0)) 19 | return rw.Button(description=f"Clicked {clicks.value} times", on_click=lambda: set_clicks(Clicks(clicks.value + 1))) 20 | 21 | 22 | app = ButtonClick() 23 | 24 | 25 | @solara.component 26 | def ClickBoom(): 27 | count, set_count = solara.use_state(0) 28 | if count == 1: 29 | raise ValueError("I crash on 1") 30 | return solara.Button("Boom", on_click=lambda: set_count(count + 1)) 31 | 32 | 33 | clickboom = ClickBoom() 34 | 35 | 36 | class TestWidget(ipyvue.VueTemplate): 37 | template_file = os.path.realpath(os.path.join(os.path.dirname(__file__), "test.vue")) 38 | 39 | value = traitlets.Any(0).tag(sync=True) 40 | 41 | 42 | @solara.component 43 | def VueTestApp(): 44 | return TestWidget.element(value="foobar") 45 | 46 | 47 | vue_test_app = VueTestApp() 48 | -------------------------------------------------------------------------------- /tests/pyinstaller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/pyinstaller/__init__.py -------------------------------------------------------------------------------- /tests/pyinstaller/pyinstaller_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import playwright.sync_api 3 | 4 | TEST_PORT = int(os.environ.get("PORT", "18765")) 5 | 6 | 7 | def test_pyinstaller_basics(page: playwright.sync_api.Page): 8 | page.goto(f"http://localhost:{TEST_PORT}") 9 | page.locator("text=Clicked: 0").click() 10 | page.locator("text=Clicked: 1").wait_for() 11 | -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-linux-ipywidgets-7-changed-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-linux-ipywidgets-7-changed-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-linux-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-linux-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-linux-ipywidgets-8-changed-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-linux-ipywidgets-8-changed-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-linux-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-linux-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-win32-ipywidgets-7-changed-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-win32-ipywidgets-7-changed-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-win32-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-win32-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-win32-ipywidgets-8-changed-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-win32-ipywidgets-8-changed-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-win32-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/latex_test.py/test_widget_latex_solara-flask-chromium-win32-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_lab-chromium-linux-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_lab-chromium-linux-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_lab-chromium-linux-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_lab-chromium-linux-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_lab-chromium-win32-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_lab-chromium-win32-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_lab-chromium-win32-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_lab-chromium-win32-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_notebook-chromium-linux-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_notebook-chromium-linux-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_notebook-chromium-linux-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_notebook-chromium-linux-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_notebook-chromium-win32-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_notebook-chromium-win32-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_notebook-chromium-win32-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-jupyter_notebook-chromium-win32-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-solara-chromium-linux-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-solara-chromium-linux-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-solara-chromium-linux-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-solara-chromium-linux-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-solara-chromium-win32-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-solara-chromium-win32-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-solara-chromium-win32-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-solara-chromium-win32-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-voila-chromium-linux-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-voila-chromium-linux-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-voila-chromium-linux-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-voila-chromium-linux-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-voila-chromium-win32-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-voila-chromium-win32-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-voila-chromium-win32-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-flask-voila-chromium-win32-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-starlette-solara-chromium-linux-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-starlette-solara-chromium-linux-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-starlette-solara-chromium-linux-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-starlette-solara-chromium-linux-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-starlette-solara-chromium-win32-ipywidgets-7-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-starlette-solara-chromium-win32-ipywidgets-7-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-starlette-solara-chromium-win32-ipywidgets-8-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_slider_all-starlette-solara-chromium-win32-ipywidgets-8-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-jupyter_lab-chromium-linux-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-jupyter_lab-chromium-linux-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-jupyter_lab-chromium-win32-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-jupyter_lab-chromium-win32-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-jupyter_notebook-chromium-linux-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-jupyter_notebook-chromium-linux-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-jupyter_notebook-chromium-win32-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-jupyter_notebook-chromium-win32-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-solara-chromium-linux-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-solara-chromium-linux-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-solara-chromium-win32-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-solara-chromium-win32-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-voila-chromium-linux-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-voila-chromium-linux-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-voila-chromium-win32-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-flask-voila-chromium-win32-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-starlette-solara-chromium-linux-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-starlette-solara-chromium-linux-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-starlette-solara-chromium-win32-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_solara_button_all-starlette-solara-chromium-win32-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-flask-chromium-linux-ipyvuetify3-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-flask-chromium-linux-ipyvuetify3-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-flask-chromium-linux-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-flask-chromium-linux-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-flask-chromium-win32-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-flask-chromium-win32-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-starlette-chromium-linux-ipyvuetify3-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-starlette-chromium-linux-ipyvuetify3-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-starlette-chromium-linux-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-starlette-chromium-linux-reference.png -------------------------------------------------------------------------------- /tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-starlette-chromium-win32-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/ui/snapshots/tests/integration/widget_test.py/test_widget_button_solara-starlette-chromium-win32-reference.png -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/chat_test.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import MagicMock 2 | 3 | import ipyvuetify as vw 4 | 5 | import solara 6 | 7 | 8 | def test_chatinput(): 9 | send_callback = MagicMock() 10 | el = solara.lab.ChatInput(send_callback=send_callback) 11 | box, rc = solara.render(el, handle_error=False) 12 | input = rc.find(vw.TextField) 13 | button = rc.find(vw.Btn) 14 | 15 | input.widget.v_model = "hello" 16 | assert send_callback.call_count == 0 17 | input.widget.fire_event("keyup.enter") 18 | assert send_callback.call_count == 1 19 | assert send_callback.call_args[0][0] == "hello" 20 | assert input.widget.v_model == "" 21 | input.widget.v_model = "hello" 22 | assert send_callback.call_count == 1 23 | button.widget.fire_event("click") 24 | assert send_callback.call_count == 2 25 | assert send_callback.call_args[0][0] == "hello" 26 | assert input.widget.v_model == "" 27 | -------------------------------------------------------------------------------- /tests/unit/checkbox_test.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import MagicMock 2 | 3 | import ipyvuetify as vw 4 | 5 | import solara 6 | 7 | 8 | def test_checkbox(): 9 | on_value = MagicMock() 10 | el = solara.Checkbox(label="label", value=True, on_value=on_value) 11 | box, rc = solara.render(el, handle_error=False) 12 | checkbox = rc.find(vw.Checkbox) 13 | checkbox.widget.v_model = False 14 | assert on_value.call_count == 1 15 | checkbox.widget.v_model = False 16 | assert on_value.call_count == 1 17 | rc.close() 18 | 19 | 20 | def test_checkbox_no_callback_on_managed(): 21 | on_value = MagicMock() 22 | el = solara.Checkbox(label="label", value=True, on_value=on_value) 23 | box, rc = solara.render(el, handle_error=False) 24 | assert on_value.call_count == 0 25 | 26 | # changing the value externally should *not* call the callback 27 | el2 = solara.Checkbox(label="label", value=False, on_value=on_value) 28 | rc.render(el2) 29 | assert on_value.call_count == 0 30 | -------------------------------------------------------------------------------- /tests/unit/component_vue_test.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/unit/component_vue_test.vue -------------------------------------------------------------------------------- /tests/unit/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import solara.server.app 4 | import solara.server.kernel_context 5 | from solara.server import kernel 6 | from solara.server.kernel_context import VirtualKernelContext 7 | 8 | 9 | @pytest.fixture(autouse=True) 10 | def kernel_context(): 11 | kernel_shared = kernel.Kernel() 12 | context = VirtualKernelContext(id="1", kernel=kernel_shared, session_id="session-1") 13 | try: 14 | with context: 15 | yield context 16 | finally: 17 | with context: 18 | context.close() 19 | 20 | 21 | @pytest.fixture() 22 | def no_kernel_context(kernel_context): 23 | context = solara.server.kernel_context.get_current_context() 24 | solara.server.kernel_context.set_current_context(None) 25 | try: 26 | yield 27 | finally: 28 | solara.server.kernel_context.set_current_context(context) 29 | -------------------------------------------------------------------------------- /tests/unit/express_test.py: -------------------------------------------------------------------------------- 1 | import plotly.express as px 2 | 3 | import solara 4 | import solara.express 5 | 6 | df = px.data.iris() 7 | 8 | 9 | def test_cross_filter(): 10 | filter, set_filter = None, None 11 | 12 | @solara.component 13 | def Test(): 14 | nonlocal filter, set_filter 15 | solara.provide_cross_filter() 16 | filter, set_filter = solara.use_cross_filter(id(df)) 17 | 18 | with solara.HBox() as main: 19 | solara.express.scatter(df, x="sepal_length", y="sepal_width") 20 | solara.express.scatter(df, x="sepal_length", y="sepal_width", size=[10 for ea in df.sepal_length], log_x=True) 21 | return main 22 | 23 | box, rc = solara.render(Test(), handle_error=False) 24 | assert set_filter is not None 25 | set_filter(df["sepal_length"] > 5) 26 | -------------------------------------------------------------------------------- /tests/unit/lab/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/unit/lab/__init__.py -------------------------------------------------------------------------------- /tests/unit/no_solara_test.py: -------------------------------------------------------------------------------- 1 | import ipywidgets as widgets 2 | from IPython.display import display 3 | 4 | import solara.components.file_download 5 | 6 | # test if normal widget code works with no app context 7 | 8 | 9 | def test_create_widget(no_kernel_context): 10 | button = widgets.Button(description="Click me") 11 | button.layout.close() 12 | button.close() 13 | 14 | 15 | def test_vue_template(no_kernel_context): 16 | widget = solara.components.file_download.FileDownloadWidget() 17 | widget.close() 18 | 19 | 20 | def test_display(no_kernel_context): 21 | display("test") 22 | -------------------------------------------------------------------------------- /tests/unit/shell_test.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import Mock 2 | 3 | import IPython.display 4 | 5 | from solara.server import kernel, kernel_context 6 | 7 | 8 | def test_shell(no_kernel_context): 9 | ws1 = Mock() 10 | ws2 = Mock() 11 | kernel1 = kernel.Kernel() 12 | kernel2 = kernel.Kernel() 13 | kernel1.session.websockets.add(ws1) 14 | kernel2.session.websockets.add(ws2) 15 | context1 = kernel_context.VirtualKernelContext(id="1", kernel=kernel1, session_id="session-1") 16 | context2 = kernel_context.VirtualKernelContext(id="2", kernel=kernel2, session_id="session-2") 17 | 18 | with context1: 19 | IPython.display.display("test1") 20 | assert ws1.send.call_count == 1 21 | assert ws2.send.call_count == 0 22 | with context2: 23 | IPython.display.display("test1") 24 | assert ws1.send.call_count == 1 25 | assert ws2.send.call_count == 1 26 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/unit/solara_test_apps/__init__.py -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/food/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/unit/solara_test_apps/food/__init__.py -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/food/food.py: -------------------------------------------------------------------------------- 1 | title = "Foods" 2 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/food/index.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | title = "Fruit home" 4 | 5 | 6 | @solara.component 7 | def Page(): 8 | return solara.Success("Yay") 9 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/kernel_start.py: -------------------------------------------------------------------------------- 1 | import solara 2 | import solara.lab 3 | 4 | 5 | def test_callback(): 6 | pass 7 | 8 | 9 | solara.lab.on_kernel_start(test_callback) 10 | 11 | 12 | @solara.component 13 | def Page(): 14 | solara.Text("Hello, World!") 15 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage-widgets/00-overview.md: -------------------------------------------------------------------------------- 1 | # Multi page app using regular ipywidget 2 | 3 | * [Record views (widget based)](/views) 4 | * [Record likes (widget based)](/likes) 5 | * [Volume (uses component)](/volume) 6 | * [Color (uses widget in notebook)](/color) 7 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage-widgets/01-views.py: -------------------------------------------------------------------------------- 1 | import ipywidgets as widgets 2 | 3 | clicks = 0 4 | 5 | print("View button: I get run at startup, and for every page request") # noqa 6 | 7 | 8 | def on_click(button): 9 | global clicks 10 | clicks += 1 11 | button.description = f"Viewed {clicks} times" 12 | 13 | 14 | button = widgets.Button(description="Never viewed") 15 | button.on_click(on_click) 16 | 17 | page = button 18 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage-widgets/02-likes.py: -------------------------------------------------------------------------------- 1 | import ipywidgets as widgets 2 | 3 | clicks = 0 4 | 5 | print("Like button: I get run at startup, and for every page request") # noqa 6 | 7 | 8 | def on_click(button): 9 | global clicks 10 | clicks += 1 11 | button.description = f"Liked {clicks} times" 12 | 13 | 14 | button = widgets.Button(description="No likes recorded") 15 | button.on_click(on_click) 16 | 17 | page = button 18 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage-widgets/03-volume.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page(): 6 | volume, set_volume = solara.use_state(5) 7 | with solara.VBox() as main: 8 | solara.Markdown("# Choose your volume") 9 | solara.IntSlider("Volume", value=volume, on_value=set_volume, min=0, max=11) 10 | if volume == 11: 11 | solara.Markdown(f"Yeah! {volume} is the best volume!") 12 | return main 13 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/01-home.py: -------------------------------------------------------------------------------- 1 | import solara.website.pages.documentation.components.input.button as mod 2 | 3 | Page = mod.Page 4 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/02-my_fruit.py: -------------------------------------------------------------------------------- 1 | import solara.website.pages.documentation.api.routing.use_route as mod 2 | 3 | routes = mod.routes 4 | Page = mod.Page 5 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/03-some-markdown.md: -------------------------------------------------------------------------------- 1 | # This title is not used 2 | 3 | We use the filename for the title. 4 | 5 | ```python 6 | this = "renders to highlighted Python code 7 | ``` 8 | 9 | 10 | ## Embed a solara example 11 | 12 | This renders highlighted Python code, and shows app. 13 | ```solara 14 | import solara 15 | 16 | 17 | @solara.component 18 | def ClickButton(): 19 | clicks, set_clicks = solara.use_state(0) 20 | 21 | color = "green" 22 | if clicks >= 5: 23 | color = "red" 24 | 25 | def on_click(): 26 | set_clicks(clicks + 1) 27 | print("clicks", clicks) 28 | 29 | return solara.Button(label=f"Clicked: {clicks}", on_click=on_click, color=color) 30 | 31 | 32 | app = ClickButton() 33 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/04-a_directory/00-another-markdown.md: -------------------------------------------------------------------------------- 1 | # Just a title 2 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/04-a_directory/01-not-an-app.py: -------------------------------------------------------------------------------- 1 | import solara.website.pages.documentation.components.layout.griddraggable as mod 2 | 3 | Page = mod.Page 4 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/04-a_directory/__init__.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Layout(children=[]): 6 | with solara.VBox(children=children) as main: 7 | solara.Info("Footer") 8 | return main 9 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/05-and-notebooks.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import reacton.ipyvuetify as v\n", 10 | "import solara\n", 11 | "\n", 12 | "\n", 13 | "@solara.component\n", 14 | "def Page():\n", 15 | " return v.Slider(label=\"Language\")" 16 | ] 17 | } 18 | ], 19 | "metadata": { 20 | "language_info": { 21 | "name": "python" 22 | }, 23 | "orig_nbformat": 4 24 | }, 25 | "nbformat": 4, 26 | "nbformat_minor": 2 27 | } 28 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/06-custom-routes.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page1(): 6 | solara.Button("hi1") 7 | 8 | 9 | @solara.component 10 | def Page2(): 11 | solara.Button("hi2") 12 | 13 | 14 | routes = [ 15 | solara.Route(path="/", component=Page1, label="Hi1"), 16 | solara.Route(path="page2", component=Page2, label="Hi2"), 17 | ] 18 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/10-single-file-directory/single-file.md: -------------------------------------------------------------------------------- 1 | # single file 2 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/99-some_other_python_script.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/widgetti/solara/2ca42f8840bc40a415b8318a38e0105b4e026079/tests/unit/solara_test_apps/multipage/99-some_other_python_script.py -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/multipage/some_other_file.txt: -------------------------------------------------------------------------------- 1 | we should do nothing with this 2 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/notebookapp_component.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import solara\n", 10 | "\n", 11 | "\n", 12 | "@solara.component\n", 13 | "def Page():\n", 14 | " return solara.Button(\"Click me\")" 15 | ] 16 | } 17 | ], 18 | "metadata": { 19 | "kernelspec": { 20 | "display_name": "dev", 21 | "language": "python", 22 | "name": "python3" 23 | }, 24 | "language_info": { 25 | "name": "python", 26 | "version": "3.9.10 | packaged by conda-forge | (main, Feb 1 2022, 21:27:43) \n[Clang 11.1.0 ]" 27 | }, 28 | "orig_nbformat": 4, 29 | "vscode": { 30 | "interpreter": { 31 | "hash": "3f54047370d637df4a365f9bae65e296d7b1c0737aca7baed81d825616d991e7" 32 | } 33 | } 34 | }, 35 | "nbformat": 4, 36 | "nbformat_minor": 2 37 | } 38 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/notebookapp_element.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import solara\n", 10 | "\n", 11 | "app = solara.Button(\"Click me\")" 12 | ] 13 | } 14 | ], 15 | "metadata": { 16 | "kernelspec": { 17 | "display_name": "dev", 18 | "language": "python", 19 | "name": "python3" 20 | }, 21 | "language_info": { 22 | "name": "python", 23 | "version": "3.9.10 | packaged by conda-forge | (main, Feb 1 2022, 21:27:43) \n[Clang 11.1.0 ]" 24 | }, 25 | "orig_nbformat": 4, 26 | "vscode": { 27 | "interpreter": { 28 | "hash": "3f54047370d637df4a365f9bae65e296d7b1c0737aca7baed81d825616d991e7" 29 | } 30 | } 31 | }, 32 | "nbformat": 4, 33 | "nbformat_minor": 2 34 | } 35 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/notebookapp_widget.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import ipywidgets\n", 10 | "\n", 11 | "app = ipywidgets.Button(description=\"Click me\")" 12 | ] 13 | } 14 | ], 15 | "metadata": { 16 | "kernelspec": { 17 | "display_name": "dev", 18 | "language": "python", 19 | "name": "python3" 20 | }, 21 | "language_info": { 22 | "name": "python", 23 | "version": "3.9.10 | packaged by conda-forge | (main, Feb 1 2022, 21:27:43) \n[Clang 11.1.0 ]" 24 | }, 25 | "orig_nbformat": 4, 26 | "vscode": { 27 | "interpreter": { 28 | "hash": "3f54047370d637df4a365f9bae65e296d7b1c0737aca7baed81d825616d991e7" 29 | } 30 | } 31 | }, 32 | "nbformat": 4, 33 | "nbformat_minor": 2 34 | } 35 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/single_file.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page(): 6 | with solara.Sidebar(): 7 | solara.SliderInt(label="in sidebar") 8 | solara.Markdown("Home") 9 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/single_file_multiple_routes.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Home(): 6 | with solara.Sidebar(): 7 | solara.SliderInt(label="in sidebar") 8 | solara.Markdown("Home") 9 | 10 | 11 | @solara.component 12 | def About(): 13 | solara.Markdown("About") 14 | 15 | 16 | routes = [ 17 | solara.Route(path="/", component=Home, label="Home"), 18 | solara.Route(path="about", component=About, label="About"), 19 | ] 20 | -------------------------------------------------------------------------------- /tests/unit/solara_test_apps/single_file_routes.py: -------------------------------------------------------------------------------- 1 | import solara 2 | 3 | 4 | @solara.component 5 | def Page1(): 6 | solara.Text("Page 1") 7 | 8 | 9 | @solara.component 10 | def Page2(): 11 | solara.Text("Page 2") 12 | 13 | 14 | @solara.component 15 | def Layout(children): 16 | with solara.AppLayout(): 17 | with solara.Column(): 18 | solara.Text("Custom layout") 19 | solara.display(*children) 20 | 21 | 22 | routes = [ 23 | solara.Route("/", component=Page1, layout=Layout), 24 | solara.Route("page2", component=Page2), 25 | ] 26 | -------------------------------------------------------------------------------- /tests/unit/switch_test.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import MagicMock 2 | 3 | import ipyvuetify as vw 4 | 5 | import solara 6 | 7 | 8 | def test_switch(): 9 | on_value = MagicMock() 10 | el = solara.Switch(label="label", value=True, on_value=on_value) 11 | _, rc = solara.render(el, handle_error=False) 12 | switch = rc.find(vw.Switch) 13 | switch.widget.v_model = False 14 | assert on_value.call_count == 1 15 | switch.widget.v_model = False 16 | assert on_value.call_count == 1 17 | switch.widget.v_model = True 18 | assert on_value.call_count == 2 19 | rc.close() 20 | -------------------------------------------------------------------------------- /tests/unit/telemetry_tests.py: -------------------------------------------------------------------------------- 1 | import json 2 | import unittest.mock 3 | from urllib.parse import unquote 4 | 5 | import requests 6 | 7 | import solara.server.telemetry 8 | 9 | 10 | def test_telemetry_basic(mocker): 11 | post: unittest.mock.MagicMock = mocker.spy(requests, "post") 12 | solara.server.telemetry.track("test_event", {"test_prop": "test_value"}) 13 | post.assert_called_once() 14 | data = json.loads(unquote(post.call_args[1]["data"])[5:])[0] # type: ignore 15 | assert data["event"] == "test_event" 16 | 17 | 18 | def test_telemetry_server_start_stopc(mocker): 19 | post: unittest.mock.MagicMock = mocker.spy(requests, "post") 20 | solara.server.telemetry.server_start() 21 | solara.server.telemetry.server_stop() 22 | post.assert_called() 23 | data = json.loads(unquote(post.call_args[1]["data"])[5:])[0] # type: ignore 24 | assert data["event"] == "Solara server stop" 25 | assert data["properties"]["duration_seconds"] > 0 26 | -------------------------------------------------------------------------------- /tests/unit/toestand_computed_reload.py: -------------------------------------------------------------------------------- 1 | import solara 2 | import solara.lab 3 | 4 | value_reactive = solara.reactive(1.0) 5 | 6 | 7 | @solara.lab.computed 8 | def computed_reactive(): 9 | return value_reactive.value + 1.0 10 | 11 | 12 | @solara.component 13 | def Page(): 14 | solara.FloatSlider("test", value=value_reactive) 15 | solara.InputFloat("test", value=computed_reactive.value) 16 | -------------------------------------------------------------------------------- /tsconfigbase.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "composite": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "incremental": true, 8 | "jsx": "react", 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "noEmitOnError": true, 12 | "noImplicitAny": true, 13 | "noUnusedLocals": true, 14 | "preserveWatchOutput": true, 15 | "resolveJsonModule": true, 16 | "strict": true, 17 | "skipLibCheck": true, 18 | "strictNullChecks": true, 19 | "target": "es2017", 20 | "types": [] 21 | } 22 | } 23 | --------------------------------------------------------------------------------