python-fasthtml 0.9.2__tar.gz → 0.10.1__tar.gz
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.
- {python-fasthtml-0.9.2/python_fasthtml.egg-info → python-fasthtml-0.10.1}/PKG-INFO +1 -1
- python-fasthtml-0.10.1/fasthtml/__init__.py +2 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/_modidx.py +7 -1
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/components.py +22 -8
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/live_reload.py +15 -25
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/oauth.py +32 -13
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/toaster.py +23 -9
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1/python_fasthtml.egg-info}/PKG-INFO +1 -1
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/settings.ini +1 -1
- python-fasthtml-0.9.2/fasthtml/__init__.py +0 -2
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/CONTRIBUTING.md +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/LICENSE +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/MANIFEST.in +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/README.md +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/authmw.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/basics.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/cli.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/common.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/components.pyi +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/core.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/core.pyi +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/fastapp.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/ft.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/js.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/jupyter.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/katex.js +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/pico.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/starlette.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/svg.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/xtend.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/fasthtml/xtend.pyi +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/pyproject.toml +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/python_fasthtml.egg-info/SOURCES.txt +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/python_fasthtml.egg-info/dependency_links.txt +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/python_fasthtml.egg-info/entry_points.txt +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/python_fasthtml.egg-info/not-zip-safe +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/python_fasthtml.egg-info/requires.txt +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/python_fasthtml.egg-info/top_level.txt +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/setup.cfg +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/setup.py +0 -0
- {python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/tests/test_toaster.py +0 -0
|
@@ -131,7 +131,13 @@ d = { 'settings': { 'branch': 'main',
|
|
|
131
131
|
'fasthtml.jupyter.wait_port_free': ('api/jupyter.html#wait_port_free', 'fasthtml/jupyter.py'),
|
|
132
132
|
'fasthtml.jupyter.ws_client': ('api/jupyter.html#ws_client', 'fasthtml/jupyter.py')},
|
|
133
133
|
'fasthtml.live_reload': {},
|
|
134
|
-
'fasthtml.oauth': { 'fasthtml.oauth.
|
|
134
|
+
'fasthtml.oauth': { 'fasthtml.oauth.Auth0AppClient': ('api/oauth.html#auth0appclient', 'fasthtml/oauth.py'),
|
|
135
|
+
'fasthtml.oauth.Auth0AppClient.__init__': ('api/oauth.html#auth0appclient.__init__', 'fasthtml/oauth.py'),
|
|
136
|
+
'fasthtml.oauth.Auth0AppClient._fetch_openid_config': ( 'api/oauth.html#auth0appclient._fetch_openid_config',
|
|
137
|
+
'fasthtml/oauth.py'),
|
|
138
|
+
'fasthtml.oauth.Auth0AppClient.login_link': ( 'api/oauth.html#auth0appclient.login_link',
|
|
139
|
+
'fasthtml/oauth.py'),
|
|
140
|
+
'fasthtml.oauth.DiscordAppClient': ('api/oauth.html#discordappclient', 'fasthtml/oauth.py'),
|
|
135
141
|
'fasthtml.oauth.DiscordAppClient.__init__': ( 'api/oauth.html#discordappclient.__init__',
|
|
136
142
|
'fasthtml/oauth.py'),
|
|
137
143
|
'fasthtml.oauth.DiscordAppClient.login_link': ( 'api/oauth.html#discordappclient.login_link',
|
|
@@ -121,7 +121,10 @@ def _fill_item(item, obj):
|
|
|
121
121
|
if val is not None and not 'skip' in attr:
|
|
122
122
|
if tag=='input':
|
|
123
123
|
if attr.get('type', '') == 'checkbox':
|
|
124
|
-
if val:
|
|
124
|
+
if isinstance(val, list):
|
|
125
|
+
if attr['value'] in val: attr['checked'] = '1'
|
|
126
|
+
else: attr.pop('checked', '')
|
|
127
|
+
elif val: attr['checked'] = '1'
|
|
125
128
|
else: attr.pop('checked', '')
|
|
126
129
|
elif attr.get('type', '') == 'radio':
|
|
127
130
|
if val and val == attr['value']: attr['checked'] = '1'
|
|
@@ -129,8 +132,13 @@ def _fill_item(item, obj):
|
|
|
129
132
|
else: attr['value'] = val
|
|
130
133
|
if tag=='textarea': cs=(val,)
|
|
131
134
|
if tag == 'select':
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
if isinstance(val, list):
|
|
136
|
+
for opt in cs:
|
|
137
|
+
if opt.tag == 'option' and opt.get('value') in val:
|
|
138
|
+
opt.selected = '1'
|
|
139
|
+
else:
|
|
140
|
+
option = next((o for o in cs if o.tag=='option' and o.get('value')==val), None)
|
|
141
|
+
if option: option.selected = '1'
|
|
134
142
|
return FT(tag,cs,attr,void_=item.void_)
|
|
135
143
|
|
|
136
144
|
# %% ../nbs/api/01_components.ipynb
|
|
@@ -181,17 +189,23 @@ def html2ft(html, attr1st=False):
|
|
|
181
189
|
cts = elm.contents
|
|
182
190
|
cs = [repr(c.strip()) if isinstance(c, str) else _parse(c, lvl+1)
|
|
183
191
|
for c in cts if str(c).strip()]
|
|
184
|
-
attrs = []
|
|
192
|
+
attrs, exotic_attrs = [], {}
|
|
185
193
|
for key, value in sorted(elm.attrs.items(), key=lambda x: x[0]=='class'):
|
|
186
194
|
if isinstance(value,(tuple,list)): value = " ".join(value)
|
|
187
|
-
key = rev_map.get(key, key)
|
|
188
|
-
attrs.append(f'{key.replace("-", "_")}={value!r}'
|
|
189
|
-
|
|
195
|
+
key, value = rev_map.get(key, key), value or True
|
|
196
|
+
if _re_h2x_attr_key.match(key): attrs.append(f'{key.replace("-", "_")}={value!r}')
|
|
197
|
+
else: exotic_attrs[key] = value
|
|
198
|
+
if exotic_attrs: attrs.append(f'**{exotic_attrs!r}')
|
|
190
199
|
spc = " "*lvl*indent
|
|
191
200
|
onlychild = not cts or (len(cts)==1 and isinstance(cts[0],str))
|
|
192
201
|
j = ', ' if onlychild else f',\n{spc}'
|
|
193
202
|
inner = j.join(filter(None, cs+attrs))
|
|
194
|
-
if onlychild:
|
|
203
|
+
if onlychild:
|
|
204
|
+
if not attr1st: return f'{tag_name}({inner})'
|
|
205
|
+
else:
|
|
206
|
+
# respect attr1st setting
|
|
207
|
+
attrs = ', '.join(filter(None, attrs))
|
|
208
|
+
return f'{tag_name}({attrs})({cs[0] if cs else ""})'
|
|
195
209
|
if not attr1st or not attrs: return f'{tag_name}(\n{spc}{inner}\n{" "*(lvl-1)*indent})'
|
|
196
210
|
inner_cs = j.join(filter(None, cs))
|
|
197
211
|
inner_attrs = ', '.join(filter(None, attrs))
|
|
@@ -4,24 +4,25 @@ from fasthtml.basics import FastHTML, Script
|
|
|
4
4
|
__all__ = ["FastHTMLWithLiveReload"]
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
-
|
|
7
|
+
def LiveReloadJs(reload_attempts:int=1, reload_interval:float=1000., **kwargs):
|
|
8
|
+
src = """
|
|
9
|
+
(function() {
|
|
10
|
+
var socket = new WebSocket(`ws://${window.location.host}/live-reload`);
|
|
11
|
+
var maxReloadAttempts = %s;
|
|
12
|
+
var reloadInterval = %s; // time between reload attempts in ms
|
|
13
|
+
socket.onclose = function() {
|
|
13
14
|
let reloadAttempts = 0;
|
|
14
|
-
const intervalFn = setInterval(function(){
|
|
15
|
+
const intervalFn = setInterval(function(){
|
|
15
16
|
window.location.reload();
|
|
16
17
|
reloadAttempts++;
|
|
17
18
|
if (reloadAttempts === maxReloadAttempts) clearInterval(intervalFn);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
19
|
+
}, reloadInterval);
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
21
22
|
"""
|
|
23
|
+
return Script(src % (reload_attempts, reload_interval))
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
async def live_reload_websocket(websocket): await websocket.accept()
|
|
25
|
+
async def live_reload_ws(websocket): await websocket.accept()
|
|
25
26
|
|
|
26
27
|
class FastHTMLWithLiveReload(FastHTML):
|
|
27
28
|
"""
|
|
@@ -47,19 +48,8 @@ class FastHTMLWithLiveReload(FastHTML):
|
|
|
47
48
|
Run:
|
|
48
49
|
serve()
|
|
49
50
|
"""
|
|
50
|
-
LIVE_RELOAD_ROUTE = WebSocketRoute("/live-reload", endpoint=live_reload_websocket)
|
|
51
|
-
|
|
52
51
|
def __init__(self, *args, **kwargs):
|
|
53
|
-
# Create the live reload script to be injected into the webpage
|
|
54
|
-
self.LIVE_RELOAD_HEADER = Script(
|
|
55
|
-
LIVE_RELOAD_SCRIPT.format(
|
|
56
|
-
reload_attempts=kwargs.get("reload_attempts", 1),
|
|
57
|
-
reload_interval=kwargs.get("reload_interval", 1000),
|
|
58
|
-
)
|
|
59
|
-
)
|
|
60
|
-
|
|
61
52
|
# "hdrs" and "routes" can be missing, None, a list or a tuple.
|
|
62
|
-
kwargs["hdrs"] = [*(kwargs.get("hdrs") or []),
|
|
63
|
-
kwargs["routes"] = [*(kwargs.get("routes") or []),
|
|
53
|
+
kwargs["hdrs"] = [*(kwargs.get("hdrs") or []), LiveReloadJs(**kwargs)]
|
|
54
|
+
kwargs["routes"] = [*(kwargs.get("routes") or []), WebSocketRoute("/live-reload", endpoint=live_reload_ws)]
|
|
64
55
|
super().__init__(*args, **kwargs)
|
|
65
|
-
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/08_oauth.ipynb.
|
|
4
4
|
|
|
5
5
|
# %% auto 0
|
|
6
|
-
__all__ = ['http_patterns', 'GoogleAppClient', 'GitHubAppClient', 'HuggingFaceClient', 'DiscordAppClient', '
|
|
7
|
-
'url_match', 'OAuth']
|
|
6
|
+
__all__ = ['http_patterns', 'GoogleAppClient', 'GitHubAppClient', 'HuggingFaceClient', 'DiscordAppClient', 'Auth0AppClient',
|
|
7
|
+
'redir_url', 'url_match', 'OAuth']
|
|
8
8
|
|
|
9
9
|
# %% ../nbs/api/08_oauth.ipynb
|
|
10
10
|
from .common import *
|
|
@@ -14,6 +14,7 @@ import secrets, httpx
|
|
|
14
14
|
|
|
15
15
|
# %% ../nbs/api/08_oauth.ipynb
|
|
16
16
|
class _AppClient(WebApplicationClient):
|
|
17
|
+
id_key = 'sub'
|
|
17
18
|
def __init__(self, client_id, client_secret, code=None, scope=None, **kwargs):
|
|
18
19
|
super().__init__(client_id, code=code, scope=scope, **kwargs)
|
|
19
20
|
self.client_secret = client_secret
|
|
@@ -22,9 +23,8 @@ class _AppClient(WebApplicationClient):
|
|
|
22
23
|
class GoogleAppClient(_AppClient):
|
|
23
24
|
"A `WebApplicationClient` for Google oauth2"
|
|
24
25
|
base_url = "https://accounts.google.com/o/oauth2/v2/auth"
|
|
25
|
-
token_url = "https://
|
|
26
|
-
info_url = "https://
|
|
27
|
-
id_key = 'sub'
|
|
26
|
+
token_url = "https://oauth2.googleapis.com/token"
|
|
27
|
+
info_url = "https://openidconnect.googleapis.com/v1/userinfo"
|
|
28
28
|
|
|
29
29
|
def __init__(self, client_id, client_secret, code=None, scope=None, **kwargs):
|
|
30
30
|
scope_pre = "https://www.googleapis.com/auth/userinfo"
|
|
@@ -39,8 +39,9 @@ class GoogleAppClient(_AppClient):
|
|
|
39
39
|
# %% ../nbs/api/08_oauth.ipynb
|
|
40
40
|
class GitHubAppClient(_AppClient):
|
|
41
41
|
"A `WebApplicationClient` for GitHub oauth2"
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
prefix = "https://github.com/login/oauth/"
|
|
43
|
+
base_url = f"{prefix}authorize"
|
|
44
|
+
token_url = f"{prefix}access_token"
|
|
44
45
|
info_url = "https://api.github.com/user"
|
|
45
46
|
id_key = 'id'
|
|
46
47
|
|
|
@@ -50,11 +51,10 @@ class GitHubAppClient(_AppClient):
|
|
|
50
51
|
# %% ../nbs/api/08_oauth.ipynb
|
|
51
52
|
class HuggingFaceClient(_AppClient):
|
|
52
53
|
"A `WebApplicationClient` for HuggingFace oauth2"
|
|
53
|
-
|
|
54
|
-
base_url = "
|
|
55
|
-
token_url = "
|
|
56
|
-
info_url = "
|
|
57
|
-
id_key = 'sub'
|
|
54
|
+
prefix = "https://huggingface.co/oauth/"
|
|
55
|
+
base_url = f"{prefix}authorize"
|
|
56
|
+
token_url = f"{prefix}token"
|
|
57
|
+
info_url = f"{prefix}userinfo"
|
|
58
58
|
|
|
59
59
|
def __init__(self, client_id, client_secret, code=None, scope=None, state=None, **kwargs):
|
|
60
60
|
if not scope: scope=["openid","profile"]
|
|
@@ -87,6 +87,24 @@ class DiscordAppClient(_AppClient):
|
|
|
87
87
|
r.raise_for_status()
|
|
88
88
|
self.parse_request_body_response(r.text)
|
|
89
89
|
|
|
90
|
+
# %% ../nbs/api/08_oauth.ipynb
|
|
91
|
+
class Auth0AppClient(_AppClient):
|
|
92
|
+
"A `WebApplicationClient` for Auth0 OAuth2"
|
|
93
|
+
def __init__(self, domain, client_id, client_secret, code=None, scope=None, redirect_uri="", **kwargs):
|
|
94
|
+
self.redirect_uri,self.domain = redirect_uri,domain
|
|
95
|
+
config = self._fetch_openid_config()
|
|
96
|
+
self.base_url,self.token_url,self.info_url = config["authorization_endpoint"],config["token_endpoint"],config["userinfo_endpoint"]
|
|
97
|
+
super().__init__(client_id, client_secret, code=code, scope=scope, redirect_uri=redirect_uri, **kwargs)
|
|
98
|
+
|
|
99
|
+
def _fetch_openid_config(self):
|
|
100
|
+
r = httpx.get(f"https://{self.domain}/.well-known/openid-configuration")
|
|
101
|
+
r.raise_for_status()
|
|
102
|
+
return r.json()
|
|
103
|
+
|
|
104
|
+
def login_link(self, req):
|
|
105
|
+
d = dict(response_type="code", client_id=self.client_id, scope=self.scope, redirect_uri=redir_url(req, self.redirect_uri))
|
|
106
|
+
return f"{self.base_url}?{urlencode(d)}"
|
|
107
|
+
|
|
90
108
|
# %% ../nbs/api/08_oauth.ipynb
|
|
91
109
|
@patch
|
|
92
110
|
def login_link(self:WebApplicationClient, redirect_uri, scope=None, state=None):
|
|
@@ -96,8 +114,9 @@ def login_link(self:WebApplicationClient, redirect_uri, scope=None, state=None):
|
|
|
96
114
|
return self.prepare_request_uri(self.base_url, redirect_uri, scope, state=state)
|
|
97
115
|
|
|
98
116
|
# %% ../nbs/api/08_oauth.ipynb
|
|
99
|
-
def redir_url(request, redir_path, scheme=
|
|
117
|
+
def redir_url(request, redir_path, scheme=None):
|
|
100
118
|
"Get the redir url for the host in `request`"
|
|
119
|
+
scheme = 'http' if request.url.hostname in ("localhost", "127.0.0.1") else 'https'
|
|
101
120
|
return f"{scheme}://{request.url.netloc}{redir_path}"
|
|
102
121
|
|
|
103
122
|
# %% ../nbs/api/08_oauth.ipynb
|
|
@@ -13,9 +13,17 @@ toast_css = """
|
|
|
13
13
|
}
|
|
14
14
|
.fh-toast {
|
|
15
15
|
background-color: #333; color: white;
|
|
16
|
-
padding: 12px 20px; border-radius: 4px; margin-bottom: 10px;
|
|
16
|
+
padding: 12px 28px 12px 20px; border-radius: 4px; margin-bottom: 10px;
|
|
17
17
|
max-width: 80%; width: auto; text-align: center;
|
|
18
18
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
19
|
+
position: relative;
|
|
20
|
+
}
|
|
21
|
+
.fh-toast-dismiss {
|
|
22
|
+
position:absolute; top:0.2em; right:0.4em;
|
|
23
|
+
line-height:1em; padding: 0 0.2em 0.2em 0.2em; border-radius:inherit;
|
|
24
|
+
transform:scaleY(0.8); transform:scaleX(1.);
|
|
25
|
+
pointer-events:auto; cursor:pointer;
|
|
26
|
+
background-color:inherit; color:inherit; filter:brightness(0.85);
|
|
19
27
|
}
|
|
20
28
|
.fh-toast-info { background-color: #2196F3; }
|
|
21
29
|
.fh-toast-success { background-color: #4CAF50; }
|
|
@@ -23,7 +31,9 @@ toast_css = """
|
|
|
23
31
|
.fh-toast-error { background-color: #F44336; }
|
|
24
32
|
"""
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
def ToastJs(duration:float):
|
|
35
|
+
duration = int(1000*duration)
|
|
36
|
+
src = """
|
|
27
37
|
export function proc_htmx(sel, func) {
|
|
28
38
|
htmx.onLoad(elt => {
|
|
29
39
|
const elements = any(sel, elt, false);
|
|
@@ -34,26 +44,30 @@ export function proc_htmx(sel, func) {
|
|
|
34
44
|
proc_htmx('.fh-toast-container', async function(toast) {
|
|
35
45
|
await sleep(100);
|
|
36
46
|
toast.style.opacity = '0.8';
|
|
37
|
-
await sleep(
|
|
47
|
+
await sleep(%s);
|
|
38
48
|
toast.style.opacity = '0';
|
|
39
49
|
await sleep(300);
|
|
40
50
|
toast.remove();
|
|
41
51
|
});
|
|
52
|
+
|
|
53
|
+
proc_htmx('.fh-toast-dismiss', function(elem) {
|
|
54
|
+
elem.addEventListener('click', (e) => { e.target.parentElement.remove() });
|
|
55
|
+
});
|
|
42
56
|
"""
|
|
57
|
+
return Script(src % (duration,), type="module")
|
|
58
|
+
|
|
43
59
|
|
|
44
60
|
def add_toast(sess, message, typ="info"):
|
|
45
61
|
assert typ in ("info", "success", "warning", "error"), '`typ` not in ("info", "success", "warning", "error")'
|
|
46
62
|
sess.setdefault(sk, []).append((message, typ))
|
|
47
63
|
|
|
48
64
|
def render_toasts(sess):
|
|
49
|
-
toasts = [Div(msg, cls=f"fh-toast fh-toast-{typ}") for msg,typ in sess.pop(sk, [])]
|
|
50
|
-
return Div(Div(*toasts, cls="fh-toast-container"),
|
|
51
|
-
hx_swap_oob="afterbegin:body")
|
|
65
|
+
toasts = [Div(msg, Span('x', cls='fh-toast-dismiss'), cls=f"fh-toast fh-toast-{typ}") for msg,typ in sess.pop(sk, [])]
|
|
66
|
+
return Div(Div(*toasts, cls="fh-toast-container"), hx_swap_oob="afterbegin:body")
|
|
52
67
|
|
|
53
68
|
def toast_after(resp, req, sess):
|
|
54
69
|
if sk in sess and (not resp or isinstance(resp, (tuple,FT,FtResponse))): req.injects.append(render_toasts(sess))
|
|
55
70
|
|
|
56
|
-
def setup_toasts(app):
|
|
57
|
-
app.hdrs += (Style(toast_css),
|
|
71
|
+
def setup_toasts(app, duration:float=10.):
|
|
72
|
+
app.hdrs += (Style(toast_css), ToastJs(duration))
|
|
58
73
|
app.after.append(toast_after)
|
|
59
|
-
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[DEFAULT]
|
|
2
2
|
repo = fasthtml
|
|
3
3
|
lib_name = fasthtml
|
|
4
|
-
version = 0.
|
|
4
|
+
version = 0.10.1
|
|
5
5
|
min_python = 3.10
|
|
6
6
|
license = apache2
|
|
7
7
|
requirements = fastcore>=1.7.18 python-dateutil starlette>0.33 oauthlib itsdangerous uvicorn[standard]>=0.30 httpx fastlite>=0.0.9 python-multipart beautifulsoup4
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python-fasthtml-0.9.2 → python-fasthtml-0.10.1}/python_fasthtml.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|