solara-ui 1.47.0__py3-none-any.whl → 1.49.0__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 +1 -1
- solara/components/file_browser.py +62 -11
- solara/components/file_drop.vue +1 -1
- solara/server/app.py +1 -1
- solara/server/static/solara_bootstrap.py +1 -1
- solara/tasks.py +80 -12
- solara/toestand.py +1 -0
- solara/website/pages/documentation/components/input/file_browser.py +21 -13
- solara/website/pages/showcase/__init__.py +1 -1
- solara/website/pages/showcase/planeto_tessa.py +1 -1
- solara/widgets/vue/gridlayout.vue +2 -2
- solara/widgets/widgets.py +2 -0
- {solara_ui-1.47.0.dist-info → solara_ui-1.49.0.dist-info}/METADATA +1 -1
- {solara_ui-1.47.0.dist-info → solara_ui-1.49.0.dist-info}/RECORD +18 -18
- {solara_ui-1.47.0.data → solara_ui-1.49.0.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
- {solara_ui-1.47.0.data → solara_ui-1.49.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
- {solara_ui-1.47.0.dist-info → solara_ui-1.49.0.dist-info}/WHEEL +0 -0
- {solara_ui-1.47.0.dist-info → solara_ui-1.49.0.dist-info}/licenses/LICENSE +0 -0
solara/__init__.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from os.path import isfile, join
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Callable, Dict, List, Optional, Union, cast
|
|
4
|
+
from typing import Callable, Dict, List, Optional, TypeVar, Union, cast
|
|
5
|
+
import logging
|
|
5
6
|
|
|
6
7
|
import humanize
|
|
7
8
|
import ipyvuetify as vy
|
|
@@ -10,6 +11,9 @@ import traitlets
|
|
|
10
11
|
import solara
|
|
11
12
|
from solara.components import Div
|
|
12
13
|
|
|
14
|
+
T = TypeVar("T")
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
13
17
|
|
|
14
18
|
def list_dir(path, filter: Callable[[Path], bool] = lambda x: True, directory_first: bool = False) -> List[dict]:
|
|
15
19
|
def mk_item(n):
|
|
@@ -48,9 +52,30 @@ class FileListWidget(vy.VuetifyTemplate):
|
|
|
48
52
|
return name in [k["name"] for k in self.files]
|
|
49
53
|
|
|
50
54
|
|
|
55
|
+
def use_reactive_or_value(
|
|
56
|
+
value: Union[T, solara.Reactive[T]], on_value: Optional[Callable[[T], None]] = None, value_name="value", on_value_name="on_value", use_internal_value=False
|
|
57
|
+
):
|
|
58
|
+
def hookup_on_value():
|
|
59
|
+
if isinstance(value, solara.Reactive) and on_value:
|
|
60
|
+
return value.subscribe(on_value)
|
|
61
|
+
|
|
62
|
+
solara.use_effect(hookup_on_value, [isinstance(value, solara.Reactive), on_value])
|
|
63
|
+
internal_value, set_internal_value = solara.use_state(value.value if isinstance(value, solara.Reactive) else value)
|
|
64
|
+
if use_internal_value:
|
|
65
|
+
return internal_value, set_internal_value
|
|
66
|
+
if isinstance(value, solara.Reactive):
|
|
67
|
+
return value.value, value.set
|
|
68
|
+
elif on_value:
|
|
69
|
+
return value, on_value
|
|
70
|
+
else:
|
|
71
|
+
logger.warning("You should provide an %s callback if you are not using a reactive value, otherwise %s input will not update", on_value_name, value_name)
|
|
72
|
+
return value, lambda x: None
|
|
73
|
+
|
|
74
|
+
|
|
51
75
|
@solara.component
|
|
52
76
|
def FileBrowser(
|
|
53
77
|
directory: Union[None, str, Path, solara.Reactive[Path]] = None,
|
|
78
|
+
selected: Union[None, Path, solara.Reactive[Optional[Path]]] = None,
|
|
54
79
|
on_directory_change: Optional[Callable[[Path], None]] = None,
|
|
55
80
|
on_path_select: Optional[Callable[[Optional[Path]], None]] = None,
|
|
56
81
|
on_file_open: Optional[Callable[[Path], None]] = None,
|
|
@@ -75,7 +100,8 @@ def FileBrowser(
|
|
|
75
100
|
|
|
76
101
|
## Arguments
|
|
77
102
|
|
|
78
|
-
* `directory`: The directory to start in. If `None
|
|
103
|
+
* `directory`: The directory to start in. If `None`, the current working directory is used.
|
|
104
|
+
* `selected`: The selected file or directory. If `None`, no file or directory is selected (requires `can_select=True`).
|
|
79
105
|
* `on_directory_change`: Depends on mode, see above.
|
|
80
106
|
* `on_path_select`: Depends on mode, see above.
|
|
81
107
|
* `on_file_open`: Depends on mode, see above.
|
|
@@ -90,13 +116,30 @@ def FileBrowser(
|
|
|
90
116
|
directory = os.getcwd() # pragma: no cover
|
|
91
117
|
if isinstance(directory, str):
|
|
92
118
|
directory = Path(directory)
|
|
119
|
+
# directory = directory.resolve()
|
|
93
120
|
current_dir = solara.use_reactive(directory)
|
|
94
|
-
selected, set_selected = solara.use_state(None)
|
|
95
121
|
double_clicked, set_double_clicked = solara.use_state(None)
|
|
96
122
|
warning, set_warning = solara.use_state(cast(Optional[str], None))
|
|
97
123
|
scroll_pos_stack, set_scroll_pos_stack = solara.use_state(cast(List[int], []))
|
|
98
124
|
scroll_pos, set_scroll_pos = solara.use_state(0)
|
|
99
|
-
|
|
125
|
+
selected_private, set_selected_private = use_reactive_or_value(
|
|
126
|
+
selected,
|
|
127
|
+
on_value=on_path_select if can_select else lambda x: None,
|
|
128
|
+
value_name="selected",
|
|
129
|
+
on_value_name="on_path_select",
|
|
130
|
+
use_internal_value=not can_select,
|
|
131
|
+
)
|
|
132
|
+
# remove so we don't accidentally use it
|
|
133
|
+
del selected
|
|
134
|
+
|
|
135
|
+
def sync_directory_from_selected():
|
|
136
|
+
if selected_private is not None:
|
|
137
|
+
# if we select a file, we need to make sure the directory is correct
|
|
138
|
+
# NOTE: although we expect a Path, abuse might make it a string
|
|
139
|
+
if isinstance(selected_private, Path):
|
|
140
|
+
current_dir.value = selected_private.resolve().parent
|
|
141
|
+
|
|
142
|
+
solara.use_effect(sync_directory_from_selected, [selected_private])
|
|
100
143
|
|
|
101
144
|
def change_dir(new_dir: Path):
|
|
102
145
|
if os.access(new_dir, os.R_OK):
|
|
@@ -114,14 +157,14 @@ def FileBrowser(
|
|
|
114
157
|
on_path_select(None)
|
|
115
158
|
return
|
|
116
159
|
if item["name"] == "..":
|
|
117
|
-
new_dir = current_dir.value.parent
|
|
160
|
+
new_dir = current_dir.value.resolve().parent
|
|
118
161
|
action_change_directory = (can_select and double_click) or (not can_select and not double_click)
|
|
119
162
|
if action_change_directory and change_dir(new_dir):
|
|
120
163
|
if scroll_pos_stack:
|
|
121
164
|
last_pos = scroll_pos_stack[-1]
|
|
122
165
|
set_scroll_pos_stack(scroll_pos_stack[:-1])
|
|
123
166
|
set_scroll_pos(last_pos)
|
|
124
|
-
|
|
167
|
+
set_selected_private(None)
|
|
125
168
|
set_double_clicked(None)
|
|
126
169
|
if on_path_select and can_select:
|
|
127
170
|
on_path_select(None)
|
|
@@ -142,7 +185,7 @@ def FileBrowser(
|
|
|
142
185
|
if change_dir(path):
|
|
143
186
|
set_scroll_pos_stack(scroll_pos_stack + [scroll_pos])
|
|
144
187
|
set_scroll_pos(0)
|
|
145
|
-
|
|
188
|
+
set_selected_private(None)
|
|
146
189
|
set_double_clicked(None)
|
|
147
190
|
if on_path_select and can_select:
|
|
148
191
|
on_path_select(None)
|
|
@@ -153,7 +196,7 @@ def FileBrowser(
|
|
|
153
196
|
raise RuntimeError("Combination should not happen") # pragma: no cover
|
|
154
197
|
|
|
155
198
|
def on_click(item):
|
|
156
|
-
|
|
199
|
+
set_selected_private(item["name"] if item else None)
|
|
157
200
|
on_item(item, False)
|
|
158
201
|
|
|
159
202
|
def on_double_click(item):
|
|
@@ -163,12 +206,20 @@ def FileBrowser(
|
|
|
163
206
|
# otherwise we can ignore it, single click will handle it
|
|
164
207
|
|
|
165
208
|
files = [{"name": "..", "is_file": False}] + list_dir(current_dir.value, filter=filter, directory_first=directory_first)
|
|
209
|
+
clicked = (
|
|
210
|
+
{
|
|
211
|
+
"name": selected_private.name if isinstance(selected_private, Path) else selected_private,
|
|
212
|
+
"is_file": isinstance(selected_private, Path),
|
|
213
|
+
"size": None,
|
|
214
|
+
}
|
|
215
|
+
if selected_private is not None
|
|
216
|
+
else None
|
|
217
|
+
)
|
|
166
218
|
with Div(class_="solara-file-browser") as main:
|
|
167
|
-
Div(children=[str(current_dir.value)])
|
|
219
|
+
Div(children=[str(current_dir.value.resolve())])
|
|
168
220
|
FileListWidget.element(
|
|
169
221
|
files=files,
|
|
170
|
-
|
|
171
|
-
clicked=selected,
|
|
222
|
+
clicked=clicked,
|
|
172
223
|
on_clicked=on_click,
|
|
173
224
|
double_clicked=double_clicked,
|
|
174
225
|
on_double_clicked=on_double_click,
|
solara/components/file_drop.vue
CHANGED
solara/server/app.py
CHANGED
|
@@ -136,7 +136,7 @@ class AppScript:
|
|
|
136
136
|
routes = solara.generate_routes_directory(self.path)
|
|
137
137
|
|
|
138
138
|
if any(name for name in sys.modules.keys() if name.startswith(self.name)):
|
|
139
|
-
logger.
|
|
139
|
+
logger.warning(
|
|
140
140
|
f"Directory {self.name} is also used as a package. This can cause modules to be loaded twice, and might "
|
|
141
141
|
"cause unexpected behavior. If you run solara from a different directory (e.g. the parent directory) you "
|
|
142
142
|
"can avoid this ambiguity."
|
|
@@ -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.49.0-py2.py3-none-any.whl", keep_going=True)
|
|
123
123
|
import solara
|
|
124
124
|
|
|
125
125
|
el = solara.Warning("lala")
|
solara/tasks.py
CHANGED
|
@@ -6,6 +6,7 @@ import functools
|
|
|
6
6
|
import inspect
|
|
7
7
|
import logging
|
|
8
8
|
import threading
|
|
9
|
+
import warnings
|
|
9
10
|
from enum import Enum
|
|
10
11
|
import typing
|
|
11
12
|
from typing import (
|
|
@@ -536,6 +537,7 @@ def task(
|
|
|
536
537
|
f: None = None,
|
|
537
538
|
*,
|
|
538
539
|
prefer_threaded: bool = ...,
|
|
540
|
+
check_for_render_context: bool = ...,
|
|
539
541
|
) -> Callable[[Callable[P, R]], Task[P, R]]: ...
|
|
540
542
|
|
|
541
543
|
|
|
@@ -544,6 +546,7 @@ def task(
|
|
|
544
546
|
f: Callable[P, Union[Coroutine[Any, Any, R], R]],
|
|
545
547
|
*,
|
|
546
548
|
prefer_threaded: bool = ...,
|
|
549
|
+
check_for_render_context: bool = ...,
|
|
547
550
|
) -> Task[P, R]: ...
|
|
548
551
|
|
|
549
552
|
|
|
@@ -551,6 +554,7 @@ def task(
|
|
|
551
554
|
f: Union[None, Callable[P, Union[Coroutine[Any, Any, R], R]]] = None,
|
|
552
555
|
*,
|
|
553
556
|
prefer_threaded: bool = True,
|
|
557
|
+
check_for_render_context: bool = True,
|
|
554
558
|
) -> Union[Callable[[Callable[P, R]], Task[P, R]], Task[P, R]]:
|
|
555
559
|
"""Decorator to turn a function or coroutine function into a task.
|
|
556
560
|
|
|
@@ -764,12 +768,76 @@ def task(
|
|
|
764
768
|
This ensures that even when a coroutine functions calls a blocking function the UI is still responsive.
|
|
765
769
|
On platform where threads are not supported (like Pyodide / WASM / Emscripten / PyScript), a coroutine
|
|
766
770
|
function will always run in the current event loop.
|
|
771
|
+
- `check_for_render_context` - bool: If true, we will check if we are in a render context, and if so, we will
|
|
772
|
+
warn you that you should probably be using `use_task` instead of `task`.
|
|
767
773
|
|
|
768
774
|
```
|
|
769
775
|
|
|
770
776
|
"""
|
|
771
777
|
|
|
778
|
+
def check_if_we_should_use_use_task():
|
|
779
|
+
import reacton.core
|
|
780
|
+
|
|
781
|
+
in_reacton_context = reacton.core.get_render_context(required=False) is not None
|
|
782
|
+
if not in_reacton_context:
|
|
783
|
+
# We are not in a reacton context, so we should not (and cannot) use use_task
|
|
784
|
+
return
|
|
785
|
+
from .toestand import _find_outside_solara_frame
|
|
786
|
+
|
|
787
|
+
frame = _find_outside_solara_frame()
|
|
788
|
+
if frame is None:
|
|
789
|
+
# We cannot determine which frame we are in, just skip this check
|
|
790
|
+
return
|
|
791
|
+
import inspect
|
|
792
|
+
|
|
793
|
+
tb = inspect.getframeinfo(frame)
|
|
794
|
+
msg = """You are calling task(...) from a component, while you should probably be using use_task.
|
|
795
|
+
|
|
796
|
+
Reason:
|
|
797
|
+
- task(...) creates a new task object on every render, and should only be used outside of a component.
|
|
798
|
+
- use_task(...) returns the same task object on every render, and should be used inside a component.
|
|
799
|
+
|
|
800
|
+
Example:
|
|
801
|
+
@solara.component
|
|
802
|
+
def Page():
|
|
803
|
+
@task # This is wrong, this creates a new task object on every render
|
|
804
|
+
def my_task():
|
|
805
|
+
...
|
|
806
|
+
|
|
807
|
+
Instead, you should do:
|
|
808
|
+
@solara.component
|
|
809
|
+
def Page():
|
|
810
|
+
@use_task
|
|
811
|
+
def my_task():
|
|
812
|
+
...
|
|
813
|
+
|
|
814
|
+
"""
|
|
815
|
+
if tb:
|
|
816
|
+
if tb.code_context:
|
|
817
|
+
code = tb.code_context[0]
|
|
818
|
+
else:
|
|
819
|
+
code = "<No code context available>"
|
|
820
|
+
msg += f"This warning was triggered from:\n{tb.filename}:{tb.lineno}\n{code.strip()}"
|
|
821
|
+
|
|
822
|
+
# Check if the call is within a use_memo context by inspecting the call stack
|
|
823
|
+
if frame:
|
|
824
|
+
caller_frame = frame.f_back
|
|
825
|
+
# Check a few frames up the stack (e.g., up to 5) for 'use_memo'
|
|
826
|
+
for _ in range(5):
|
|
827
|
+
if caller_frame is None:
|
|
828
|
+
break
|
|
829
|
+
func_name = caller_frame.f_code.co_name
|
|
830
|
+
module_name = caller_frame.f_globals.get("__name__", "")
|
|
831
|
+
if func_name == "use_memo" and (module_name.startswith("solara.") or module_name.startswith("reacton.")):
|
|
832
|
+
# We are in a use_memo (or a context that should not trigger the warning)
|
|
833
|
+
return
|
|
834
|
+
caller_frame = caller_frame.f_back
|
|
835
|
+
|
|
836
|
+
warnings.warn(msg)
|
|
837
|
+
|
|
772
838
|
def wrapper(f: Union[None, Callable[P, Union[Coroutine[Any, Any, R], R]]]) -> Task[P, R]:
|
|
839
|
+
if check_for_render_context:
|
|
840
|
+
check_if_we_should_use_use_task()
|
|
773
841
|
# we use wraps to make the key of the reactive variable more unique
|
|
774
842
|
# and less likely to mixup during hot reloads
|
|
775
843
|
assert f is not None
|
|
@@ -800,46 +868,46 @@ def use_task(
|
|
|
800
868
|
dependencies: Literal[None] = ...,
|
|
801
869
|
raise_error=...,
|
|
802
870
|
prefer_threaded=...,
|
|
803
|
-
) -> Callable[[Callable[
|
|
871
|
+
) -> Callable[[Callable[P, R]], "Task[P, R]"]: ...
|
|
804
872
|
|
|
805
873
|
|
|
806
874
|
@overload
|
|
807
875
|
def use_task(
|
|
808
|
-
f:
|
|
876
|
+
f: None = None,
|
|
809
877
|
*,
|
|
810
|
-
dependencies:
|
|
878
|
+
dependencies: List = ...,
|
|
811
879
|
raise_error=...,
|
|
812
880
|
prefer_threaded=...,
|
|
813
|
-
) -> "Task[[], R]": ...
|
|
881
|
+
) -> Callable[[Callable[[], R]], "Task[[], R]"]: ...
|
|
814
882
|
|
|
815
883
|
|
|
816
884
|
@overload
|
|
817
885
|
def use_task(
|
|
818
|
-
f:
|
|
886
|
+
f: Callable[[], R],
|
|
819
887
|
*,
|
|
820
888
|
dependencies: List = ...,
|
|
821
889
|
raise_error=...,
|
|
822
890
|
prefer_threaded=...,
|
|
823
|
-
) ->
|
|
891
|
+
) -> "Task[[], R]": ...
|
|
824
892
|
|
|
825
893
|
|
|
826
894
|
@overload
|
|
827
895
|
def use_task(
|
|
828
|
-
f: Callable[
|
|
896
|
+
f: Callable[P, R],
|
|
829
897
|
*,
|
|
830
|
-
dependencies:
|
|
898
|
+
dependencies: Literal[None] = ...,
|
|
831
899
|
raise_error=...,
|
|
832
900
|
prefer_threaded=...,
|
|
833
|
-
) -> "Task[
|
|
901
|
+
) -> "Task[P, R]": ...
|
|
834
902
|
|
|
835
903
|
|
|
836
904
|
def use_task(
|
|
837
|
-
f: Union[None, Callable[
|
|
905
|
+
f: Union[None, Callable[P, R]] = None,
|
|
838
906
|
*,
|
|
839
907
|
dependencies: Union[None, List] = [],
|
|
840
908
|
raise_error=True,
|
|
841
909
|
prefer_threaded=True,
|
|
842
|
-
) -> Union[Callable[[Callable[
|
|
910
|
+
) -> Union[Callable[[Callable[P, R]], "Task[P, R]"], "Task[P, R]"]:
|
|
843
911
|
"""A hook that runs a function or coroutine function as a task and returns the result.
|
|
844
912
|
|
|
845
913
|
Allows you to run code in the background, with the UI available to the user. This is useful for long running tasks,
|
|
@@ -919,7 +987,7 @@ def use_task(
|
|
|
919
987
|
|
|
920
988
|
def wrapper(f):
|
|
921
989
|
def create_task() -> "Task[[], R]":
|
|
922
|
-
return task(f, prefer_threaded=prefer_threaded)
|
|
990
|
+
return task(f, prefer_threaded=prefer_threaded, check_for_render_context=False)
|
|
923
991
|
|
|
924
992
|
task_instance = solara.use_memo(create_task, dependencies=[])
|
|
925
993
|
# we always update the function so we do not have stale data in the function
|
solara/toestand.py
CHANGED
|
@@ -343,6 +343,7 @@ def _is_internal_module(file_name: str):
|
|
|
343
343
|
return (
|
|
344
344
|
file_name_parts[-2:] == ["solara", "toestand.py"]
|
|
345
345
|
or file_name_parts[-2:] == ["solara", "reactive.py"]
|
|
346
|
+
or file_name_parts[-2:] == ["solara", "tasks.py"]
|
|
346
347
|
or file_name_parts[-2:] == ["solara", "_stores.py"]
|
|
347
348
|
or file_name_parts[-3:] == ["solara", "hooks", "use_reactive.py"]
|
|
348
349
|
or file_name_parts[-2:] == ["reacton", "core.py"]
|
|
@@ -2,29 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Optional, cast
|
|
5
|
-
|
|
5
|
+
import random
|
|
6
6
|
import solara
|
|
7
7
|
from solara.website.utils import apidoc
|
|
8
8
|
|
|
9
|
+
opened = solara.reactive(cast(Optional[Path], None))
|
|
10
|
+
selected = solara.reactive(cast(Optional[Path], None))
|
|
11
|
+
directory = solara.reactive(Path("~").expanduser())
|
|
12
|
+
can_select = solara.reactive(False)
|
|
13
|
+
|
|
9
14
|
|
|
10
15
|
@solara.component
|
|
11
16
|
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
17
|
def reset_path():
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
opened.value = None
|
|
19
|
+
selected.value = None
|
|
20
|
+
|
|
21
|
+
def select_random_file():
|
|
22
|
+
files = list(directory.value.glob("*"))
|
|
23
|
+
if files:
|
|
24
|
+
selected.value = random.choice(files)
|
|
21
25
|
|
|
22
26
|
# reset path and file when can_select changes
|
|
23
|
-
solara.use_memo(reset_path, [can_select])
|
|
24
|
-
solara.
|
|
27
|
+
solara.use_memo(reset_path, [can_select.value])
|
|
28
|
+
solara.Checkbox(label="Enable select", value=can_select)
|
|
29
|
+
solara.FileBrowser(directory, selected=selected, on_file_open=opened.set, can_select=can_select.value)
|
|
25
30
|
solara.Info(f"You are in directory: {directory}")
|
|
26
|
-
solara.Info(f"You selected path: {
|
|
27
|
-
solara.Info(f"You opened file: {
|
|
31
|
+
solara.Info(f"You selected path: {selected}")
|
|
32
|
+
solara.Info(f"You opened file: {opened}")
|
|
33
|
+
|
|
34
|
+
if can_select.value:
|
|
35
|
+
solara.Button(label="Select random file", on_click=select_random_file)
|
|
28
36
|
|
|
29
37
|
|
|
30
38
|
__doc__ += apidoc(solara.FileBrowser.f) # type: ignore
|
|
@@ -32,7 +32,7 @@ def Page():
|
|
|
32
32
|
with solara.Card("TESSA", style={"height": "100%"}):
|
|
33
33
|
solara.Markdown(
|
|
34
34
|
"""
|
|
35
|
-
[TESSA](https://planeto-energy.ch/
|
|
35
|
+
[TESSA](https://planeto-energy.ch/) is a tool developed by Planeto for district heating & cooling planning.
|
|
36
36
|
"""
|
|
37
37
|
)
|
|
38
38
|
with solara.Link("./planeto_tessa"):
|
|
@@ -10,7 +10,7 @@ def Page():
|
|
|
10
10
|
"""
|
|
11
11
|
# TESSA by Planeto
|
|
12
12
|
|
|
13
|
-
[Planeto](https://planeto-energy.ch/) developed a tool called
|
|
13
|
+
[Planeto](https://planeto-energy.ch/) developed a tool called TESSA for district heating & cooling planning.
|
|
14
14
|
|
|
15
15
|
TESSA was prototyped in the Jupyter notebook using ipywidgets. Using solara, they are able to bring TESSA into production using the
|
|
16
16
|
same technology stack.
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<div v-if="gridlayout_loaded" style="padding: 0px; width: 100%;">
|
|
4
4
|
<grid-layout
|
|
5
5
|
:layout.sync="grid_layout"
|
|
6
|
-
:col-num="
|
|
7
|
-
:row-height="
|
|
6
|
+
:col-num="col_num"
|
|
7
|
+
:row-height="row_height"
|
|
8
8
|
:is-draggable="draggable"
|
|
9
9
|
:is-resizable="resizable"
|
|
10
10
|
:is-mirrored="false"
|
solara/widgets/widgets.py
CHANGED
|
@@ -53,6 +53,8 @@ class GridLayout(v.VuetifyTemplate):
|
|
|
53
53
|
resizable = traitlets.CBool(True).tag(sync=True)
|
|
54
54
|
cdn = traitlets.Unicode(None, allow_none=True).tag(sync=True)
|
|
55
55
|
on_layout_updated = traitlets.traitlets.Callable(None, allow_none=True)
|
|
56
|
+
col_num = traitlets.Int(12).tag(sync=True)
|
|
57
|
+
row_height = traitlets.Int(30).tag(sync=True)
|
|
56
58
|
|
|
57
59
|
def vue_layout_updated(self, *args):
|
|
58
60
|
if self.on_layout_updated is not None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
prefix/etc/jupyter/jupyter_notebook_config.d/solara.json,sha256=3UhTBQi6z7F7pPjmqXxfddv79c8VGR9H7zStDLp6AwY,115
|
|
2
2
|
prefix/etc/jupyter/jupyter_server_config.d/solara.json,sha256=D9J-rYxAzyD5GOqWvuPjacGUVFHsYtTfZ4FUbRzRvIA,113
|
|
3
|
-
solara/__init__.py,sha256=
|
|
3
|
+
solara/__init__.py,sha256=wkvVNU53A8t1_3SujgqIpjFmcWrnOx_JyZzVmpAcpm8,3647
|
|
4
4
|
solara/__main__.py,sha256=pm79jfba-0ZapLR0PtwZfMeiTHrLz8kEt79EygRyXxQ,24828
|
|
5
5
|
solara/_stores.py,sha256=N2Ec-61XNFXwigBx8f5QYPx7gDXenCOBdmLPXiJB45E,12320
|
|
6
6
|
solara/alias.py,sha256=9vfLzud77NP8in3OID9b5mmIO8NyrnFjN2_aE0lSb1k,216
|
|
@@ -20,8 +20,8 @@ solara/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
20
20
|
solara/reactive.py,sha256=KN0PJl-ivxjgQj008zyPGnORo5bTNaY77uASsSW0mFQ,3430
|
|
21
21
|
solara/routing.py,sha256=G_iZKozdVoUuD-qSMyuPV6jeN4qBqujAUvekw036f88,9143
|
|
22
22
|
solara/settings.py,sha256=FED5SYfw1W88U8SPk9iXSgSSvMHgPkU1auONSdk5cNs,2688
|
|
23
|
-
solara/tasks.py,sha256=
|
|
24
|
-
solara/toestand.py,sha256
|
|
23
|
+
solara/tasks.py,sha256=uaauVf5zuVROWlU5KD_uoBEjy81YcpUtJhxcxX_Rs88,37197
|
|
24
|
+
solara/toestand.py,sha256=-ddVhZGmFR4qia_skg4f15xAdNaiB_9drpS-qxrQDaQ,33497
|
|
25
25
|
solara/util.py,sha256=UUO3BfhXb3tGP-uj8UuTYMx6kuph6PiDp4XXm-f6uyg,9697
|
|
26
26
|
solara/validate_hooks.py,sha256=F0CYDOVF_23O1apJBIk9lZMq11JmkoE3BrVVT8QvZWI,9999
|
|
27
27
|
solara/components/__init__.py,sha256=j5Tv0tyzs80Bsl5hvTIF_x-RQyAvr3ooqIXnABamW44,3214
|
|
@@ -43,10 +43,10 @@ solara/components/download.vue,sha256=xdh4olwVvGkQwGNRMU5eVUW1FGvcXrYrCyjddJbvH7
|
|
|
43
43
|
solara/components/echarts.py,sha256=aAedLqKuVJnBi3FwhSdQIgAn-w55sNb_hGSmqkosfuA,3069
|
|
44
44
|
solara/components/echarts.vue,sha256=7TGmxaRUmH-LeH16jHiUzyuZgebGu_JDiWsa2DBSWW4,4056
|
|
45
45
|
solara/components/figure_altair.py,sha256=t4EEwXrdoisrbV5j2MS-HBlPc5HSFCK5r4mRXvYRH9w,1150
|
|
46
|
-
solara/components/file_browser.py,sha256=
|
|
46
|
+
solara/components/file_browser.py,sha256=x4NdYmazI2NChq9x_I7lqcpFOIdX_A4avRPjoB5bvKw,9585
|
|
47
47
|
solara/components/file_download.py,sha256=Lil0qyiozU_Pxyb_HgnJXOumrxMeDwwavEmZZw6kAs8,7475
|
|
48
48
|
solara/components/file_drop.py,sha256=BA53EZsCzzPCS8i2ZH_S1NAkmmQlTOvNEoXAt0_1l4A,5239
|
|
49
|
-
solara/components/file_drop.vue,sha256=
|
|
49
|
+
solara/components/file_drop.vue,sha256=7V6YjHhoqOCVDMVPtObNwfHz2MdXLCFlNEqK1brl3zI,2243
|
|
50
50
|
solara/components/file_list_widget.vue,sha256=atp-FO9tBjvyCQ_32NqeB9Rcehg03vPLD1eIROgBDDU,1860
|
|
51
51
|
solara/components/head.py,sha256=QZRTbwaUH0trfce3ntEcOqmLjw74CbSHpuMt9gGj7oA,648
|
|
52
52
|
solara/components/head_tag.py,sha256=xPj_ug0TUAZF4yN6ypKlmLcsHORIHU8zfIZgEDNi4PQ,1591
|
|
@@ -106,7 +106,7 @@ solara/lab/utils/headers.py,sha256=RMo8JUdztRePrdNfYCX1QEhrfyF6ktodr4v6tIREKbs,2
|
|
|
106
106
|
solara/scope/__init__.py,sha256=0sP3B6L4Aai0b6nadPtEETb8XqdGmSFKvQusNH0-yvY,2987
|
|
107
107
|
solara/scope/types.py,sha256=HTf_wnkpkhhtGaeFsB690KBP623CUuqiMssd72-u9yg,1540
|
|
108
108
|
solara/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
109
|
-
solara/server/app.py,sha256=
|
|
109
|
+
solara/server/app.py,sha256=vYUyqucwz2KZwH5RCCJtQDGPJdSLYqB5dAULudSqm74,22137
|
|
110
110
|
solara/server/cdn_helper.py,sha256=mjdDWL1jVed40rEagP9L5CfZwFB77yUKqcFg--u2eds,3250
|
|
111
111
|
solara/server/esm.py,sha256=dX9pzTJQ6kd6qNHQgzC938O5LTowLhuATXO0Q1paz44,2951
|
|
112
112
|
solara/server/fastapi.py,sha256=qVIHn0_Kxr6zWqcBWySu5nnJ6pNTSDqb4EHIh-cqH_8,93
|
|
@@ -146,7 +146,7 @@ solara/server/static/highlight-dark.css,sha256=xO8-vta9vG4s1OfJNHXWqiLWzx_gM03jo
|
|
|
146
146
|
solara/server/static/highlight.css,sha256=k8ZdT5iwrGQ5tXTQHAXuxvZrSUq3kwCdEpy3mlFoZjs,2637
|
|
147
147
|
solara/server/static/main-vuetify.js,sha256=R3qM4xMlstMpRUdRaul78p34z_Av2ONSTXksg2V9TqQ,9503
|
|
148
148
|
solara/server/static/main.js,sha256=mcx4JNQ4Lg4pNdUIqMoZos1mZyYFS48yd_JNFFJUqIE,5679
|
|
149
|
-
solara/server/static/solara_bootstrap.py,sha256=
|
|
149
|
+
solara/server/static/solara_bootstrap.py,sha256=1du6V59LpN8Al8-Lrir3kR3_uG0HpcbYB25mq0hRDks,3195
|
|
150
150
|
solara/server/static/sun.svg,sha256=jEKBAGCr7b9zNYv0VUb7lMWKjnU2dX69_Ye_DZWGXJI,6855
|
|
151
151
|
solara/server/static/webworker.js,sha256=cjAFz7-SygStHJnYlJUlJs-gE_7YQeQ-WBDcmKYyjvo,1372
|
|
152
152
|
solara/server/templates/index.html.j2,sha256=JXQo1M-STFHLBOFetgG7509cAq8xUP0VAEtYDzz35fY,31
|
|
@@ -312,7 +312,7 @@ solara/website/pages/documentation/components/enterprise/avatar_menu.py,sha256=T
|
|
|
312
312
|
solara/website/pages/documentation/components/input/__init__.py,sha256=5qmC4lE7WKhZ-NCpOXfINltTKpiC0mBW86vHoGb9Mdk,179
|
|
313
313
|
solara/website/pages/documentation/components/input/button.py,sha256=MB46yBccVgFc-CM9rs1_GzuXoA2PzCfNsn3qbyguums,433
|
|
314
314
|
solara/website/pages/documentation/components/input/checkbox.py,sha256=OdbHlLOrEPc3a2R8-UY0q9e4bnCgzgatVG9yHSftNqk,187
|
|
315
|
-
solara/website/pages/documentation/components/input/file_browser.py,sha256=
|
|
315
|
+
solara/website/pages/documentation/components/input/file_browser.py,sha256=dBhvhd5PUTZql0DsvIiASOt30QtSR3xLx97xXDQVU6k,1188
|
|
316
316
|
solara/website/pages/documentation/components/input/file_drop.py,sha256=Khj_ge7vlwdTLhHqzEwWAJ7GBHTM63NvHPREv7u_Rx8,2290
|
|
317
317
|
solara/website/pages/documentation/components/input/input.py,sha256=WpHuv40mrl0yGxipcvEoF2TkBPpqyNdjHlNwEBdpg8k,1156
|
|
318
318
|
solara/website/pages/documentation/components/input/select.py,sha256=PWvUsJHSyNJNenLcj2XSMD3NnclkxcxpBk553-RXNOc,439
|
|
@@ -434,9 +434,9 @@ solara/website/pages/our_team/__init__.py,sha256=sLTqx4rw1HuXr-HEXUsexjfGn9IkVx9
|
|
|
434
434
|
solara/website/pages/pricing/__init__.py,sha256=wYlHoEE6A3fVY_K47LuS2txXJifOSHhMI9dN3-jfW8s,1362
|
|
435
435
|
solara/website/pages/roadmap/__init__.py,sha256=T7NULo-JEldhIpKaj_NQ_ka8iFGijUxk6f1LN-rvVUk,283
|
|
436
436
|
solara/website/pages/roadmap/roadmap.md,sha256=rRD9EH4mUlCQezbI52-MQBWL1mAQQJ6GUdUuasPnZiQ,3634
|
|
437
|
-
solara/website/pages/showcase/__init__.py,sha256=
|
|
437
|
+
solara/website/pages/showcase/__init__.py,sha256=p3a_6dIcHrw8kipIUz7jBM_AIPWUs0VMkfeHdHU5h68,6317
|
|
438
438
|
solara/website/pages/showcase/domino_code_assist.py,sha256=dxEbAYeZwiSx1_JHVd1dsnEqpPwiv3TcmYSonwjc-PE,2297
|
|
439
|
-
solara/website/pages/showcase/planeto_tessa.py,sha256=
|
|
439
|
+
solara/website/pages/showcase/planeto_tessa.py,sha256=DqRZO7oVEN4kJyzHTKixEILXMwJFzEUFElstSWS5Ybc,684
|
|
440
440
|
solara/website/pages/showcase/solara_dev.py,sha256=Rpjp6sMg5kZSM4z5K-oZ5T3cyAhvnQS9n8cX4seR27U,2028
|
|
441
441
|
solara/website/pages/showcase/solarathon_2023_team_2.py,sha256=NT_TffxdrOG-puaIDYVepJH2bX_yH4AaiUIWRJ6wACg,1084
|
|
442
442
|
solara/website/pages/showcase/solarathon_2023_team_4.py,sha256=F8hvevZ2wqqf8agWHjKFUEhrxCxPJkd19uJ4tv2HkAs,1078
|
|
@@ -451,14 +451,14 @@ solara/website/public/social/github.svg,sha256=XGlfnNccpMHYjfji25r5UW9FvS9pYPR1H
|
|
|
451
451
|
solara/website/public/social/twitter.svg,sha256=3Ub5a29H_NM2g7ed3689rKHU-K66PA8r3hWExpzGmdQ,430
|
|
452
452
|
solara/website/templates/index.html.j2,sha256=NYBuEHmKeSju-b3apY0h3FEJ-tnGDhrnkY-0cZ92Rro,4358
|
|
453
453
|
solara/widgets/__init__.py,sha256=D3RfEez9dllmst64_nyzzII-NZXoNMEPDnEog4ov3VE,43
|
|
454
|
-
solara/widgets/widgets.py,sha256=
|
|
455
|
-
solara/widgets/vue/gridlayout.vue,sha256=
|
|
454
|
+
solara/widgets/widgets.py,sha256=pog0XbqRxyWxLdjsthvM3G_6mIPTUaT6ZErb1POQXJg,2756
|
|
455
|
+
solara/widgets/vue/gridlayout.vue,sha256=LZk-YlqM7nv_7Y5TTq2xqfH1j2SLP1QOH5eiz7G0ayo,3584
|
|
456
456
|
solara/widgets/vue/html.vue,sha256=48K5rjp0AdJDeRV6F3nOHW3J0WXPeHn55r5pGClK2fU,112
|
|
457
457
|
solara/widgets/vue/navigator.vue,sha256=7fkX-4_YSnnMIPUMKMvQVVEzrmhY9BFAYvHMqZqTXpI,4790
|
|
458
458
|
solara/widgets/vue/vegalite.vue,sha256=zhocRsUCNIRQCEbD16er5sYnuHU0YThatRHnorA3P18,4596
|
|
459
|
-
solara_ui-1.
|
|
460
|
-
solara_ui-1.
|
|
461
|
-
solara_ui-1.
|
|
462
|
-
solara_ui-1.
|
|
463
|
-
solara_ui-1.
|
|
464
|
-
solara_ui-1.
|
|
459
|
+
solara_ui-1.49.0.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json,sha256=3UhTBQi6z7F7pPjmqXxfddv79c8VGR9H7zStDLp6AwY,115
|
|
460
|
+
solara_ui-1.49.0.data/data/etc/jupyter/jupyter_server_config.d/solara.json,sha256=D9J-rYxAzyD5GOqWvuPjacGUVFHsYtTfZ4FUbRzRvIA,113
|
|
461
|
+
solara_ui-1.49.0.dist-info/METADATA,sha256=NVpNXdQ7OF1dnnWLInqm9L7OFq-trlhtFgiF4AHgimE,7459
|
|
462
|
+
solara_ui-1.49.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
463
|
+
solara_ui-1.49.0.dist-info/licenses/LICENSE,sha256=fFJUz-CWzZ9nEc4QZKu44jMEoDr5fEW-SiqljKpD82E,1086
|
|
464
|
+
solara_ui-1.49.0.dist-info/RECORD,,
|
|
File without changes
|
{solara_ui-1.47.0.data → solara_ui-1.49.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|