streamlit-nightly 1.34.1.dev20240522__py2.py3-none-any.whl → 1.35.1.dev20240524__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.
- streamlit/__init__.py +4 -5
- streamlit/commands/logo.py +61 -13
- streamlit/config.py +9 -15
- streamlit/elements/arrow.py +141 -46
- streamlit/elements/bokeh_chart.py +9 -5
- streamlit/elements/deck_gl_json_chart.py +6 -0
- streamlit/elements/dialog_decorator.py +5 -3
- streamlit/elements/graphviz_chart.py +6 -2
- streamlit/elements/lib/built_in_chart_utils.py +4 -4
- streamlit/elements/map.py +6 -2
- streamlit/elements/plotly_chart.py +158 -39
- streamlit/elements/pyplot.py +9 -4
- streamlit/elements/utils.py +3 -6
- streamlit/elements/vega_charts.py +327 -84
- streamlit/elements/widgets/data_editor.py +17 -8
- streamlit/runtime/app_session.py +3 -3
- streamlit/runtime/caching/__init__.py +5 -0
- streamlit/runtime/caching/legacy_cache_api.py +164 -0
- streamlit/runtime/fragment.py +91 -80
- streamlit/runtime/runtime.py +0 -2
- streamlit/static/asset-manifest.json +2 -2
- streamlit/static/index.html +1 -1
- streamlit/static/static/js/{main.7e42f54d.js → main.e93f99a3.js} +2 -2
- streamlit/web/cli.py +1 -8
- streamlit/web/server/browser_websocket_handler.py +8 -4
- {streamlit_nightly-1.34.1.dev20240522.dist-info → streamlit_nightly-1.35.1.dev20240524.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.34.1.dev20240522.dist-info → streamlit_nightly-1.35.1.dev20240524.dist-info}/RECORD +32 -34
- streamlit/runtime/legacy_caching/__init__.py +0 -17
- streamlit/runtime/legacy_caching/caching.py +0 -810
- streamlit/runtime/legacy_caching/hashing.py +0 -1005
- /streamlit/static/static/js/{main.7e42f54d.js.LICENSE.txt → main.e93f99a3.js.LICENSE.txt} +0 -0
- {streamlit_nightly-1.34.1.dev20240522.data → streamlit_nightly-1.35.1.dev20240524.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.34.1.dev20240522.dist-info → streamlit_nightly-1.35.1.dev20240524.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.34.1.dev20240522.dist-info → streamlit_nightly-1.35.1.dev20240524.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.34.1.dev20240522.dist-info → streamlit_nightly-1.35.1.dev20240524.dist-info}/top_level.txt +0 -0
@@ -30,6 +30,7 @@ from streamlit.runtime.caching.cache_resource_api import (
|
|
30
30
|
CacheResourceAPI,
|
31
31
|
_resource_caches,
|
32
32
|
)
|
33
|
+
from streamlit.runtime.caching.legacy_cache_api import cache as _cache
|
33
34
|
from streamlit.runtime.state.common import WidgetMetadata
|
34
35
|
|
35
36
|
|
@@ -92,6 +93,9 @@ from streamlit.runtime.caching.cache_resource_api import (
|
|
92
93
|
# Create and export public API singletons.
|
93
94
|
cache_data = CacheDataAPI(decorator_metric_name="cache_data")
|
94
95
|
cache_resource = CacheResourceAPI(decorator_metric_name="cache_resource")
|
96
|
+
# TODO(lukasmasuch): This is the legacy cache API name which is deprecated
|
97
|
+
# and it should be removed in the future.
|
98
|
+
cache = _cache
|
95
99
|
|
96
100
|
# Deprecated singletons
|
97
101
|
_MEMO_WARNING = (
|
@@ -115,6 +119,7 @@ experimental_singleton = CacheResourceAPI(
|
|
115
119
|
|
116
120
|
|
117
121
|
__all__ = [
|
122
|
+
"cache",
|
118
123
|
"CACHE_DOCS_URL",
|
119
124
|
"save_element_message",
|
120
125
|
"save_block_message",
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""A library of caching utilities."""
|
16
|
+
|
17
|
+
from __future__ import annotations
|
18
|
+
|
19
|
+
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
20
|
+
|
21
|
+
from streamlit import deprecation_util
|
22
|
+
from streamlit.runtime.caching import CACHE_DOCS_URL
|
23
|
+
from streamlit.runtime.metrics_util import gather_metrics
|
24
|
+
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from streamlit.runtime.caching.hashing import HashFuncsDict
|
27
|
+
|
28
|
+
# Type-annotate the decorator function.
|
29
|
+
# (See https://mypy.readthedocs.io/en/stable/generics.html#decorator-factories)
|
30
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
31
|
+
|
32
|
+
|
33
|
+
@gather_metrics("cache")
|
34
|
+
def cache(
|
35
|
+
func: F | None = None,
|
36
|
+
persist: bool = False,
|
37
|
+
allow_output_mutation: bool = False,
|
38
|
+
show_spinner: bool = True,
|
39
|
+
suppress_st_warning: bool = False,
|
40
|
+
hash_funcs: HashFuncsDict | None = None,
|
41
|
+
max_entries: int | None = None,
|
42
|
+
ttl: float | None = None,
|
43
|
+
):
|
44
|
+
"""Function decorator to memoize function executions.
|
45
|
+
|
46
|
+
Parameters
|
47
|
+
----------
|
48
|
+
func : callable
|
49
|
+
The function to cache. Streamlit hashes the function and dependent code.
|
50
|
+
|
51
|
+
persist : bool
|
52
|
+
Whether to persist the cache on disk.
|
53
|
+
|
54
|
+
allow_output_mutation : bool
|
55
|
+
Streamlit shows a warning when return values are mutated, as that
|
56
|
+
can have unintended consequences. This is done by hashing the return value internally.
|
57
|
+
|
58
|
+
If you know what you're doing and would like to override this warning, set this to True.
|
59
|
+
|
60
|
+
show_spinner : bool
|
61
|
+
Enable the spinner. Default is True to show a spinner when there is
|
62
|
+
a cache miss.
|
63
|
+
|
64
|
+
suppress_st_warning : bool
|
65
|
+
Suppress warnings about calling Streamlit commands from within
|
66
|
+
the cached function.
|
67
|
+
|
68
|
+
hash_funcs : dict or None
|
69
|
+
Mapping of types or fully qualified names to hash functions. This is used to override
|
70
|
+
the behavior of the hasher inside Streamlit's caching mechanism: when the hasher
|
71
|
+
encounters an object, it will first check to see if its type matches a key in this
|
72
|
+
dict and, if so, will use the provided function to generate a hash for it. See below
|
73
|
+
for an example of how this can be used.
|
74
|
+
|
75
|
+
max_entries : int or None
|
76
|
+
The maximum number of entries to keep in the cache, or None
|
77
|
+
for an unbounded cache. (When a new entry is added to a full cache,
|
78
|
+
the oldest cached entry will be removed.) The default is None.
|
79
|
+
|
80
|
+
ttl : float or None
|
81
|
+
The maximum number of seconds to keep an entry in the cache, or
|
82
|
+
None if cache entries should not expire. The default is None.
|
83
|
+
|
84
|
+
Example
|
85
|
+
-------
|
86
|
+
>>> import streamlit as st
|
87
|
+
>>>
|
88
|
+
>>> @st.cache
|
89
|
+
... def fetch_and_clean_data(url):
|
90
|
+
... # Fetch data from URL here, and then clean it up.
|
91
|
+
... return data
|
92
|
+
...
|
93
|
+
>>> d1 = fetch_and_clean_data(DATA_URL_1)
|
94
|
+
>>> # Actually executes the function, since this is the first time it was
|
95
|
+
>>> # encountered.
|
96
|
+
>>>
|
97
|
+
>>> d2 = fetch_and_clean_data(DATA_URL_1)
|
98
|
+
>>> # Does not execute the function. Instead, returns its previously computed
|
99
|
+
>>> # value. This means that now the data in d1 is the same as in d2.
|
100
|
+
>>>
|
101
|
+
>>> d3 = fetch_and_clean_data(DATA_URL_2)
|
102
|
+
>>> # This is a different URL, so the function executes.
|
103
|
+
|
104
|
+
To set the ``persist`` parameter, use this command as follows:
|
105
|
+
|
106
|
+
>>> @st.cache(persist=True)
|
107
|
+
... def fetch_and_clean_data(url):
|
108
|
+
... # Fetch data from URL here, and then clean it up.
|
109
|
+
... return data
|
110
|
+
|
111
|
+
To disable hashing return values, set the ``allow_output_mutation`` parameter to ``True``:
|
112
|
+
|
113
|
+
>>> @st.cache(allow_output_mutation=True)
|
114
|
+
... def fetch_and_clean_data(url):
|
115
|
+
... # Fetch data from URL here, and then clean it up.
|
116
|
+
... return data
|
117
|
+
|
118
|
+
|
119
|
+
To override the default hashing behavior, pass a custom hash function.
|
120
|
+
You can do that by mapping a type (e.g. ``MongoClient``) to a hash function (``id``) like this:
|
121
|
+
|
122
|
+
>>> @st.cache(hash_funcs={MongoClient: id})
|
123
|
+
... def connect_to_database(url):
|
124
|
+
... return MongoClient(url)
|
125
|
+
|
126
|
+
Alternatively, you can map the type's fully-qualified name
|
127
|
+
(e.g. ``"pymongo.mongo_client.MongoClient"``) to the hash function instead:
|
128
|
+
|
129
|
+
>>> @st.cache(hash_funcs={"pymongo.mongo_client.MongoClient": id})
|
130
|
+
... def connect_to_database(url):
|
131
|
+
... return MongoClient(url)
|
132
|
+
|
133
|
+
"""
|
134
|
+
import streamlit as st
|
135
|
+
|
136
|
+
deprecation_util.show_deprecation_warning(
|
137
|
+
f"""
|
138
|
+
`st.cache` is deprecated and will be removed soon. Please use one of Streamlit's new caching commands, `st.cache_data` or `st.cache_resource`.
|
139
|
+
More information [in our docs]({CACHE_DOCS_URL}).
|
140
|
+
|
141
|
+
**Note**: The behavior of `st.cache` was updated in Streamlit 1.36 to the new caching logic used by `st.cache_data` and `st.cache_resource`.
|
142
|
+
This might lead to some problems or unexpected behavior in certain edge cases.
|
143
|
+
"""
|
144
|
+
)
|
145
|
+
|
146
|
+
# suppress_st_warning is unused since its not supported by the new caching commands
|
147
|
+
|
148
|
+
if allow_output_mutation:
|
149
|
+
return st.cache_resource( # type: ignore
|
150
|
+
func,
|
151
|
+
show_spinner=show_spinner,
|
152
|
+
hash_funcs=hash_funcs,
|
153
|
+
max_entries=max_entries,
|
154
|
+
ttl=ttl,
|
155
|
+
)
|
156
|
+
|
157
|
+
return st.cache_data( # type: ignore
|
158
|
+
func,
|
159
|
+
persist=persist,
|
160
|
+
show_spinner=show_spinner,
|
161
|
+
hash_funcs=hash_funcs,
|
162
|
+
max_entries=max_entries,
|
163
|
+
ttl=ttl,
|
164
|
+
)
|
streamlit/runtime/fragment.py
CHANGED
@@ -93,6 +93,96 @@ class MemoryFragmentStorage(FragmentStorage):
|
|
93
93
|
self._fragments.clear()
|
94
94
|
|
95
95
|
|
96
|
+
def _fragment(
|
97
|
+
func: F | None = None, *, run_every: int | float | timedelta | str | None = None
|
98
|
+
) -> Callable[[F], F] | F:
|
99
|
+
"""Contains the actual fragment logic.
|
100
|
+
|
101
|
+
This function should be used by our internal functions that use fragments under-the-hood,
|
102
|
+
so that fragment metrics are not tracked for those elements (note that the @gather_metrics annotation is only on the publicly exposed function)
|
103
|
+
"""
|
104
|
+
|
105
|
+
if func is None:
|
106
|
+
# Support passing the params via function decorator
|
107
|
+
def wrapper(f: F) -> F:
|
108
|
+
return fragment(
|
109
|
+
func=f,
|
110
|
+
run_every=run_every,
|
111
|
+
)
|
112
|
+
|
113
|
+
return wrapper
|
114
|
+
else:
|
115
|
+
non_optional_func = func
|
116
|
+
|
117
|
+
@wraps(non_optional_func)
|
118
|
+
def wrap(*args, **kwargs):
|
119
|
+
from streamlit.delta_generator import dg_stack
|
120
|
+
|
121
|
+
ctx = get_script_run_ctx()
|
122
|
+
if ctx is None:
|
123
|
+
return
|
124
|
+
|
125
|
+
cursors_snapshot = deepcopy(ctx.cursors)
|
126
|
+
dg_stack_snapshot = deepcopy(dg_stack.get())
|
127
|
+
active_dg = dg_stack_snapshot[-1]
|
128
|
+
h = hashlib.new("md5")
|
129
|
+
h.update(
|
130
|
+
f"{non_optional_func.__module__}.{non_optional_func.__qualname__}{active_dg._get_delta_path_str()}".encode(
|
131
|
+
"utf-8"
|
132
|
+
)
|
133
|
+
)
|
134
|
+
fragment_id = h.hexdigest()
|
135
|
+
|
136
|
+
def wrapped_fragment():
|
137
|
+
import streamlit as st
|
138
|
+
|
139
|
+
# NOTE: We need to call get_script_run_ctx here again and can't just use the
|
140
|
+
# value of ctx from above captured by the closure because subsequent
|
141
|
+
# fragment runs will generally run in a new script run, thus we'll have a
|
142
|
+
# new ctx.
|
143
|
+
ctx = get_script_run_ctx(suppress_warning=True)
|
144
|
+
assert ctx is not None
|
145
|
+
|
146
|
+
if ctx.fragment_ids_this_run:
|
147
|
+
# This script run is a run of one or more fragments. We restore the
|
148
|
+
# state of ctx.cursors and dg_stack to the snapshots we took when this
|
149
|
+
# fragment was declared.
|
150
|
+
ctx.cursors = deepcopy(cursors_snapshot)
|
151
|
+
dg_stack.set(deepcopy(dg_stack_snapshot))
|
152
|
+
else:
|
153
|
+
# Otherwise, we must be in a full script run. We need to temporarily set
|
154
|
+
# ctx.current_fragment_id so that elements corresponding to this
|
155
|
+
# fragment get tagged with the appropriate ID. ctx.current_fragment_id
|
156
|
+
# gets reset after the fragment function finishes running.
|
157
|
+
ctx.current_fragment_id = fragment_id
|
158
|
+
|
159
|
+
try:
|
160
|
+
with st.container():
|
161
|
+
result = non_optional_func(*args, **kwargs)
|
162
|
+
finally:
|
163
|
+
ctx.current_fragment_id = None
|
164
|
+
|
165
|
+
return result
|
166
|
+
|
167
|
+
ctx.fragment_storage.set(fragment_id, wrapped_fragment)
|
168
|
+
|
169
|
+
if run_every:
|
170
|
+
msg = ForwardMsg()
|
171
|
+
msg.auto_rerun.interval = time_to_seconds(run_every)
|
172
|
+
msg.auto_rerun.fragment_id = fragment_id
|
173
|
+
ctx.enqueue(msg)
|
174
|
+
|
175
|
+
return wrapped_fragment()
|
176
|
+
|
177
|
+
with contextlib.suppress(AttributeError):
|
178
|
+
# Make this a well-behaved decorator by preserving important function
|
179
|
+
# attributes.
|
180
|
+
wrap.__dict__.update(non_optional_func.__dict__)
|
181
|
+
wrap.__signature__ = inspect.signature(non_optional_func) # type: ignore
|
182
|
+
|
183
|
+
return wrap
|
184
|
+
|
185
|
+
|
96
186
|
@overload
|
97
187
|
def fragment(
|
98
188
|
func: F,
|
@@ -228,83 +318,4 @@ def fragment(
|
|
228
318
|
height: 400px
|
229
319
|
|
230
320
|
"""
|
231
|
-
|
232
|
-
if func is None:
|
233
|
-
# Support passing the params via function decorator
|
234
|
-
def wrapper(f: F) -> F:
|
235
|
-
return fragment(
|
236
|
-
func=f,
|
237
|
-
run_every=run_every,
|
238
|
-
)
|
239
|
-
|
240
|
-
return wrapper
|
241
|
-
else:
|
242
|
-
non_optional_func = func
|
243
|
-
|
244
|
-
@wraps(non_optional_func)
|
245
|
-
def wrap(*args, **kwargs):
|
246
|
-
from streamlit.delta_generator import dg_stack
|
247
|
-
|
248
|
-
ctx = get_script_run_ctx()
|
249
|
-
if ctx is None:
|
250
|
-
return
|
251
|
-
|
252
|
-
cursors_snapshot = deepcopy(ctx.cursors)
|
253
|
-
dg_stack_snapshot = deepcopy(dg_stack.get())
|
254
|
-
active_dg = dg_stack_snapshot[-1]
|
255
|
-
h = hashlib.new("md5")
|
256
|
-
h.update(
|
257
|
-
f"{non_optional_func.__module__}.{non_optional_func.__qualname__}{active_dg._get_delta_path_str()}".encode(
|
258
|
-
"utf-8"
|
259
|
-
)
|
260
|
-
)
|
261
|
-
fragment_id = h.hexdigest()
|
262
|
-
|
263
|
-
def wrapped_fragment():
|
264
|
-
import streamlit as st
|
265
|
-
|
266
|
-
# NOTE: We need to call get_script_run_ctx here again and can't just use the
|
267
|
-
# value of ctx from above captured by the closure because subsequent
|
268
|
-
# fragment runs will generally run in a new script run, thus we'll have a
|
269
|
-
# new ctx.
|
270
|
-
ctx = get_script_run_ctx(suppress_warning=True)
|
271
|
-
assert ctx is not None
|
272
|
-
|
273
|
-
if ctx.fragment_ids_this_run:
|
274
|
-
# This script run is a run of one or more fragments. We restore the
|
275
|
-
# state of ctx.cursors and dg_stack to the snapshots we took when this
|
276
|
-
# fragment was declared.
|
277
|
-
ctx.cursors = deepcopy(cursors_snapshot)
|
278
|
-
dg_stack.set(deepcopy(dg_stack_snapshot))
|
279
|
-
else:
|
280
|
-
# Otherwise, we must be in a full script run. We need to temporarily set
|
281
|
-
# ctx.current_fragment_id so that elements corresponding to this
|
282
|
-
# fragment get tagged with the appropriate ID. ctx.current_fragment_id
|
283
|
-
# gets reset after the fragment function finishes running.
|
284
|
-
ctx.current_fragment_id = fragment_id
|
285
|
-
|
286
|
-
try:
|
287
|
-
with st.container():
|
288
|
-
result = non_optional_func(*args, **kwargs)
|
289
|
-
finally:
|
290
|
-
ctx.current_fragment_id = None
|
291
|
-
|
292
|
-
return result
|
293
|
-
|
294
|
-
ctx.fragment_storage.set(fragment_id, wrapped_fragment)
|
295
|
-
|
296
|
-
if run_every:
|
297
|
-
msg = ForwardMsg()
|
298
|
-
msg.auto_rerun.interval = time_to_seconds(run_every)
|
299
|
-
msg.auto_rerun.fragment_id = fragment_id
|
300
|
-
ctx.enqueue(msg)
|
301
|
-
|
302
|
-
return wrapped_fragment()
|
303
|
-
|
304
|
-
with contextlib.suppress(AttributeError):
|
305
|
-
# Make this a well-behaved decorator by preserving important function
|
306
|
-
# attributes.
|
307
|
-
wrap.__dict__.update(non_optional_func.__dict__)
|
308
|
-
wrap.__signature__ = inspect.signature(non_optional_func) # type: ignore
|
309
|
-
|
310
|
-
return wrap
|
321
|
+
return _fragment(func, run_every=run_every)
|
streamlit/runtime/runtime.py
CHANGED
@@ -40,7 +40,6 @@ from streamlit.runtime.forward_msg_cache import (
|
|
40
40
|
create_reference_msg,
|
41
41
|
populate_hash_if_needed,
|
42
42
|
)
|
43
|
-
from streamlit.runtime.legacy_caching.caching import _mem_caches
|
44
43
|
from streamlit.runtime.media_file_manager import MediaFileManager
|
45
44
|
from streamlit.runtime.media_file_storage import MediaFileStorage
|
46
45
|
from streamlit.runtime.memory_session_storage import MemorySessionStorage
|
@@ -218,7 +217,6 @@ class Runtime:
|
|
218
217
|
self._stats_mgr = StatsManager()
|
219
218
|
self._stats_mgr.register_provider(get_data_cache_stats_provider())
|
220
219
|
self._stats_mgr.register_provider(get_resource_cache_stats_provider())
|
221
|
-
self._stats_mgr.register_provider(_mem_caches)
|
222
220
|
self._stats_mgr.register_provider(self._message_cache)
|
223
221
|
self._stats_mgr.register_provider(self._uploaded_file_mgr)
|
224
222
|
self._stats_mgr.register_provider(SessionStateStatProvider(self._session_mgr))
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"files": {
|
3
3
|
"main.css": "./static/css/main.3aaaea00.css",
|
4
|
-
"main.js": "./static/js/main.
|
4
|
+
"main.js": "./static/js/main.e93f99a3.js",
|
5
5
|
"static/js/9336.3e046ad7.chunk.js": "./static/js/9336.3e046ad7.chunk.js",
|
6
6
|
"static/js/9330.2b4c99e0.chunk.js": "./static/js/9330.2b4c99e0.chunk.js",
|
7
7
|
"static/js/2736.4336e2b9.chunk.js": "./static/js/2736.4336e2b9.chunk.js",
|
@@ -151,6 +151,6 @@
|
|
151
151
|
},
|
152
152
|
"entrypoints": [
|
153
153
|
"static/css/main.3aaaea00.css",
|
154
|
-
"static/js/main.
|
154
|
+
"static/js/main.e93f99a3.js"
|
155
155
|
]
|
156
156
|
}
|
streamlit/static/index.html
CHANGED
@@ -1 +1 @@
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><link rel="shortcut icon" href="./favicon.png"/><link rel="preload" href="./static/media/SourceSansPro-Regular.0d69e5ff5e92ac64a0c9.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="./static/media/SourceSansPro-SemiBold.abed79cd0df1827e18cf.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="./static/media/SourceSansPro-Bold.118dea98980e20a81ced.woff2" as="font" type="font/woff2" crossorigin><title>Streamlit</title><script>window.prerenderReady=!1</script><script defer="defer" src="./static/js/main.
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><link rel="shortcut icon" href="./favicon.png"/><link rel="preload" href="./static/media/SourceSansPro-Regular.0d69e5ff5e92ac64a0c9.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="./static/media/SourceSansPro-SemiBold.abed79cd0df1827e18cf.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="./static/media/SourceSansPro-Bold.118dea98980e20a81ced.woff2" as="font" type="font/woff2" crossorigin><title>Streamlit</title><script>window.prerenderReady=!1</script><script defer="defer" src="./static/js/main.e93f99a3.js"></script><link href="./static/css/main.3aaaea00.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|