solara 1.29.0__py2.py3-none-any.whl → 1.30.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solara/__init__.py +5 -5
- solara/__main__.py +6 -2
- solara/autorouting.py +79 -38
- solara/cache.py +2 -2
- solara/checks.html +1 -1
- solara/components/__init__.py +1 -1
- solara/components/applayout.py +5 -5
- solara/components/button.py +4 -4
- solara/components/card.py +1 -2
- solara/components/component_vue.py +1 -1
- solara/components/cross_filter.py +6 -7
- solara/components/datatable.py +2 -3
- solara/components/figure_altair.py +1 -1
- solara/components/file_download.py +2 -2
- solara/components/file_drop.py +76 -33
- solara/components/file_drop.vue +24 -10
- solara/components/head.py +1 -2
- solara/components/head_tag.py +2 -3
- solara/components/link.py +3 -4
- solara/components/meta.py +1 -1
- solara/components/misc.py +5 -9
- solara/datatypes.py +2 -2
- solara/hooks/use_reactive.py +2 -2
- solara/lab/components/confirmation_dialog.py +1 -1
- solara/lab/components/tabs.py +6 -6
- solara/reactive.py +1 -1
- solara/routing.py +9 -8
- solara/server/app.py +44 -1
- solara/server/assets/style.css +6 -0
- solara/server/esm.py +28 -4
- solara/server/kernel_context.py +75 -7
- solara/server/patch.py +3 -0
- solara/server/reload.py +2 -2
- solara/server/server.py +3 -3
- solara/server/settings.py +1 -0
- solara/server/starlette.py +66 -33
- solara/server/static/solara_bootstrap.py +1 -1
- solara/server/templates/solara.html.j2 +62 -42
- solara/tasks.py +1 -6
- solara/util.py +23 -1
- solara/website/assets/custom.css +56 -0
- solara/website/components/__init__.py +1 -0
- solara/website/components/algolia_api.vue +157 -0
- solara/website/components/docs.py +118 -0
- solara/website/components/header.py +20 -10
- solara/website/components/hero.py +1 -1
- solara/website/pages/__init__.py +223 -20
- solara/website/pages/apps/jupyter-dashboard-1.py +1 -1
- solara/website/pages/apps/multipage/__init__.py +1 -1
- solara/website/pages/apps/multipage/page2.py +1 -1
- solara/website/pages/apps/scatter.py +21 -7
- solara/website/pages/changelog/__init__.py +8 -0
- solara/website/pages/{docs/content/95-changelog.md → changelog/changelog.md} +28 -2
- solara/website/pages/contact/__init__.py +8 -0
- solara/website/pages/documentation/__init__.py +184 -0
- solara/website/pages/documentation/advanced/__init__.py +36 -0
- solara/website/pages/documentation/advanced/content/00-overview.md +1 -0
- solara/website/pages/{docs/content/10-howto/20-multipage.md → documentation/advanced/content/10-howto/10-multipage.md} +4 -5
- solara/website/pages/{docs/content/10-howto/30-layout.md → documentation/advanced/content/10-howto/20-layout.md} +21 -21
- solara/website/pages/{docs/content/10-howto/80-embed.md → documentation/advanced/content/10-howto/40-embed.md} +1 -1
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/06-ipyvuetify.md +1 -1
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/12-reacton-basics.md +1 -1
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/15-anatomy.md +2 -2
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/18-containers.md +3 -3
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/40-routing.md +9 -9
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/50-solara-server.md +1 -1
- solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/10-oauth.md +3 -3
- solara/website/pages/{docs/content/10-howto → documentation/advanced/content/40-development}/01-contribute.md +5 -5
- solara/website/pages/{docs/content/90-development → documentation/advanced/content/40-development}/10-setup.md +2 -2
- solara/website/pages/documentation/advanced/content/__init__.py +0 -0
- solara/website/pages/documentation/api/__init__.py +19 -0
- solara/website/pages/documentation/api/cross_filter/__init__.py +9 -0
- solara/website/pages/documentation/api/hooks/__init__.py +9 -0
- solara/website/pages/{api → documentation/api/hooks}/use_effect.md +3 -3
- solara/website/pages/{api → documentation/api/hooks}/use_effect.py +2 -1
- solara/website/pages/{api → documentation/api/hooks}/use_memo.md +2 -2
- solara/website/pages/{api → documentation/api/hooks}/use_memo.py +2 -1
- solara/website/pages/{api → documentation/api/hooks}/use_reactive.py +1 -2
- solara/website/pages/{api → documentation/api/hooks}/use_state.py +1 -2
- solara/website/pages/documentation/api/routing/__init__.py +9 -0
- solara/website/pages/{api → documentation/api/routing}/generate_routes.py +1 -2
- solara/website/pages/{api → documentation/api/routing}/generate_routes_directory.py +1 -2
- solara/website/pages/{api → documentation/api/routing}/use_route.py +2 -2
- solara/website/pages/{api → documentation/api/routing}/use_router.py +1 -2
- solara/website/pages/documentation/api/utilities/__init__.py +9 -0
- solara/website/pages/{api → documentation/api/utilities}/component_vue.py +1 -2
- solara/website/pages/{api → documentation/api/utilities}/computed.py +2 -2
- solara/website/pages/{api → documentation/api/utilities}/display.py +1 -2
- solara/website/pages/{api → documentation/api/utilities}/get_kernel_id.py +2 -2
- solara/website/pages/{api → documentation/api/utilities}/get_session_id.py +2 -2
- solara/website/pages/{api → documentation/api/utilities}/on_kernel_start.py +9 -3
- solara/website/pages/{api → documentation/api/utilities}/reactive.py +1 -2
- solara/website/pages/{api → documentation/api/utilities}/widget.py +2 -2
- solara/website/pages/documentation/components/__init__.py +12 -0
- solara/website/pages/documentation/components/advanced/__init__.py +9 -0
- solara/website/pages/documentation/components/data/__init__.py +9 -0
- solara/website/pages/documentation/components/enterprise/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/enterprise}/avatar.py +1 -2
- solara/website/pages/{api → documentation/components/enterprise}/avatar_menu.py +1 -2
- solara/website/pages/documentation/components/input/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/input}/checkbox.py +1 -2
- solara/website/pages/documentation/components/input/file_drop.py +75 -0
- solara/website/pages/{api → documentation/components/input}/input.py +1 -2
- solara/website/pages/{api → documentation/components/input}/select.py +1 -2
- solara/website/pages/{api → documentation/components/input}/slider.py +1 -2
- solara/website/pages/{api → documentation/components/input}/switch.py +1 -2
- solara/website/pages/{api → documentation/components/input}/togglebuttons.py +1 -2
- solara/website/pages/documentation/components/lab/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/lab}/chat.py +2 -3
- solara/website/pages/{api → documentation/components/lab}/cookies_headers.py +1 -1
- solara/website/pages/{api → documentation/components/lab}/input_date.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/menu.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/task.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/theming.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/use_task.py +1 -2
- solara/website/pages/documentation/components/layout/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/layout}/app_bar.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/app_bar_title.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/card.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/card_actions.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/griddraggable.py +1 -1
- solara/website/pages/{api → documentation/components/layout}/gridfixed.py +1 -1
- solara/website/pages/{api → documentation/components/layout}/hbox.py +1 -1
- solara/website/pages/{api → documentation/components/layout}/vbox.py +1 -1
- solara/website/pages/documentation/components/output/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/output}/file_download.py +1 -2
- solara/website/pages/{api → documentation/components/output}/image.py +1 -2
- solara/website/pages/{api → documentation/components/output}/tooltip.py +1 -2
- solara/website/pages/documentation/components/page/__init__.py +9 -0
- solara/website/pages/documentation/components/status/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/status}/error.py +3 -3
- solara/website/pages/{api → documentation/components/status}/info.py +3 -3
- solara/website/pages/{api → documentation/components/status}/progress.py +1 -2
- solara/website/pages/{api → documentation/components/status}/spinner.py +1 -2
- solara/website/pages/{api → documentation/components/status}/success.py +3 -3
- solara/website/pages/{api → documentation/components/status}/warning.py +3 -3
- solara/website/pages/documentation/components/viz/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/viz}/plotly.py +1 -0
- solara/website/pages/{examples → documentation/examples}/__init__.py +3 -43
- solara/website/pages/{examples → documentation/examples}/general/deploy_model.py +3 -3
- solara/website/pages/{examples → documentation/examples}/general/login_oauth.py +1 -1
- solara/website/pages/{examples → documentation/examples}/general/vue_component.py +1 -2
- solara/website/pages/{examples → documentation/examples}/libraries/altair.py +2 -3
- solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet_advanced.py +1 -1
- solara/website/pages/{examples → documentation/examples}/utilities/countdown_timer.py +1 -1
- solara/website/pages/{examples → documentation/examples}/visualization/plotly.py +1 -2
- solara/website/pages/documentation/faq/__init__.py +11 -0
- solara/website/pages/{docs → documentation/getting_started}/__init__.py +7 -1
- solara/website/pages/{docs/content/03-quickstart.md → documentation/getting_started/content/00-quickstart.md} +2 -2
- solara/website/pages/{docs/content/00-introduction.md → documentation/getting_started/content/01-introduction.md} +16 -16
- solara/website/pages/{docs → documentation/getting_started}/content/02-installing.md +1 -1
- solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +9 -0
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/20-web-app.md +4 -4
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/30-ipywidgets.md +9 -9
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/40-streamlit.md +13 -13
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/50-dash.md +3 -3
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_data_science.ipynb +5 -5
- solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +7 -0
- solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/10-components.md +2 -2
- solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/50-state-management.md +3 -3
- solara/website/pages/documentation/getting_started/content/06-reference/00-overview.md +3 -0
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/41-asset-files.md +1 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/60-static-site-generation.md +1 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/70-search.md +1 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/80-reloading.md +3 -3
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/95-caching.md +1 -1
- solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +3 -0
- solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/10-self-hosted.md +3 -3
- solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/20-cloud-hosted.md +1 -1
- solara/website/pages/documentation/getting_started/content/__init__.py +0 -0
- solara/website/pages/showcase/__init__.py +1 -1
- solara/website/pages/showcase/domino_code_assist.py +1 -1
- solara/website/pages/showcase/solara_dev.py +1 -1
- solara/website/public/social/discord.svg +1 -0
- solara/website/public/social/github.svg +1 -0
- solara/website/public/social/twitter.svg +3 -0
- {solara-1.29.0.dist-info → solara-1.30.0.dist-info}/METADATA +8 -7
- solara-1.30.0.dist-info/RECORD +438 -0
- {solara-1.29.0.dist-info → solara-1.30.0.dist-info}/WHEEL +1 -1
- solara/website/pages/api/__init__.py +0 -292
- solara/website/pages/api/default_layout.py +0 -16
- solara/website/pages/api/file_drop.py +0 -36
- solara/website/pages/docs/content/04-tutorial/00-overview.md +0 -9
- solara/website/pages/docs/content/07-fundamentals/00-overview.md +0 -7
- solara/website/pages/docs/content/15-reference/00-overview.md +0 -6
- solara/website/pages/docs/content/30-deploying/00-overview.md +0 -3
- solara-1.29.0.data/data/prefix/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -7
- solara-1.29.0.data/data/prefix/etc/jupyter/jupyter_server_config.d/solara.json +0 -7
- solara-1.29.0.dist-info/RECORD +0 -413
- /solara/website/pages/{docs/content/99-contact.md → contact/contact.md} +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/10-howto/00-overview.md +0 -0
- /solara/website/pages/{docs/content/10-howto/50-testing.md → documentation/advanced/content/10-howto/30-testing.md} +0 -0
- /solara/website/pages/{docs/content/10-howto/51-debugging.md → documentation/advanced/content/10-howto/31-debugging.md} +0 -0
- /solara/website/pages/{docs/content/10-howto/ipywidget_libraries.md → documentation/advanced/content/10-howto/50-ipywidget_libraries.md} +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/00-introduction.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/05-ipywidgets.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/10-reacton.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/17-rules-of-hooks.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/20-solara.md +0 -0
- /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/60-voila.md +0 -0
- /solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/00-overview.md +0 -0
- /solara/website/pages/{docs/content/__init__.py → documentation/advanced/content/40-development/00-overview.md} +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_dataframe.py +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_report.py +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_select.py +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_slider.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_cross_filter.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_dark_effective.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_exception.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_previous.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_state_or_update.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_thread.md +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_thread.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_trait_observe.py +0 -0
- /solara/website/pages/{api → documentation/api/routing}/resolve_path.py +0 -0
- /solara/website/pages/{api → documentation/api/routing}/route.py +0 -0
- /solara/website/pages/{api → documentation/api/utilities}/memoize.py +0 -0
- /solara/website/pages/{api → documentation/components/advanced}/link.py +0 -0
- /solara/website/pages/{api → documentation/components/advanced}/meta.py +0 -0
- /solara/website/pages/{api → documentation/components/advanced}/style.py +0 -0
- /solara/website/pages/{api → documentation/components}/common.py +0 -0
- /solara/website/pages/{api → documentation/components/data}/dataframe.py +0 -0
- /solara/website/pages/{api → documentation/components/data}/pivot_table.py +0 -0
- /solara/website/pages/{api → documentation/components/input}/button.py +0 -0
- /solara/website/pages/{api → documentation/components/input}/file_browser.py +0 -0
- /solara/website/pages/{api → documentation/components/lab}/confirmation_dialog.py +0 -0
- /solara/website/pages/{api → documentation/components/lab}/tab.py +0 -0
- /solara/website/pages/{api → documentation/components/lab}/tabs.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/app_layout.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/column.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/columns.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/columns_responsive.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/row.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/sidebar.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/html.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/markdown.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/markdown_editor.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/sql_code.py +0 -0
- /solara/website/pages/{api → documentation/components/page}/head.py +0 -0
- /solara/website/pages/{api → documentation/components/page}/title.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/altair.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/echarts.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/matplotlib.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/plotly_express.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ai/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ai/chatbot.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ai/tokenizer.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/basics/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/basics/sine.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/authorization.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/layout_demo.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/multipage.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/scatter.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/scrolling.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/tutorial_streamlit.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/custom_storage.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/live_update.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/mycard.vue +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/pokemon_search.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ipycanvas.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/libraries/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/libraries/bqplot.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/utilities/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/utilities/calculator.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/utilities/todo.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/visualization/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/visualization/annotator.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/visualization/linked_views.py +0 -0
- /solara/website/pages/{docs → documentation/faq}/content/99-faq.md +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/10_data_science.py +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/60-jupyter-dashboard-part1.py +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/SF_crime_sample.csv.gz +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_jupyter_dashboard_1.ipynb +0 -0
- /solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/40-static_files.md +0 -0
- /solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/90-notebook-support.md +0 -0
- /solara/website/pages/{docs/content/lab → documentation/getting_started/content/08-lab}/00-what-is-lab.md +0 -0
- /solara/website/pages/{docs → documentation/getting_started}/content/90-troubleshoot.md +0 -0
- {solara-1.29.0.data → solara-1.30.0.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
- {solara-1.29.0.data → solara-1.30.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
- {solara-1.29.0.dist-info → solara-1.30.0.dist-info}/entry_points.txt +0 -0
- {solara-1.29.0.dist-info → solara-1.30.0.dist-info}/licenses/LICENSE +0 -0
solara/server/settings.py
CHANGED
solara/server/starlette.py
CHANGED
|
@@ -6,7 +6,7 @@ import os
|
|
|
6
6
|
import sys
|
|
7
7
|
import threading
|
|
8
8
|
import typing
|
|
9
|
-
from typing import Any, Dict, List, Optional, Union, cast
|
|
9
|
+
from typing import Any, Dict, List, Optional, Set, Union, cast
|
|
10
10
|
from uuid import uuid4
|
|
11
11
|
|
|
12
12
|
import anyio
|
|
@@ -40,7 +40,7 @@ from starlette.middleware import Middleware
|
|
|
40
40
|
from starlette.middleware.authentication import AuthenticationMiddleware
|
|
41
41
|
from starlette.middleware.gzip import GZipMiddleware
|
|
42
42
|
from starlette.requests import HTTPConnection, Request
|
|
43
|
-
from starlette.responses import HTMLResponse, JSONResponse, Response
|
|
43
|
+
from starlette.responses import HTMLResponse, JSONResponse, RedirectResponse, Response
|
|
44
44
|
from starlette.routing import Mount, Route, WebSocketRoute
|
|
45
45
|
from starlette.staticfiles import StaticFiles
|
|
46
46
|
from starlette.types import Receive, Scope, Send
|
|
@@ -96,10 +96,14 @@ class WebsocketDebugInfo:
|
|
|
96
96
|
class WebsocketWrapper(websocket.WebsocketWrapper):
|
|
97
97
|
ws: starlette.websockets.WebSocket
|
|
98
98
|
|
|
99
|
-
def __init__(self, ws: starlette.websockets.WebSocket, portal: anyio.from_thread.BlockingPortal) -> None:
|
|
99
|
+
def __init__(self, ws: starlette.websockets.WebSocket, portal: Optional[anyio.from_thread.BlockingPortal]) -> None:
|
|
100
100
|
self.ws = ws
|
|
101
101
|
self.portal = portal
|
|
102
102
|
self.to_send: List[Union[str, bytes]] = []
|
|
103
|
+
# following https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task
|
|
104
|
+
# we store a strong reference
|
|
105
|
+
self.tasks: Set[asyncio.Task] = set()
|
|
106
|
+
self.event_loop = asyncio.get_event_loop()
|
|
103
107
|
if settings.main.experimental_performance:
|
|
104
108
|
self.task = asyncio.ensure_future(self.process_messages_task())
|
|
105
109
|
|
|
@@ -114,28 +118,44 @@ class WebsocketWrapper(websocket.WebsocketWrapper):
|
|
|
114
118
|
await self.ws.send_text(first)
|
|
115
119
|
|
|
116
120
|
def close(self):
|
|
117
|
-
self.portal
|
|
121
|
+
if self.portal is None:
|
|
122
|
+
asyncio.ensure_future(self.ws.close())
|
|
123
|
+
else:
|
|
124
|
+
self.portal.call(self.ws.close) # type: ignore
|
|
118
125
|
|
|
119
126
|
def send_text(self, data: str) -> None:
|
|
120
|
-
if
|
|
121
|
-
self.
|
|
127
|
+
if self.portal is None:
|
|
128
|
+
task = self.event_loop.create_task(self.ws.send_text(data))
|
|
129
|
+
self.tasks.add(task)
|
|
130
|
+
task.add_done_callback(self.tasks.discard)
|
|
122
131
|
else:
|
|
123
|
-
|
|
132
|
+
if settings.main.experimental_performance:
|
|
133
|
+
self.to_send.append(data)
|
|
134
|
+
else:
|
|
135
|
+
self.portal.call(self.ws.send_bytes, data) # type: ignore
|
|
124
136
|
|
|
125
137
|
def send_bytes(self, data: bytes) -> None:
|
|
126
|
-
if
|
|
127
|
-
self.
|
|
138
|
+
if self.portal is None:
|
|
139
|
+
task = self.event_loop.create_task(self.ws.send_bytes(data))
|
|
140
|
+
self.tasks.add(task)
|
|
141
|
+
task.add_done_callback(self.tasks.discard)
|
|
128
142
|
else:
|
|
129
|
-
|
|
143
|
+
if settings.main.experimental_performance:
|
|
144
|
+
self.to_send.append(data)
|
|
145
|
+
else:
|
|
146
|
+
self.portal.call(self.ws.send_bytes, data) # type: ignore
|
|
130
147
|
|
|
131
148
|
async def receive(self):
|
|
132
|
-
if
|
|
133
|
-
|
|
134
|
-
fut = self.portal.start_task_soon(self.ws.receive) # type: ignore
|
|
149
|
+
if self.portal is None:
|
|
150
|
+
message = await asyncio.ensure_future(self.ws.receive())
|
|
135
151
|
else:
|
|
136
|
-
|
|
152
|
+
if hasattr(self.portal, "start_task_soon"):
|
|
153
|
+
# version 3+
|
|
154
|
+
fut = self.portal.start_task_soon(self.ws.receive) # type: ignore
|
|
155
|
+
else:
|
|
156
|
+
fut = self.portal.spawn_task(self.ws.receive) # type: ignore
|
|
137
157
|
|
|
138
|
-
|
|
158
|
+
message = await asyncio.wrap_future(fut)
|
|
139
159
|
if "text" in message:
|
|
140
160
|
return message["text"]
|
|
141
161
|
elif "bytes" in message:
|
|
@@ -237,35 +257,45 @@ async def _kernel_connection(ws: starlette.websockets.WebSocket):
|
|
|
237
257
|
WebsocketDebugInfo.connecting -= 1
|
|
238
258
|
WebsocketDebugInfo.open += 1
|
|
239
259
|
|
|
240
|
-
def
|
|
241
|
-
|
|
260
|
+
async def run(ws_wrapper: WebsocketWrapper):
|
|
261
|
+
if kernel_context.async_context_id is not None:
|
|
262
|
+
kernel_context.async_context_id.set(uuid4().hex)
|
|
263
|
+
assert session_id is not None
|
|
264
|
+
assert kernel_id is not None
|
|
265
|
+
telemetry.connection_open(session_id)
|
|
266
|
+
headers_dict: Dict[str, List[str]] = {}
|
|
267
|
+
for k, v in ws.headers.items():
|
|
268
|
+
if k not in headers_dict.keys():
|
|
269
|
+
headers_dict[k] = [v]
|
|
270
|
+
else:
|
|
271
|
+
headers_dict[k].append(v)
|
|
272
|
+
await server.app_loop(ws_wrapper, ws.cookies, headers_dict, session_id, kernel_id, page_id, user)
|
|
273
|
+
|
|
274
|
+
def websocket_thread_runner(ws_wrapper: WebsocketWrapper, portal: anyio.from_thread.BlockingPortal):
|
|
275
|
+
async def run_wrapper():
|
|
242
276
|
try:
|
|
243
|
-
|
|
244
|
-
assert kernel_id is not None
|
|
245
|
-
telemetry.connection_open(session_id)
|
|
246
|
-
headers_dict: Dict[str, List[str]] = {}
|
|
247
|
-
for k, v in ws.headers.items():
|
|
248
|
-
if k not in headers_dict.keys():
|
|
249
|
-
headers_dict[k] = [v]
|
|
250
|
-
else:
|
|
251
|
-
headers_dict[k].append(v)
|
|
252
|
-
await server.app_loop(ws_wrapper, ws.cookies, headers_dict, session_id, kernel_id, page_id, user)
|
|
277
|
+
await run(ws_wrapper)
|
|
253
278
|
except: # noqa
|
|
254
|
-
|
|
279
|
+
if portal is not None:
|
|
280
|
+
await portal.stop(cancel_remaining=True)
|
|
255
281
|
raise
|
|
256
282
|
finally:
|
|
257
283
|
telemetry.connection_close(session_id)
|
|
258
284
|
|
|
259
285
|
# sometimes throws: RuntimeError: Already running asyncio in this thread
|
|
260
|
-
anyio.run(
|
|
286
|
+
anyio.run(run_wrapper) # type: ignore
|
|
261
287
|
|
|
262
288
|
# this portal allows us to sync call the websocket calls from this current event loop we are in
|
|
263
289
|
# each websocket however, is handled from a separate thread
|
|
264
290
|
try:
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
291
|
+
if settings.kernel.threaded:
|
|
292
|
+
async with anyio.from_thread.BlockingPortal() as portal:
|
|
293
|
+
ws_wrapper = WebsocketWrapper(ws, portal)
|
|
294
|
+
thread_return = anyio.to_thread.run_sync(websocket_thread_runner, ws_wrapper, portal, limiter=limiter) # type: ignore
|
|
295
|
+
await thread_return
|
|
296
|
+
else:
|
|
297
|
+
ws_wrapper = WebsocketWrapper(ws, None)
|
|
298
|
+
await run(ws_wrapper)
|
|
269
299
|
finally:
|
|
270
300
|
if settings.main.experimental_performance:
|
|
271
301
|
try:
|
|
@@ -315,6 +345,9 @@ async def root(request: Request, fullpath: str = ""):
|
|
|
315
345
|
request_path = request.url.path
|
|
316
346
|
if request_path.startswith(root_path):
|
|
317
347
|
request_path = request_path[len(root_path) :]
|
|
348
|
+
if request_path in server._redirects.keys():
|
|
349
|
+
return RedirectResponse(server._redirects[request_path])
|
|
350
|
+
|
|
318
351
|
content = server.read_root(request_path, root_path)
|
|
319
352
|
if content is None:
|
|
320
353
|
if settings.oauth.private and not request.user.is_authenticated:
|
|
@@ -119,7 +119,7 @@ async def main():
|
|
|
119
119
|
]
|
|
120
120
|
for dep in requirements:
|
|
121
121
|
await micropip.install(dep, keep_going=True)
|
|
122
|
-
await micropip.install("/wheels/solara-1.
|
|
122
|
+
await micropip.install("/wheels/solara-1.30.0-py2.py3-none-any.whl", keep_going=True)
|
|
123
123
|
import solara
|
|
124
124
|
|
|
125
125
|
el = solara.Warning("lala")
|
|
@@ -11,13 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
{% block header %}
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
{{ pre_rendered_css | safe }}
|
|
16
|
-
|
|
17
15
|
{% if vue3 == True %}
|
|
18
|
-
<link href="{{cdn}}/@widgetti/solara-vuetify3-app@5.0.1/dist/main{{ipywidget_major_version}}.css" rel="stylesheet"></link>
|
|
16
|
+
<link href="{{cdn}}/@widgetti/solara-vuetify3-app@5.0.1/dist/main{{ipywidget_major_version}}.css" rel="stylesheet" class="solara-template-css"></link>
|
|
19
17
|
{% else %}
|
|
20
|
-
<link href="{{cdn}}/@widgetti/solara-vuetify-app@10.0.1/dist/main{{ipywidget_major_version}}.css" rel="stylesheet"></link>
|
|
18
|
+
<link href="{{cdn}}/@widgetti/solara-vuetify-app@10.0.1/dist/main{{ipywidget_major_version}}.css" rel="stylesheet" class="solara-template-css"></link>
|
|
21
19
|
{% endif %}
|
|
22
20
|
|
|
23
21
|
|
|
@@ -27,8 +25,7 @@
|
|
|
27
25
|
{{ resources.include_css("/static/highlight.css") }}
|
|
28
26
|
{{ resources.include_css("/static/highlight-dark.css") }}
|
|
29
27
|
{{ resources.include_css("/static/assets/style.css") }}
|
|
30
|
-
<style id="jupyter-theme-css">
|
|
31
|
-
{{ resources.include_css("/static/assets/theme-"~theme.variant~".css") }}
|
|
28
|
+
<style id="jupyter-theme-css" class="solara-template-css">
|
|
32
29
|
</style>
|
|
33
30
|
{{ resources.include_css("/static/assets/custom.css") }}
|
|
34
31
|
|
|
@@ -38,9 +35,24 @@
|
|
|
38
35
|
"kernelId": "1234"
|
|
39
36
|
}
|
|
40
37
|
</script>
|
|
41
|
-
<style>
|
|
38
|
+
<style class="solara-template-css">
|
|
42
39
|
{% include "loader-"~theme.loader~".css" %}
|
|
43
40
|
</style>
|
|
41
|
+
<!-- Include Vuetify background colours so static html from SSG renders the right general colour theme
|
|
42
|
+
before first render. We remove these after Vue takes over rendering to avoid collisions -->
|
|
43
|
+
<style id="pre-render-theme">
|
|
44
|
+
.theme--light .v-sheet {
|
|
45
|
+
background-color: #fff;
|
|
46
|
+
border-color: #fff;
|
|
47
|
+
color: rgba(0,0,0,.87)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.theme--dark .v-sheet {
|
|
51
|
+
background-color: #1e1e1e;
|
|
52
|
+
border-color: #1e1e1e;
|
|
53
|
+
color: #fff
|
|
54
|
+
}
|
|
55
|
+
</style>
|
|
44
56
|
{% endblock header %}
|
|
45
57
|
</head>
|
|
46
58
|
{% raw -%}
|
|
@@ -175,6 +187,38 @@
|
|
|
175
187
|
{# next div is used in ssg code to see if vue took over rendering #}
|
|
176
188
|
<div id="pre-rendered-html-present" style="display: none"></div>
|
|
177
189
|
</div>
|
|
190
|
+
<script>
|
|
191
|
+
var theme = {{ theme | tojson | safe }}
|
|
192
|
+
function getThemeVariant() {
|
|
193
|
+
if (localStorage.getItem(':solara:theme.variant')) {
|
|
194
|
+
return JSON.parse(localStorage.getItem(':solara:theme.variant'))
|
|
195
|
+
}
|
|
196
|
+
return theme.variant;
|
|
197
|
+
}
|
|
198
|
+
if (localStorage.getItem(':solara:theme.variant')) {
|
|
199
|
+
theme.variant = JSON.parse(localStorage.getItem(':solara:theme.variant'))
|
|
200
|
+
}
|
|
201
|
+
function prefersDarkScheme() {
|
|
202
|
+
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
203
|
+
}
|
|
204
|
+
function inDarkMode() {
|
|
205
|
+
if (getThemeVariant() == 'auto') {
|
|
206
|
+
return prefersDarkScheme();
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
return getThemeVariant() == 'dark';
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Init theme
|
|
213
|
+
let appContainer = document.getElementById('app');
|
|
214
|
+
if (inDarkMode()) {
|
|
215
|
+
appContainer.classList.remove('theme--light');
|
|
216
|
+
appContainer.classList.add('theme--dark');
|
|
217
|
+
} else {
|
|
218
|
+
appContainer.classList.remove('theme--dark');
|
|
219
|
+
appContainer.classList.add('theme--light');
|
|
220
|
+
}
|
|
221
|
+
</script>
|
|
178
222
|
{% block after_pre_rendered_html %}{% endblock %}
|
|
179
223
|
{% if vue3 == True %}
|
|
180
224
|
<link href="{{cdn}}/@widgetti/solara-vuetify3-app@5.0.1/dist/fonts.css" rel="stylesheet"></link>
|
|
@@ -194,6 +238,12 @@
|
|
|
194
238
|
<script>
|
|
195
239
|
solara.rootPath = {{ root_path | tojson | safe}};
|
|
196
240
|
console.log("rootPath", solara.rootPath);
|
|
241
|
+
|
|
242
|
+
async function changeThemeCSS(theme) {
|
|
243
|
+
let css = await fetch(`${solara.rootPath}/static/assets/theme-${theme}.css`).then(r => r.text());
|
|
244
|
+
document.getElementById('jupyter-theme-css').innerHTML = css;
|
|
245
|
+
}
|
|
246
|
+
changeThemeCSS(inDarkMode() ? 'dark' : 'light');
|
|
197
247
|
</script>
|
|
198
248
|
|
|
199
249
|
{{ resources.include_js("/static/assets/custom.js") }}
|
|
@@ -208,27 +258,6 @@
|
|
|
208
258
|
solara.production = {{ production | tojson | safe }};
|
|
209
259
|
const themeVariants = ['light', 'dark', 'auto']
|
|
210
260
|
solara.preRendered = {{ pre_rendered_html | safe | length | tojson }} > 0
|
|
211
|
-
var theme = {{ theme | tojson | safe }}
|
|
212
|
-
function getThemeVariant() {
|
|
213
|
-
if (localStorage.getItem(':solara:theme.variant')) {
|
|
214
|
-
return JSON.parse(localStorage.getItem(':solara:theme.variant'))
|
|
215
|
-
}
|
|
216
|
-
return theme.variant;
|
|
217
|
-
}
|
|
218
|
-
if (localStorage.getItem(':solara:theme.variant')) {
|
|
219
|
-
theme.variant = JSON.parse(localStorage.getItem(':solara:theme.variant'))
|
|
220
|
-
}
|
|
221
|
-
function prefersDarkScheme() {
|
|
222
|
-
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
223
|
-
}
|
|
224
|
-
function inDarkMode() {
|
|
225
|
-
if (getThemeVariant() == 'auto') {
|
|
226
|
-
return prefersDarkScheme();
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
return getThemeVariant() == 'dark';
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
261
|
</script>
|
|
233
262
|
|
|
234
263
|
<script>
|
|
@@ -256,11 +285,7 @@
|
|
|
256
285
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
257
286
|
this.$vuetify.theme.dark = inDarkMode();
|
|
258
287
|
});
|
|
259
|
-
|
|
260
|
-
this.changeThemeCSS('dark');
|
|
261
|
-
} else {
|
|
262
|
-
this.changeThemeCSS('light');
|
|
263
|
-
}
|
|
288
|
+
document.getElementById('pre-render-theme').remove();
|
|
264
289
|
},
|
|
265
290
|
methods: {
|
|
266
291
|
stateReset() {
|
|
@@ -275,10 +300,6 @@
|
|
|
275
300
|
reload() {
|
|
276
301
|
location.reload();
|
|
277
302
|
},
|
|
278
|
-
async changeThemeCSS(theme) {
|
|
279
|
-
let css = await fetch(`/static/assets/theme-${theme}.css`).then(r => r.text());
|
|
280
|
-
document.getElementById('jupyter-theme-css').innerHTML = css;
|
|
281
|
-
}
|
|
282
303
|
},
|
|
283
304
|
watch: {
|
|
284
305
|
kernelBusy: function (value) {
|
|
@@ -325,15 +346,14 @@
|
|
|
325
346
|
}
|
|
326
347
|
},
|
|
327
348
|
'$vuetify.theme.dark': function (value) {
|
|
328
|
-
let app = document.getElementById('app');
|
|
329
349
|
if ( value ) {
|
|
330
350
|
this.changeThemeCSS('dark');
|
|
331
|
-
|
|
332
|
-
|
|
351
|
+
appContainer.classList.remove('theme--light');
|
|
352
|
+
appContainer.classList.add('theme--dark');
|
|
333
353
|
} else {
|
|
334
354
|
this.changeThemeCSS('light');
|
|
335
|
-
|
|
336
|
-
|
|
355
|
+
appContainer.classList.remove('theme--dark');
|
|
356
|
+
appContainer.classList.add('theme--light');
|
|
337
357
|
}
|
|
338
358
|
}
|
|
339
359
|
},
|
solara/tasks.py
CHANGED
|
@@ -32,12 +32,7 @@ P = typing_extensions.ParamSpec("P")
|
|
|
32
32
|
|
|
33
33
|
logger = logging.getLogger("solara.task")
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
threading.Thread(target=lambda: None).start()
|
|
37
|
-
has_threads = True
|
|
38
|
-
except RuntimeError:
|
|
39
|
-
has_threads = False
|
|
40
|
-
has_threads
|
|
35
|
+
has_threads = solara.util.has_threads
|
|
41
36
|
|
|
42
37
|
|
|
43
38
|
class TaskState(Enum):
|
solara/util.py
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import contextlib
|
|
3
|
+
import gzip
|
|
3
4
|
import hashlib
|
|
5
|
+
import json
|
|
4
6
|
import os
|
|
5
7
|
import sys
|
|
6
8
|
import threading
|
|
7
9
|
from collections import abc
|
|
8
10
|
from pathlib import Path
|
|
9
|
-
from typing import TYPE_CHECKING, Dict, List, Tuple, Union
|
|
11
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
|
|
12
|
+
from urllib.parse import urlencode
|
|
10
13
|
|
|
11
14
|
if TYPE_CHECKING:
|
|
12
15
|
import numpy as np
|
|
@@ -21,6 +24,12 @@ SOLARA_ALLOW_OTHER_TRACER = os.environ.get("SOLARA_ALLOW_OTHER_TRACER", False) i
|
|
|
21
24
|
ipyvuetify_major_version = int(ipyvuetify.__version__.split(".")[0])
|
|
22
25
|
ipywidgets_major = int(ipywidgets.__version__.split(".")[0])
|
|
23
26
|
|
|
27
|
+
try:
|
|
28
|
+
threading.Thread(target=lambda: None).start()
|
|
29
|
+
has_threads = True
|
|
30
|
+
except RuntimeError:
|
|
31
|
+
has_threads = False
|
|
32
|
+
|
|
24
33
|
|
|
25
34
|
def github_url(file):
|
|
26
35
|
rel_path = os.path.relpath(file, Path(solara.__file__).parent.parent)
|
|
@@ -28,6 +37,19 @@ def github_url(file):
|
|
|
28
37
|
return github_url
|
|
29
38
|
|
|
30
39
|
|
|
40
|
+
def pycafe_url(*, path: Path, requirements: Optional[List[str]] = None):
|
|
41
|
+
json_object = {"code": path.read_text("utf8")}
|
|
42
|
+
if requirements:
|
|
43
|
+
json_object["requirements"] = "\n".join(requirements)
|
|
44
|
+
json_text = json.dumps(json_object)
|
|
45
|
+
# gzip -> base64
|
|
46
|
+
compressed_json_text = gzip.compress(json_text.encode("utf8"))
|
|
47
|
+
base64_text = base64.b64encode(compressed_json_text).decode("utf8")
|
|
48
|
+
query = urlencode({"c": base64_text})
|
|
49
|
+
base_url = "https://app.py.cafe"
|
|
50
|
+
return f"{base_url}/snippet/solara?{query}"
|
|
51
|
+
|
|
52
|
+
|
|
31
53
|
def github_edit_url(file):
|
|
32
54
|
# e.g. https://github.com/widgetti/solara/edit/master/solara/__init__.py
|
|
33
55
|
rel_path = os.path.relpath(file, Path(solara.__file__).parent.parent)
|
solara/website/assets/custom.css
CHANGED
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
--dark-color-text: hsla(0,0%,100%,.7);
|
|
28
28
|
--dark-color-grey: #ababab;
|
|
29
29
|
--dark-color-grey-light: #202020;
|
|
30
|
+
|
|
31
|
+
--docs-color-grey: #40362E;
|
|
32
|
+
--docs-social-discord: #5865F2;
|
|
33
|
+
--docs-social-github: #333;
|
|
34
|
+
--docs-social-twitter: #000000;
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
html,
|
|
@@ -178,6 +183,56 @@ blockquote p:last-child {
|
|
|
178
183
|
background-color: var(--dark-color-primary-lightest);
|
|
179
184
|
}
|
|
180
185
|
|
|
186
|
+
/* API PAGE REFACTOR STYLE */
|
|
187
|
+
|
|
188
|
+
.v-sheet.api-search-container{
|
|
189
|
+
flex-grow: 1;
|
|
190
|
+
}
|
|
191
|
+
.v-sheet.api-search-container .v-input{
|
|
192
|
+
flex-grow: 0;
|
|
193
|
+
}
|
|
194
|
+
.v-sheet.docs-card-container{
|
|
195
|
+
width: 100%;
|
|
196
|
+
padding: 0 10%;
|
|
197
|
+
justify-content: start;
|
|
198
|
+
align-content: center;
|
|
199
|
+
flex-wrap: wrap !important;
|
|
200
|
+
flex-grow: 1;
|
|
201
|
+
row-gap: 20px;
|
|
202
|
+
}
|
|
203
|
+
.v-sheet.docs-card-container > a{
|
|
204
|
+
height: fit-content;
|
|
205
|
+
}
|
|
206
|
+
.v-sheet.docs-card-container > a > .docs-card{
|
|
207
|
+
display: flex;
|
|
208
|
+
width: 20rem;
|
|
209
|
+
height: 12rem;
|
|
210
|
+
border-radius: 16px !important;
|
|
211
|
+
color: var(--color-grey-light);
|
|
212
|
+
}
|
|
213
|
+
.v-sheet.docs-card-container > a .docs-card-icon{
|
|
214
|
+
padding-right: 1.5rem;
|
|
215
|
+
transition: ease padding 0.25s;
|
|
216
|
+
}
|
|
217
|
+
.v-sheet.docs-card-container > a:hover .docs-card-icon{
|
|
218
|
+
color: var(--color-primary);
|
|
219
|
+
padding-right: 0.5rem;
|
|
220
|
+
transition: ease padding 0.25s;
|
|
221
|
+
}
|
|
222
|
+
.component-card{
|
|
223
|
+
display: flex;
|
|
224
|
+
flex-direction: column;
|
|
225
|
+
width: 19rem;
|
|
226
|
+
}
|
|
227
|
+
.social-logo-container{
|
|
228
|
+
display: flex;
|
|
229
|
+
justify-content: center;
|
|
230
|
+
align-items: center;
|
|
231
|
+
width: 4rem;
|
|
232
|
+
height: 4rem;
|
|
233
|
+
border-radius: 8px;
|
|
234
|
+
}
|
|
235
|
+
|
|
181
236
|
/* vuetify component overrides */
|
|
182
237
|
|
|
183
238
|
.v-btn.solara-docs-button {
|
|
@@ -354,6 +409,7 @@ blockquote p:last-child {
|
|
|
354
409
|
}
|
|
355
410
|
|
|
356
411
|
.solara-autorouter-content {
|
|
412
|
+
padding-top: 2rem;
|
|
357
413
|
max-width: 100%;
|
|
358
414
|
}
|
|
359
415
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-menu
|
|
3
|
+
v-model="show_results"
|
|
4
|
+
offset-y>
|
|
5
|
+
<template v-slot:activator="{ on }">
|
|
6
|
+
<v-text-field
|
|
7
|
+
v-model="query"
|
|
8
|
+
prepend-inner-icon="mdi-magnify"
|
|
9
|
+
hide-details
|
|
10
|
+
:placeholder="mac ? '⌘K to search' : 'Ctrl+K to search'"
|
|
11
|
+
outlined
|
|
12
|
+
rounded
|
|
13
|
+
clearable
|
|
14
|
+
ref="search"
|
|
15
|
+
style="max-width: 50%; flex-grow: 1;"
|
|
16
|
+
@click="show($event, on);"
|
|
17
|
+
@keyup.enter="item = 0"
|
|
18
|
+
></v-text-field>
|
|
19
|
+
</template>
|
|
20
|
+
<v-list v-if="results != null && results.length == 0">
|
|
21
|
+
<v-list-item>
|
|
22
|
+
<v-list-item-content>
|
|
23
|
+
{{this.query == "" ? "Start Typing to Search" : "No search results found."}}
|
|
24
|
+
</v-list-item-content>
|
|
25
|
+
</v-list-item>
|
|
26
|
+
</v-list>
|
|
27
|
+
<v-list v-else>
|
|
28
|
+
<v-list-item-group v-model="item">
|
|
29
|
+
<v-list-item v-for="(element, index) in this.results.hits" :key="element['url']">
|
|
30
|
+
<v-list-item-content>
|
|
31
|
+
<v-list-item-title>{{ element.hierarchy[element.type] }}</v-list-item-title>
|
|
32
|
+
</v-list-item-content>
|
|
33
|
+
</v-list-item>
|
|
34
|
+
</v-list-item-group>
|
|
35
|
+
<v-list-item>
|
|
36
|
+
<v-list-item-content>
|
|
37
|
+
<v-list-item-title>And {{ this.results.nbHits - 10}} More...</v-list-item-title>
|
|
38
|
+
</v-list-item-content>
|
|
39
|
+
</v-list-item>
|
|
40
|
+
</v-list>
|
|
41
|
+
</v-menu>
|
|
42
|
+
</template>
|
|
43
|
+
<script>
|
|
44
|
+
const search = ref(null);
|
|
45
|
+
module.exports = {
|
|
46
|
+
async mounted() {
|
|
47
|
+
window.search = this;
|
|
48
|
+
await this.loadRequire();
|
|
49
|
+
this.algoliasearch = (await this.import([`https://cdn.jsdelivr.net/npm/algoliasearch@4.20.0/dist/algoliasearch-lite.umd.js`]))[0];
|
|
50
|
+
this.initSearch();
|
|
51
|
+
window.search = this;
|
|
52
|
+
},
|
|
53
|
+
watch: {
|
|
54
|
+
query ( value ) {
|
|
55
|
+
this.show_results = true;
|
|
56
|
+
this.search();
|
|
57
|
+
},
|
|
58
|
+
item ( value ) {
|
|
59
|
+
if ( this.results.hits != null && this.results.hits.length > 0 ) {
|
|
60
|
+
let url = this.results.hits[value].url;
|
|
61
|
+
if (url.startsWith("https://solara.dev")) {
|
|
62
|
+
url = url.slice(18);
|
|
63
|
+
}
|
|
64
|
+
solara.router.push( url );
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
methods: {
|
|
69
|
+
show( e, on ) {
|
|
70
|
+
this.show_results = false;
|
|
71
|
+
this.$nextTick( () => {
|
|
72
|
+
on.click( e )
|
|
73
|
+
} );
|
|
74
|
+
},
|
|
75
|
+
initSearch() {
|
|
76
|
+
this.client = this.algoliasearch( '9KW9L7O5EQ', '647ca12ba642437cc40c2adee4a78d08' );
|
|
77
|
+
this.index = this.client.initIndex( 'solara' );
|
|
78
|
+
this.mac = window.navigator.userAgent.indexOf("Mac") != -1;
|
|
79
|
+
document.addEventListener( 'keydown', ( e ) => {
|
|
80
|
+
|
|
81
|
+
if (this.mac) {
|
|
82
|
+
if ( this.$refs.search && e.metaKey && e.key === 'k' ) {
|
|
83
|
+
this.$refs.search.focus();
|
|
84
|
+
}
|
|
85
|
+
}else{
|
|
86
|
+
if ( this.$refs.search && e.ctrlKey && e.key === 'k' ) {
|
|
87
|
+
this.$refs.search.focus();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
async search() {
|
|
93
|
+
results = await this.index.search( this.query, { hitsPerPage: 10 } );
|
|
94
|
+
if (results) {
|
|
95
|
+
this.results = results;
|
|
96
|
+
}else{
|
|
97
|
+
this.$set(this.results, []);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
import(dependencies) {
|
|
101
|
+
return this.loadRequire().then(
|
|
102
|
+
() => {
|
|
103
|
+
if (window.jupyterVue) {
|
|
104
|
+
// in jupyterlab, we take Vue from ipyvue/jupyterVue
|
|
105
|
+
define("vue", [], () => window.jupyterVue.Vue);
|
|
106
|
+
} else {
|
|
107
|
+
define("vue", ['jupyter-vue'], jupyterVue => jupyterVue.Vue);
|
|
108
|
+
}
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
requirejs(dependencies, (...modules) => resolve(modules));
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
},
|
|
115
|
+
loadRequire() {
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const script = document.createElement('script');
|
|
118
|
+
script.src = `${this.getCdn()}/requirejs@2.3.6/require.js`;
|
|
119
|
+
script.onload = resolve;
|
|
120
|
+
script.onerror = reject;
|
|
121
|
+
document.head.appendChild(script);
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
getBaseUrl() {
|
|
125
|
+
if (window.solara && window.solara.rootPath !== undefined) {
|
|
126
|
+
return solara.rootPath + "/";
|
|
127
|
+
}
|
|
128
|
+
// if base url is set, we use ./ for relative paths compared to the base url
|
|
129
|
+
if (document.getElementsByTagName("base").length) {
|
|
130
|
+
return document.baseURI;
|
|
131
|
+
}
|
|
132
|
+
const labConfigData = document.getElementById('jupyter-config-data');
|
|
133
|
+
if (labConfigData) {
|
|
134
|
+
/* lab and Voila */
|
|
135
|
+
return JSON.parse(labConfigData.textContent).baseUrl;
|
|
136
|
+
}
|
|
137
|
+
let base = document.body.dataset.baseUrl || document.baseURI;
|
|
138
|
+
if (!base.endsWith('/')) {
|
|
139
|
+
base += '/';
|
|
140
|
+
}
|
|
141
|
+
return base
|
|
142
|
+
},
|
|
143
|
+
getCdn() {
|
|
144
|
+
return (typeof solara_cdn !== "undefined" && solara_cdn) || `${this.getBaseUrl()}_solara/cdn`;
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
data(){
|
|
148
|
+
return {
|
|
149
|
+
query: '',
|
|
150
|
+
results: [],
|
|
151
|
+
item: null,
|
|
152
|
+
show_results: false,
|
|
153
|
+
mac: false,
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
</script>
|