solara-ui 1.41.0__py2.py3-none-any.whl → 1.43.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.
Files changed (83) hide show
  1. solara/__init__.py +1 -1
  2. solara/__main__.py +17 -6
  3. solara/_stores.py +189 -0
  4. solara/components/__init__.py +18 -1
  5. solara/components/component_vue.py +23 -0
  6. solara/components/datatable.py +4 -4
  7. solara/components/echarts.py +5 -2
  8. solara/components/echarts.vue +22 -5
  9. solara/components/file_drop.py +20 -0
  10. solara/components/input.py +21 -1
  11. solara/components/markdown.py +62 -17
  12. solara/components/misc.py +2 -2
  13. solara/components/spinner-solara.vue +2 -2
  14. solara/components/spinner.py +17 -2
  15. solara/hooks/use_reactive.py +8 -1
  16. solara/lab/components/__init__.py +1 -0
  17. solara/lab/components/chat.py +3 -3
  18. solara/lab/components/input_time.py +133 -0
  19. solara/lab/hooks/dataframe.py +1 -0
  20. solara/lab/utils/dataframe.py +11 -1
  21. solara/reactive.py +9 -3
  22. solara/server/app.py +63 -30
  23. solara/server/flask.py +12 -2
  24. solara/server/jupyter/server_extension.py +1 -0
  25. solara/server/kernel.py +52 -4
  26. solara/server/kernel_context.py +66 -7
  27. solara/server/patch.py +25 -29
  28. solara/server/qt.py +1 -1
  29. solara/server/server.py +15 -5
  30. solara/server/settings.py +11 -0
  31. solara/server/shell.py +19 -1
  32. solara/server/starlette.py +39 -11
  33. solara/server/static/solara_bootstrap.py +1 -1
  34. solara/settings.py +17 -0
  35. solara/tasks.py +18 -8
  36. solara/template/portal/pyproject.toml +1 -1
  37. solara/test/pytest_plugin.py +4 -0
  38. solara/toestand.py +170 -16
  39. solara/util.py +40 -0
  40. solara/website/components/docs.py +4 -0
  41. solara/website/components/markdown.py +60 -2
  42. solara/website/pages/changelog/changelog.md +17 -0
  43. solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md +10 -0
  44. solara/website/pages/documentation/api/cross_filter/cross_filter_dataframe.py +4 -5
  45. solara/website/pages/documentation/api/cross_filter/cross_filter_report.py +3 -5
  46. solara/website/pages/documentation/api/cross_filter/cross_filter_select.py +3 -5
  47. solara/website/pages/documentation/api/cross_filter/cross_filter_slider.py +3 -5
  48. solara/website/pages/documentation/api/hooks/use_cross_filter.py +3 -5
  49. solara/website/pages/documentation/api/hooks/use_exception.py +9 -11
  50. solara/website/pages/documentation/api/hooks/use_previous.py +6 -9
  51. solara/website/pages/documentation/api/hooks/use_state_or_update.py +23 -26
  52. solara/website/pages/documentation/api/hooks/use_thread.py +11 -13
  53. solara/website/pages/documentation/api/routing/route.py +10 -12
  54. solara/website/pages/documentation/api/routing/use_route.py +26 -30
  55. solara/website/pages/documentation/api/utilities/on_kernel_start.py +17 -0
  56. solara/website/pages/documentation/components/advanced/link.py +6 -8
  57. solara/website/pages/documentation/components/advanced/meta.py +6 -9
  58. solara/website/pages/documentation/components/advanced/style.py +7 -9
  59. solara/website/pages/documentation/components/input/file_browser.py +12 -14
  60. solara/website/pages/documentation/components/input/input.py +22 -0
  61. solara/website/pages/documentation/components/lab/input_time.py +15 -0
  62. solara/website/pages/documentation/components/layout/columns_responsive.py +37 -39
  63. solara/website/pages/documentation/components/layout/gridfixed.py +4 -6
  64. solara/website/pages/documentation/components/output/html.py +1 -3
  65. solara/website/pages/documentation/components/page/head.py +4 -7
  66. solara/website/pages/documentation/components/viz/echarts.py +3 -1
  67. solara/website/pages/documentation/examples/__init__.py +9 -0
  68. solara/website/pages/documentation/examples/ai/chatbot.py +60 -44
  69. solara/website/pages/documentation/examples/general/live_update.py +1 -0
  70. solara/website/pages/documentation/examples/general/pokemon_search.py +3 -3
  71. solara/website/pages/documentation/examples/visualization/linked_views.py +0 -3
  72. solara/website/pages/documentation/faq/content/99-faq.md +9 -0
  73. solara/website/pages/documentation/getting_started/content/00-quickstart.md +2 -2
  74. solara/website/pages/documentation/getting_started/content/01-introduction.md +1 -1
  75. solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb +23 -19
  76. solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +2 -2
  77. solara/website/pages/roadmap/roadmap.md +6 -0
  78. {solara_ui-1.41.0.dist-info → solara_ui-1.43.0.dist-info}/METADATA +9 -6
  79. {solara_ui-1.41.0.dist-info → solara_ui-1.43.0.dist-info}/RECORD +83 -80
  80. {solara_ui-1.41.0.dist-info → solara_ui-1.43.0.dist-info}/WHEEL +1 -1
  81. {solara_ui-1.41.0.data → solara_ui-1.43.0.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
  82. {solara_ui-1.41.0.data → solara_ui-1.43.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
  83. {solara_ui-1.41.0.dist-info → solara_ui-1.43.0.dist-info}/licenses/LICENSE +0 -0
@@ -5,6 +5,8 @@ from .breadcrumbs import BreadCrumbs
5
5
 
6
6
  @solara.component
7
7
  def Gallery(route_external=None):
8
+ from ..pages.documentation.examples import pycafe_projects
9
+
8
10
  if route_external is not None:
9
11
  route_current = route_external
10
12
  else:
@@ -49,6 +51,8 @@ def Gallery(route_external=None):
49
51
  image_url = "https://dxhl76zpt6fap.cloudfront.net/public/api/" + child.path + ".gif"
50
52
  elif child.path in ["card", "dataframe", "pivot_table", "slider"]:
51
53
  image_url = "https://dxhl76zpt6fap.cloudfront.net/public/api/" + child.path + ".png"
54
+ elif child.path in pycafe_projects:
55
+ image_url = f"https://py.cafe/preview/solara/{child.path}"
52
56
  else:
53
57
  image_url = "https://dxhl76zpt6fap.cloudfront.net/public/logo.svg"
54
58
 
@@ -1,13 +1,18 @@
1
- from typing import Dict, List, Union
1
+ from typing import Callable, Dict, List, Union, cast
2
2
 
3
3
  import yaml
4
+ import markdown
5
+ import mkdocs_pycafe
6
+ import pymdownx.superfences
4
7
 
5
8
  import solara
9
+ from solara.components.markdown import formatter, _no_deep_copy_emojione
6
10
 
7
11
 
8
12
  # We want to separate metadata from the markdown files before rendering them, which solara.Markdown doesn't support
9
13
  @solara.component
10
14
  def MarkdownWithMetadata(content: str, unsafe_solara_execute=True):
15
+ cleanups = solara.use_ref(cast(List[Callable[[], None]], []))
11
16
  if "---" in content:
12
17
  pre_content, raw_metadata, post_content = content.split("---")
13
18
  metadata: Dict[str, Union[str, List[str]]] = yaml.safe_load(raw_metadata)
@@ -27,12 +32,65 @@ def MarkdownWithMetadata(content: str, unsafe_solara_execute=True):
27
32
  solara.Meta(property=key, content=value)
28
33
  else:
29
34
  solara.Meta(name=key, content=value)
35
+
36
+ def make_markdown_object():
37
+ return markdown.Markdown( # type: ignore
38
+ extensions=[
39
+ "pymdownx.highlight",
40
+ "pymdownx.superfences",
41
+ "pymdownx.emoji",
42
+ "toc", # so we get anchors for h1 h2 etc
43
+ "tables",
44
+ ],
45
+ extension_configs={
46
+ "pymdownx.emoji": {
47
+ "emoji_index": _no_deep_copy_emojione,
48
+ },
49
+ "pymdownx.superfences": {
50
+ "custom_fences": [
51
+ {
52
+ "name": "mermaid",
53
+ "class": "mermaid",
54
+ "format": pymdownx.superfences.fence_div_format,
55
+ },
56
+ {
57
+ "name": "solara",
58
+ "class": "",
59
+ "validator": mkdocs_pycafe.validator,
60
+ "format": mkdocs_pycafe.formatter(
61
+ type="solara", next_formatter=formatter(unsafe_solara_execute, cleanups.current), inside_last_div=False
62
+ ),
63
+ },
64
+ {
65
+ "name": "python",
66
+ "class": "highlight",
67
+ "validator": mkdocs_pycafe.validator,
68
+ "format": mkdocs_pycafe.formatter(
69
+ type="solara", next_formatter=formatter(unsafe_solara_execute, cleanups.current), inside_last_div=False
70
+ ),
71
+ },
72
+ ],
73
+ },
74
+ },
75
+ )
76
+
77
+ md_parser = solara.use_memo(make_markdown_object, dependencies=[unsafe_solara_execute])
78
+
79
+ def cleanup_wrapper():
80
+ def cleanup():
81
+ for cleanup in cleanups.current:
82
+ cleanup()
83
+
84
+ return cleanup
85
+
86
+ solara.use_effect(cleanup_wrapper, [])
87
+
30
88
  with solara.v.Html(
31
89
  tag="div",
32
90
  style_="display: flex; flex-direction: row; justify-content: center; gap: 15px; max-width: 90%; margin: 0 auto;",
33
91
  attributes={"id": "markdown-to-navigate"},
34
92
  ):
35
- solara.Markdown(content, unsafe_solara_execute=unsafe_solara_execute, style="flex-grow: 1; max-width: min(100%, 1024px);")
93
+ solara.Markdown(content, unsafe_solara_execute=unsafe_solara_execute, style="flex-grow: 1; max-width: min(100%, 1024px);", md_parser=md_parser)
36
94
  MarkdownNavigation(id="markdown-to-navigate").key("markdown-nav" + str(hash(content)))
37
95
 
38
96
 
@@ -1,5 +1,22 @@
1
1
  # Solara Changelog
2
2
 
3
+ ## Version 1.42.0
4
+ * Feature: Mutation detection is now available under the `SOLARA_STORAGE_MUTATION_DETECTION` environmental variable. [#595](https://github.com/widgetti/solara/pull/595).
5
+ * Feature: Autofocusing text inputs is now supported. [#788](https://github.com/widgetti/solara/pull/788).
6
+ * Feature: Custom colours are now supported for the Solara loading spinner. [#858](https://github.com/widgetti/solara/pull/858)
7
+ * Bug Fix: Echarts responsive size is now properly supported. [#273](https://github.com/widgetti/solara/pull/273).
8
+ * Bug Fix: Some version checks would prevent Solara from starting. [#904](https://github.com/widgetti/solara/pull/904).
9
+ * Bug Fix: Solara apps running in qt mode (`--qt`) should now always work correctly. [#856](https://github.com/widgetti/solara/pull/856).
10
+ * Bug Fix: Hot reloading of files outside working directory would crash app. [069a205](https://github.com/widgetti/solara/commit/069a205c88a8cbcb0b0ca23f4d56889c8ad6134a) and [#869](https://github.com/widgetti/solara/pull/869).
11
+
12
+ ## Version 1.41.0
13
+ * Feature: Support automatic resizing of Altair (Vega-Lite) figures. [#833](https://github.com/widgetti/solara/pull/833).
14
+ * Feature (Experimental): Support running Solara applications as standalone QT apps. [#835](https://github.com/widgetti/solara/pull/835).
15
+ * Feature: Add option to hide "This website runs on Solara"-banner. [#836](https://github.com/widgetti/solara/pull/836).
16
+ * Feature: Support navigating to hashes. [#814](https://github.com/widgetti/solara/pull/814).
17
+ * Bug Fix: Chunks and assets in nbextensions would fail to load. [9efe26c](https://github.com/widgetti/solara/commit/9efe26cbe00210163a6e8ef251ebfe50ca87fce2).
18
+ * Bug Fix: Vue widget decorator now always uses absolute paths. [#826](https://github.com/widgetti/solara/pull/826).
19
+
3
20
  ## Version 1.40.0
4
21
  * Feature: In Jupyter Notebook and Lab, Solara (server) now renders the [ipypopout](https://github.com/widgetti/ipypopout) window instead of Voila [#805](render ipypopout content in jupyter notebook and lab)
5
22
  * Feature: Support styling input field of [ChatInput component](https://solara.dev/documentation/components/lab/chat). [#800](https://github.com/widgetti/solara/pull/800).
@@ -51,6 +51,7 @@ browser pages. This can be used to store state that outlives a page refresh.
51
51
  We recommend storing the state in an external database, especially in the case of multiple workers/nodes. If you want to store state associated to a session in-memory, make sure to set up sticky sessions.
52
52
 
53
53
 
54
+ The `solara-session-id` cookie is accessible in the browser using JavaScript. If you deem this a security risk, you can disable the cookie by setting the `SOLARA_SESSION_HTTP_ONLY` environment variable to `True`.
54
55
 
55
56
 
56
57
  ## Readiness check
@@ -78,7 +79,16 @@ $ curl http://localhost:8765/resourcez\?verbose
78
79
 
79
80
  The JSON format may be subject to change.
80
81
 
82
+ ## Ignoring notebook extensions
81
83
 
84
+ Not all (classic) jupyter notebook extensions are compatible with Solara, and there is not way to distinguish between notebook extensions that are needed for widgets and those that are not.
85
+ To ignore notebook extensions, you can set the `SOLARA_SERVER_IGNORE_NBEXTENSIONS` environment variable. This is a comma separated list of notebook extensions to ignore. For example, to ignore the `dash/main` and `foo/bar` extensions, you can run:
86
+
87
+ ```bash
88
+ $ SOLARA_SERVER_IGNORE_NBEXTENSIONS="dash/main,foo/bar" solara run nogit/sol.py -a
89
+ ```
90
+
91
+ Note that these error are not fatal, and the Solara app will still run.
82
92
 
83
93
  ## Production mode
84
94
 
@@ -13,11 +13,10 @@ df = plotly.data.gapminder()
13
13
  @solara.component
14
14
  def Page():
15
15
  solara.provide_cross_filter()
16
- with solara.VBox() as main:
17
- solara.CrossFilterReport(df, classes=["py-2"])
18
- solara.CrossFilterSelect(df, "country")
19
- solara.CrossFilterDataFrame(df)
20
- return main
16
+
17
+ solara.CrossFilterReport(df, classes=["py-2"])
18
+ solara.CrossFilterSelect(df, "country")
19
+ solara.CrossFilterDataFrame(df)
21
20
 
22
21
 
23
22
  __doc__ += apidoc(solara.CrossFilterDataFrame.f) # type: ignore
@@ -12,11 +12,9 @@ df = plotly.data.gapminder()
12
12
  @solara.component
13
13
  def Page():
14
14
  solara.provide_cross_filter()
15
- with solara.VBox() as main:
16
- solara.CrossFilterReport(df, classes=["py-2"])
17
- solara.CrossFilterSelect(df, "country")
18
- solara.CrossFilterSlider(df, "gdpPercap", mode=">")
19
- return main
15
+ solara.CrossFilterReport(df, classes=["py-2"])
16
+ solara.CrossFilterSelect(df, "country")
17
+ solara.CrossFilterSlider(df, "gdpPercap", mode=">")
20
18
 
21
19
 
22
20
  __doc__ += apidoc(solara.CrossFilterReport.f) # type: ignore
@@ -12,11 +12,9 @@ df = plotly.data.tips()
12
12
  @solara.component
13
13
  def Page():
14
14
  solara.provide_cross_filter()
15
- with solara.VBox() as main:
16
- solara.CrossFilterReport(df, classes=["py-2"])
17
- solara.CrossFilterSelect(df, "sex")
18
- solara.CrossFilterSelect(df, "time")
19
- return main
15
+ solara.CrossFilterReport(df, classes=["py-2"])
16
+ solara.CrossFilterSelect(df, "sex")
17
+ solara.CrossFilterSelect(df, "time")
20
18
 
21
19
 
22
20
  __doc__ += apidoc(solara.CrossFilterSelect.f) # type: ignore
@@ -12,11 +12,9 @@ df = plotly.data.gapminder()
12
12
  @solara.component
13
13
  def Page():
14
14
  solara.provide_cross_filter()
15
- with solara.VBox() as main:
16
- solara.CrossFilterReport(df, classes=["py-2"])
17
- solara.CrossFilterSlider(df, "pop", mode=">")
18
- solara.CrossFilterSlider(df, "gdpPercap", mode="<")
19
- return main
15
+ solara.CrossFilterReport(df, classes=["py-2"])
16
+ solara.CrossFilterSlider(df, "pop", mode=">")
17
+ solara.CrossFilterSlider(df, "gdpPercap", mode="<")
20
18
 
21
19
 
22
20
  __doc__ += apidoc(solara.CrossFilterSlider.f) # type: ignore
@@ -15,11 +15,9 @@ df = plotly.data.gapminder()
15
15
  @solara.component
16
16
  def Page():
17
17
  solara.provide_cross_filter()
18
- with solara.VBox() as main:
19
- solara.CrossFilterReport(df, classes=["py-2"])
20
- solara.CrossFilterSelect(df, "continent")
21
- solara.CrossFilterSlider(df, "gdpPercap", mode=">")
22
- return main
18
+ solara.CrossFilterReport(df, classes=["py-2"])
19
+ solara.CrossFilterSelect(df, "continent")
20
+ solara.CrossFilterSlider(df, "gdpPercap", mode=">")
23
21
 
24
22
 
25
23
  __doc__ += apidoc(solara.use_cross_filter) # type: ignore
@@ -18,16 +18,14 @@ def Page():
18
18
  value_previous = solara.use_previous(value)
19
19
  exception, clear_exception = solara.use_exception()
20
20
  # print(exception)
21
- with solara.VBox() as main:
22
- if exception:
21
+ if exception:
23
22
 
24
- def reset():
25
- set_value(value_previous)
26
- clear_exception()
23
+ def reset():
24
+ set_value(value_previous)
25
+ clear_exception()
27
26
 
28
- solara.Text("Exception: " + str(exception))
29
- solara.Button(label="Go to previous state", on_click=reset)
30
- else:
31
- solara.IntSlider(value=value, min=0, max=10, on_value=set_value, label="Pick a number, except 3")
32
- UnstableComponent(value)
33
- return main
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)
@@ -20,14 +20,11 @@ title = "use_previous"
20
20
  def Page():
21
21
  value, set_value = solara.use_state(4)
22
22
  value_previous = solara.use_previous(value)
23
- with solara.VBox() as main:
24
- solara.IntSlider("value", value=value, on_value=set_value)
25
- solara.Markdown(
26
- f"""
27
- **Current**: `{value}`
23
+ solara.IntSlider("value", value=value, on_value=set_value)
24
+ solara.Markdown(
25
+ f"""
26
+ **Current**: `{value}`
28
27
 
29
- **Previous**: `{value_previous}`
28
+ **Previous**: `{value_previous}`
30
29
  """
31
- )
32
-
33
- return main
30
+ )
@@ -41,29 +41,26 @@ def Page():
41
41
  parent_value, set_parent_value = solara.use_state(4)
42
42
  # used to force rerenders
43
43
  rerender_counter, set_rerender_counter = solara.use_state(4)
44
- with solara.VBox() as main:
45
- with solara.Card("Parent value selection"):
46
- solara.Info("This slider value gets passed down to the child components")
47
- solara.IntSlider("parent value", value=parent_value, on_value=set_parent_value)
48
- solara.Button("Force redraw", on_click=lambda: set_rerender_counter(rerender_counter + 1))
49
-
50
- with solara.Card("Child without state"):
51
- solara.Info("This child will simply render the value passed into the argument, a redraw will reset it to its parent value.")
52
- SliderWithoutState(parent_value)
53
-
54
- with solara.Card("Child with state"):
55
- solara.Info("This child will not care about the value passed into the prop, it manages its own state.")
56
- SliderWithState(parent_value)
57
-
58
- with solara.Card("Child with state (or update)"):
59
- solara.Info("This child will update when the passes in a new value, but a redraw will not reset it.")
60
- SliderWithStateOrUpdate(parent_value)
61
-
62
- with solara.Card("Child with state + key"):
63
- solara.Info(
64
- "We can also use the `.key(...)` method to force the component to forget its state, this will however cause the widget to be re-created"
65
- "(a performance penalty)."
66
- )
67
- SliderWithState(parent_value).key(f"slider-{parent_value}")
68
-
69
- return main
44
+ with solara.Card("Parent value selection"):
45
+ solara.Info("This slider value gets passed down to the child components")
46
+ solara.IntSlider("parent value", value=parent_value, on_value=set_parent_value)
47
+ solara.Button("Force redraw", on_click=lambda: set_rerender_counter(rerender_counter + 1))
48
+
49
+ with solara.Card("Child without state"):
50
+ solara.Info("This child will simply render the value passed into the argument, a redraw will reset it to its parent value.")
51
+ SliderWithoutState(parent_value)
52
+
53
+ with solara.Card("Child with state"):
54
+ solara.Info("This child will not care about the value passed into the prop, it manages its own state.")
55
+ SliderWithState(parent_value)
56
+
57
+ with solara.Card("Child with state (or update)"):
58
+ solara.Info("This child will update when the passes in a new value, but a redraw will not reset it.")
59
+ SliderWithStateOrUpdate(parent_value)
60
+
61
+ with solara.Card("Child with state + key"):
62
+ solara.Info(
63
+ "We can also use the `.key(...)` method to force the component to forget its state, this will however cause the widget to be re-created"
64
+ "(a performance penalty)."
65
+ )
66
+ SliderWithState(parent_value).key(f"slider-{parent_value}")
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  from typing import Optional, cast
4
4
 
5
5
  import solara
6
- from solara.alias import rv, rw
6
+ from solara.alias import rw
7
7
 
8
8
  HERE = Path(__file__).parent
9
9
  title = "use_thread"
@@ -29,16 +29,14 @@ def Page():
29
29
  # work will be cancelled/restarted every time the dependency changes
30
30
  result: solara.Result[bool] = solara.use_thread(work, dependencies=[number])
31
31
 
32
- with solara.VBox() as main:
33
- rw.IntText(value=number, on_value=set_number)
34
- if result.state == solara.ResultState.FINISHED:
35
- if result.value:
36
- solara.Success(f"{number} is a prime!")
37
- else:
38
- solara.Error(f"{number} is not a prime, it can be divided by {proof} ")
39
- elif result.state == solara.ResultState.ERROR:
40
- solara.Error(f"Error occurred: {result.error}")
32
+ rw.IntText(value=number, on_value=set_number)
33
+ if result.state == solara.ResultState.FINISHED:
34
+ if result.value:
35
+ solara.Success(f"{number} is a prime!")
41
36
  else:
42
- solara.Info(f"Running... (status = {result.state})")
43
- rv.ProgressLinear(indeterminate=True)
44
- return main
37
+ solara.Error(f"{number} is not a prime, it can be divided by {proof} ")
38
+ elif result.state == solara.ResultState.ERROR:
39
+ solara.Error(f"Error occurred: {result.error}")
40
+ else:
41
+ solara.Info(f"Running... (status = {result.state})")
42
+ solara.v.ProgressLinear(indeterminate=True)
@@ -14,18 +14,16 @@ routes = [
14
14
  @solara.component
15
15
  def Page():
16
16
  route_current, routes = solara.use_route()
17
- with solara.VBox() as main:
18
- # solara.Warning("Note the address bar in the browser. It should change to the path of the link.")
19
- 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.*")
20
- with solara.VBox():
21
- for route in routes:
22
- with solara.Link(route):
23
- current = route_current is route
24
- if current:
25
- solara.Success(f"You are at solara.Route(path={route.path!r})")
26
- else:
27
- solara.Info(f"Go to solara.Route(path={route.path!r})")
28
- return main
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})")
29
27
 
30
28
 
31
29
  __doc__ += apidoc(solara.Route, full=True) # type: ignore
@@ -23,43 +23,39 @@ def Fruit():
23
23
  solara.Button("Choose a fruit, I recomment banana")
24
24
  return main
25
25
 
26
- with solara.VBox() as main:
27
- with solara.HBox():
28
- for route_fruit in routes[1:]:
29
- with solara.Link(solara.resolve_path(route_fruit)):
30
- solara.Button(route_fruit.path)
26
+ with solara.Row():
27
+ for route_fruit in routes[1:]:
28
+ with solara.Link(solara.resolve_path(route_fruit)):
29
+ solara.Button(route_fruit.path)
31
30
 
32
- with solara.Link("/documentation/api/routing/use_route/fruit/nofruit", nofollow=True):
33
- solara.Button("Wrong fruit")
34
- with solara.Link("/documentation/api/routing/use_route/not-routed", nofollow=True):
35
- solara.Button("Wrong url")
36
- solara.Success(f"You chose {route.path}")
37
- return main
31
+ with solara.Link("/documentation/api/routing/use_route/fruit/nofruit", nofollow=True):
32
+ solara.Button("Wrong fruit")
33
+ with solara.Link("/documentation/api/routing/use_route/not-routed", nofollow=True):
34
+ solara.Button("Wrong url")
35
+ solara.Success(f"You chose {route.path}")
38
36
 
39
37
 
40
38
  @solara.component
41
39
  def Page():
42
40
  # this gets the top level routes, '/' and 'fruit'
43
41
  route_current, routes_all = solara.use_route()
44
- with solara.VBox() as main:
45
- with solara.Card("Navigation using buttons"):
46
- with solara.HBox():
47
- for route in routes_all:
48
- with solara.Link(route):
49
- solara.Button(route.path, color="red" if route_current == route else None)
50
- with solara.Card("Content decided by route:"):
51
- if route_current is None:
52
- solara.Error("Page does not exist")
53
- with solara.Link("fruit/kiwi"):
54
- solara.Button("Go to fruit/kiwi")
55
- elif route_current.path == "/":
56
- with solara.Link("fruit/banana"):
57
- solara.Button("Go to fruit/banana")
58
- elif route_current.path == "fruit":
59
- Fruit()
60
- else:
61
- solara.Error(f"Unknown route: {route_current.path}")
62
- return main
42
+ with solara.Card("Navigation using buttons"):
43
+ with solara.Row():
44
+ for route in routes_all:
45
+ with solara.Link(route):
46
+ solara.Button(route.path, color="red" if route_current == route else None)
47
+ with solara.Card("Content decided by route:"):
48
+ if route_current is None:
49
+ solara.Error("Page does not exist")
50
+ with solara.Link("fruit/kiwi"):
51
+ solara.Button("Go to fruit/kiwi")
52
+ elif route_current.path == "/":
53
+ with solara.Link("fruit/banana"):
54
+ solara.Button("Go to fruit/banana")
55
+ elif route_current.path == "fruit":
56
+ Fruit()
57
+ else:
58
+ solara.Error(f"Unknown route: {route_current.path}")
63
59
 
64
60
 
65
61
  routes = [
@@ -19,6 +19,23 @@ The return value of on_kernel_start is a cleanup function that will remove the c
19
19
 
20
20
  During hot reload, the callbacks that are added from scripts or modules that will be reloaded will be removed before the app is loaded
21
21
  again. This can cause the order of the callbacks to be different than at first run.
22
+
23
+ ## Example
24
+
25
+ ```
26
+ import solara
27
+ import solara.lab
28
+
29
+
30
+ @solara.lab.on_kernel_start
31
+ def on_kernel_start():
32
+ id = solara.get_kernel_id()
33
+ print("Kernel started", id)
34
+ def cleanup():
35
+ print("Kernel stopped", id)
36
+ return cleanup # this function will be called on kernel shutdown
37
+ ```
38
+
22
39
  """
23
40
 
24
41
  from solara.website.components import NoPage
@@ -14,14 +14,12 @@ routes = [
14
14
  @solara.component
15
15
  def Page():
16
16
  route_current, routes = solara.use_route()
17
- with solara.VBox() as main:
18
- solara.Info("Note the address bar in the browser. It should change to the path of the link.")
19
- with solara.HBox():
20
- for route in routes:
21
- with solara.Link(route):
22
- current = route_current is route
23
- solara.Button(f"Go to {route.path}", color="red" if current else None)
24
- return main
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)
25
23
 
26
24
 
27
25
  __doc__ += apidoc(solara.Link.f) # type: ignore
@@ -6,15 +6,12 @@ from solara.website.utils import apidoc
6
6
 
7
7
  @solara.component
8
8
  def Page():
9
- with solara.VBox() as main:
10
- solara.Info("Nothing to see here, only in this page's source code, or by looking at the google search results for this page.")
11
- with solara.Head():
12
- solara.Meta(
13
- name="description",
14
- 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.",
15
- )
16
-
17
- return main
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
+ )
18
15
 
19
16
 
20
17
  __doc__ += apidoc(solara.Meta.f) # type: ignore
@@ -26,20 +26,18 @@ def Page():
26
26
  }
27
27
  """
28
28
 
29
- with solara.VBox() as main:
30
- solara.Checkbox(label="Use CSS", value=insert_css, on_value=set_insert_css)
31
- solara.Markdown(
32
- f"""
29
+ solara.Checkbox(label="Use CSS", value=insert_css, on_value=set_insert_css)
30
+ solara.Markdown(
31
+ f"""
33
32
  ## CSS Example that styles the button below
34
33
  ```css
35
34
  {css}
36
35
  ```
37
36
  """
38
- )
39
- if insert_css:
40
- solara.Style(css)
41
- solara.Button(label="Advanced users might want to style this", icon_name="mdi-thumb-up", classes=["mybutton"])
42
- return main
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"])
43
41
 
44
42
 
45
43
  __doc__ += apidoc(solara.Style.f) # type: ignore
@@ -13,20 +13,18 @@ def Page():
13
13
  path, set_path = solara.use_state(cast(Optional[Path], None))
14
14
  directory, set_directory = solara.use_state(Path("~").expanduser())
15
15
 
16
- with solara.VBox() as main:
17
- can_select = solara.ui_checkbox("Enable select")
18
-
19
- def reset_path():
20
- set_path(None)
21
- set_file(None)
22
-
23
- # reset path and file when can_select changes
24
- solara.use_memo(reset_path, [can_select])
25
- solara.FileBrowser(directory, on_directory_change=set_directory, on_path_select=set_path, on_file_open=set_file, can_select=can_select)
26
- solara.Info(f"You are in directory: {directory}")
27
- solara.Info(f"You selected path: {path}")
28
- solara.Info(f"You opened file: {file}")
29
- return main
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}")
30
28
 
31
29
 
32
30
  __doc__ += apidoc(solara.FileBrowser.f) # type: ignore