python-fasthtml 0.12.20__tar.gz → 0.12.22__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.12.20/python_fasthtml.egg-info → python_fasthtml-0.12.22}/PKG-INFO +2 -2
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/README.md +1 -1
- python_fasthtml-0.12.22/fasthtml/__init__.py +2 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/_modidx.py +1 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/core.py +3 -1
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/core.pyi +9 -6
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/jupyter.py +5 -3
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/oauth.py +17 -10
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/toaster.py +11 -3
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22/python_fasthtml.egg-info}/PKG-INFO +2 -2
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/settings.ini +1 -1
- python_fasthtml-0.12.20/fasthtml/__init__.py +0 -2
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/CONTRIBUTING.md +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/LICENSE +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/MANIFEST.in +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/authmw.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/basics.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/cli.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/common.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/components.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/components.pyi +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/fastapp.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/ft.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/js.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/katex.js +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/live_reload.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/pico.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/starlette.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/stripe_otp.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/svg.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/xtend.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/fasthtml/xtend.pyi +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/pyproject.toml +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/python_fasthtml.egg-info/SOURCES.txt +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/python_fasthtml.egg-info/dependency_links.txt +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/python_fasthtml.egg-info/entry_points.txt +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/python_fasthtml.egg-info/not-zip-safe +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/python_fasthtml.egg-info/requires.txt +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/python_fasthtml.egg-info/top_level.txt +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/setup.cfg +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/setup.py +0 -0
- {python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/tests/test_toaster.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-fasthtml
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.22
|
|
4
4
|
Summary: The fastest way to create an HTML app
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/fasthtml
|
|
6
6
|
Author: Jeremy Howard and contributors
|
|
@@ -187,7 +187,7 @@ repo’s notebooks and the official FastHTML examples repo:
|
|
|
187
187
|
Then explore the small but growing third-party ecosystem of FastHTML
|
|
188
188
|
tutorials, notebooks, libraries, and components:
|
|
189
189
|
|
|
190
|
-
- [FastHTML Gallery](https://fastht.ml
|
|
190
|
+
- [FastHTML Gallery](https://gallery.fastht.ml): Learn from minimal
|
|
191
191
|
examples of components (ie chat bubbles, click-to-edit, infinite
|
|
192
192
|
scroll, etc)
|
|
193
193
|
- [Creating Custom FastHTML Tags for Markdown
|
|
@@ -138,7 +138,7 @@ repo’s notebooks and the official FastHTML examples repo:
|
|
|
138
138
|
Then explore the small but growing third-party ecosystem of FastHTML
|
|
139
139
|
tutorials, notebooks, libraries, and components:
|
|
140
140
|
|
|
141
|
-
- [FastHTML Gallery](https://fastht.ml
|
|
141
|
+
- [FastHTML Gallery](https://gallery.fastht.ml): Learn from minimal
|
|
142
142
|
examples of components (ie chat bubbles, click-to-edit, infinite
|
|
143
143
|
scroll, etc)
|
|
144
144
|
- [Creating Custom FastHTML Tags for Markdown
|
|
@@ -197,6 +197,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
197
197
|
'fasthtml/oauth.py'),
|
|
198
198
|
'fasthtml.oauth._AppClient.retr_id': ('api/oauth.html#_appclient.retr_id', 'fasthtml/oauth.py'),
|
|
199
199
|
'fasthtml.oauth._AppClient.retr_info': ('api/oauth.html#_appclient.retr_info', 'fasthtml/oauth.py'),
|
|
200
|
+
'fasthtml.oauth.get_host': ('api/oauth.html#get_host', 'fasthtml/oauth.py'),
|
|
200
201
|
'fasthtml.oauth.load_creds': ('api/oauth.html#load_creds', 'fasthtml/oauth.py'),
|
|
201
202
|
'fasthtml.oauth.redir_url': ('api/oauth.html#redir_url', 'fasthtml/oauth.py'),
|
|
202
203
|
'fasthtml.oauth.url_match': ('api/oauth.html#url_match', 'fasthtml/oauth.py')},
|
|
@@ -240,6 +240,7 @@ def _find_wsp(ws, data, hdrs, arg:str, p:Parameter):
|
|
|
240
240
|
if issubclass(anno, HtmxHeaders): return _get_htmx(hdrs)
|
|
241
241
|
if issubclass(anno, Starlette): return ws.scope['app']
|
|
242
242
|
if issubclass(anno, WebSocket): return ws
|
|
243
|
+
if issubclass(anno, dict): return data
|
|
243
244
|
if anno is empty:
|
|
244
245
|
if arg.lower()=='ws': return ws
|
|
245
246
|
if arg.lower()=='scope': return dict2obj(ws.scope)
|
|
@@ -263,7 +264,8 @@ def _wrap_ws(ws, data, params):
|
|
|
263
264
|
# %% ../nbs/api/00_core.ipynb
|
|
264
265
|
async def _send_ws(ws, resp):
|
|
265
266
|
if not resp: return
|
|
266
|
-
|
|
267
|
+
# res = to_xml(resp, indent=fh_cfg.indent) if isinstance(resp, (list,tuple,FT)) or hasattr(resp, '__ft__') else resp
|
|
268
|
+
res = to_xml(resp, indent=fh_cfg.indent)
|
|
267
269
|
await ws.send_text(res)
|
|
268
270
|
|
|
269
271
|
def _ws_endp(recv, conn=None, disconn=None):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""The `FastHTML` subclass of `Starlette`, along with the `RouterX` and `RouteX` classes it automatically uses."""
|
|
2
|
-
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmx_exts', 'htmxsrc', 'fhjsscr', 'surrsrc', 'scopesrc', 'viewport', 'charset', 'cors_allow', 'iframe_scr', 'all_meths', 'devtools_loc', 'parsed_date', 'snake2hyphens', 'HtmxHeaders', 'HttpHeader', 'HtmxResponseHeaders', 'form2dict', 'parse_form', 'JSONResponse', 'flat_xt', 'Beforeware', 'EventStream', 'signal_shutdown', 'uri', 'decode_uri', 'flat_tuple', 'noop_body', 'respond', 'is_full_page', 'Redirect', 'get_key', 'qp', 'def_hdrs', 'FastHTML', 'nested_name', 'serve', 'Client', 'RouteFuncs', 'APIRouter', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse', 'unqid'
|
|
3
|
-
import json, uuid, inspect, types, signal, asyncio, threading, inspect, random
|
|
2
|
+
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmx_exts', 'htmxsrc', 'fhjsscr', 'surrsrc', 'scopesrc', 'viewport', 'charset', 'cors_allow', 'iframe_scr', 'all_meths', 'devtools_loc', 'parsed_date', 'snake2hyphens', 'HtmxHeaders', 'HttpHeader', 'HtmxResponseHeaders', 'form2dict', 'parse_form', 'JSONResponse', 'flat_xt', 'Beforeware', 'EventStream', 'signal_shutdown', 'uri', 'decode_uri', 'flat_tuple', 'noop_body', 'respond', 'is_full_page', 'Redirect', 'get_key', 'qp', 'def_hdrs', 'FastHTML', 'nested_name', 'serve', 'Client', 'RouteFuncs', 'APIRouter', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse', 'unqid']
|
|
3
|
+
import json, uuid, inspect, types, signal, asyncio, threading, inspect, random, contextlib
|
|
4
4
|
from fastcore.utils import *
|
|
5
5
|
from fastcore.xml import *
|
|
6
6
|
from fastcore.meta import use_kwargs_dict
|
|
@@ -213,7 +213,7 @@ class Redirect:
|
|
|
213
213
|
|
|
214
214
|
async def _wrap_call(f, req, params):
|
|
215
215
|
...
|
|
216
|
-
htmx_exts = {'morph': 'https://cdn.jsdelivr.net/npm/idiomorph@0.7.3/dist/idiomorph-ext.min.js', 'head-support': 'https://cdn.jsdelivr.net/npm/htmx-ext-head-support@2.0.3/head-support.js', 'preload': 'https://cdn.jsdelivr.net/npm/htmx-ext-preload@2.1.0/preload.js', 'class-tools': 'https://cdn.jsdelivr.net/npm/htmx-ext-class-tools@2.0.1/class-tools.js', 'loading-states': 'https://cdn.jsdelivr.net/npm/htmx-ext-loading-states@2.0.0/loading-states.js', 'multi-swap': 'https://cdn.jsdelivr.net/npm/htmx-ext-multi-swap@2.0.0/multi-swap.js', 'path-deps': 'https://cdn.jsdelivr.net/npm/htmx-ext-path-deps@2.0.0/path-deps.js', 'remove-me': 'https://cdn.jsdelivr.net/npm/htmx-ext-remove-me@2.0.0/remove-me.js', 'ws': 'https://cdn.jsdelivr.net/npm/htmx-ext-ws@2.0.
|
|
216
|
+
htmx_exts = {'morph': 'https://cdn.jsdelivr.net/npm/idiomorph@0.7.3/dist/idiomorph-ext.min.js', 'head-support': 'https://cdn.jsdelivr.net/npm/htmx-ext-head-support@2.0.3/head-support.js', 'preload': 'https://cdn.jsdelivr.net/npm/htmx-ext-preload@2.1.0/preload.js', 'class-tools': 'https://cdn.jsdelivr.net/npm/htmx-ext-class-tools@2.0.1/class-tools.js', 'loading-states': 'https://cdn.jsdelivr.net/npm/htmx-ext-loading-states@2.0.0/loading-states.js', 'multi-swap': 'https://cdn.jsdelivr.net/npm/htmx-ext-multi-swap@2.0.0/multi-swap.js', 'path-deps': 'https://cdn.jsdelivr.net/npm/htmx-ext-path-deps@2.0.0/path-deps.js', 'remove-me': 'https://cdn.jsdelivr.net/npm/htmx-ext-remove-me@2.0.0/remove-me.js', 'ws': 'https://cdn.jsdelivr.net/npm/htmx-ext-ws@2.0.3/ws.js', 'chunked-transfer': 'https://cdn.jsdelivr.net/npm/htmx-ext-transfer-encoding-chunked@0.4.0/transfer-encoding-chunked.js'}
|
|
217
217
|
htmxsrc = Script(src='https://cdn.jsdelivr.net/npm/htmx.org@2.0.4/dist/htmx.min.js')
|
|
218
218
|
fhjsscr = Script(src='https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.12/fasthtml.js')
|
|
219
219
|
surrsrc = Script(src='https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js')
|
|
@@ -265,6 +265,9 @@ class FastHTML(Starlette):
|
|
|
265
265
|
"""Add a route at `path`"""
|
|
266
266
|
...
|
|
267
267
|
|
|
268
|
+
def set_lifespan(self, value):
|
|
269
|
+
...
|
|
270
|
+
|
|
268
271
|
def static_route_exts(self, prefix='/', static_path='.', exts='static'):
|
|
269
272
|
"""Add a static route at URL path `prefix` with files from `static_path` and `exts` defined by `reg_re_param()`"""
|
|
270
273
|
...
|
|
@@ -273,6 +276,9 @@ class FastHTML(Starlette):
|
|
|
273
276
|
"""Add a static route at URL path `prefix` with files from `static_path` and single `ext` (including the '.')"""
|
|
274
277
|
...
|
|
275
278
|
|
|
279
|
+
def setup_ws(app, f=noop):
|
|
280
|
+
...
|
|
281
|
+
|
|
276
282
|
def devtools_json(self, path=None, uuid=None):
|
|
277
283
|
...
|
|
278
284
|
all_meths = 'get post put delete patch head trace options'.split()
|
|
@@ -370,7 +376,4 @@ def unqid(seeded=False):
|
|
|
370
376
|
|
|
371
377
|
def _add_ids(s):
|
|
372
378
|
...
|
|
373
|
-
|
|
374
|
-
def setup_ws(app, f=noop):
|
|
375
|
-
...
|
|
376
379
|
devtools_loc = '/.well-known/appspecific/com.chrome.devtools.json'
|
|
@@ -111,7 +111,7 @@ class JupyUviAsync(JupyUvi):
|
|
|
111
111
|
wait_port_free(self.port)
|
|
112
112
|
|
|
113
113
|
# %% ../nbs/api/06_jupyter.ipynb
|
|
114
|
-
def HTMX(path="",
|
|
114
|
+
def HTMX(path="/", host='localhost', app=None, port=8000, height="auto", link=False, iframe=True):
|
|
115
115
|
"An iframe which displays the HTMX application in a notebook."
|
|
116
116
|
if isinstance(path, (FT,tuple,Safe)):
|
|
117
117
|
assert app, 'Need an app to render a component'
|
|
@@ -127,9 +127,11 @@ def HTMX(path="", app=None, host='localhost', port=8000, height="auto", link=Fal
|
|
|
127
127
|
if (e.data.height) frame.style.height = (e.data.height+1) + 'px';
|
|
128
128
|
}, false);
|
|
129
129
|
}""" if height == "auto" else ""
|
|
130
|
-
|
|
130
|
+
proto = 'http' if host=='localhost' else 'https'
|
|
131
|
+
fullpath = f"{proto}://{host}:{port}{path}" if host else path
|
|
132
|
+
if link: display(HTML(f'<a href="{fullpath}" target="_blank">Open in new tab</a>'))
|
|
131
133
|
if iframe:
|
|
132
|
-
return HTML(f'<iframe src="
|
|
134
|
+
return HTML(f'<iframe src="{fullpath}" style="width: 100%; height: {height}; border: none;" onload="{scr}" ' + """allow="accelerometer; autoplay; camera; clipboard-read; clipboard-write; display-capture; encrypted-media; fullscreen; gamepad; geolocation; gyroscope; hid; identity-credentials-get; idle-detection; magnetometer; microphone; midi; payment; picture-in-picture; publickey-credentials-get; screen-wake-lock; serial; usb; web-share; xr-spatial-tracking"></iframe> """)
|
|
133
135
|
|
|
134
136
|
# %% ../nbs/api/06_jupyter.ipynb
|
|
135
137
|
def ws_client(app, nm='', host='localhost', port=8000, ws_connect='/ws', frame=True, link=True, **kwargs):
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# %% auto 0
|
|
6
6
|
__all__ = ['http_patterns', 'GoogleAppClient', 'GitHubAppClient', 'HuggingFaceClient', 'DiscordAppClient', 'Auth0AppClient',
|
|
7
|
-
'redir_url', 'url_match', 'OAuth', 'load_creds']
|
|
7
|
+
'get_host', 'redir_url', 'url_match', 'OAuth', 'load_creds']
|
|
8
8
|
|
|
9
9
|
# %% ../nbs/api/08_oauth.ipynb
|
|
10
10
|
from .common import *
|
|
@@ -121,10 +121,17 @@ def login_link(self:WebApplicationClient, redirect_uri, scope=None, state=None,
|
|
|
121
121
|
return self.prepare_request_uri(self.base_url, redirect_uri, scope, state=state, **kwargs)
|
|
122
122
|
|
|
123
123
|
# %% ../nbs/api/08_oauth.ipynb
|
|
124
|
-
def
|
|
124
|
+
def get_host(request):
|
|
125
|
+
"""Get the host, preferring X-Forwarded-Host if available"""
|
|
126
|
+
forwarded_host = request.headers.get('x-forwarded-host')
|
|
127
|
+
return forwarded_host if forwarded_host else request.url.netloc
|
|
128
|
+
|
|
129
|
+
# %% ../nbs/api/08_oauth.ipynb
|
|
130
|
+
def redir_url(req, redir_path, scheme=None):
|
|
125
131
|
"Get the redir url for the host in `request`"
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
host = get_host(req)
|
|
133
|
+
scheme = 'http' if host.split(':')[0] in ("localhost", "127.0.0.1") else 'https'
|
|
134
|
+
return f"{scheme}://{host}{redir_path}"
|
|
128
135
|
|
|
129
136
|
# %% ../nbs/api/08_oauth.ipynb
|
|
130
137
|
@patch
|
|
@@ -132,7 +139,7 @@ def parse_response(self:_AppClient, code, redirect_uri):
|
|
|
132
139
|
"Get the token from the oauth2 server response"
|
|
133
140
|
payload = dict(code=code, redirect_uri=redirect_uri, client_id=self.client_id,
|
|
134
141
|
client_secret=self.client_secret, grant_type='authorization_code')
|
|
135
|
-
r = httpx.post(self.token_url,
|
|
142
|
+
r = httpx.post(self.token_url, data=payload)
|
|
136
143
|
r.raise_for_status()
|
|
137
144
|
self.parse_request_body_response(r.text)
|
|
138
145
|
|
|
@@ -159,8 +166,8 @@ def retr_id(self:_AppClient, code, redirect_uri):
|
|
|
159
166
|
|
|
160
167
|
# %% ../nbs/api/08_oauth.ipynb
|
|
161
168
|
http_patterns = (r'^(localhost|127\.0\.0\.1)(:\d+)?$',)
|
|
162
|
-
def url_match(
|
|
163
|
-
return any(re.match(pattern,
|
|
169
|
+
def url_match(request, patterns=http_patterns):
|
|
170
|
+
return any(re.match(pattern, get_host(request).split(':')[0]) for pattern in patterns)
|
|
164
171
|
|
|
165
172
|
# %% ../nbs/api/08_oauth.ipynb
|
|
166
173
|
class OAuth:
|
|
@@ -178,8 +185,8 @@ class OAuth:
|
|
|
178
185
|
@app.get(redir_path)
|
|
179
186
|
def redirect(req, session, code:str=None, error:str=None, state:str=None):
|
|
180
187
|
if not code: session['oauth_error']=error; return RedirectResponse(self.error_path, status_code=303)
|
|
181
|
-
scheme = 'http' if url_match(req
|
|
182
|
-
base_url = f"{scheme}://{req
|
|
188
|
+
scheme = 'http' if url_match(req,self.http_patterns) or not self.https else 'https'
|
|
189
|
+
base_url = f"{scheme}://{get_host(req)}"
|
|
183
190
|
info = AttrDictDefault(cli.retr_info(code, base_url+redir_path))
|
|
184
191
|
ident = info.get(self.cli.id_key)
|
|
185
192
|
if not ident: return self.redir_login(session)
|
|
@@ -195,7 +202,7 @@ class OAuth:
|
|
|
195
202
|
|
|
196
203
|
def redir_login(self, session): return RedirectResponse(self.login_path, status_code=303)
|
|
197
204
|
def redir_url(self, req):
|
|
198
|
-
scheme = 'http' if url_match(req
|
|
205
|
+
scheme = 'http' if url_match(req,self.http_patterns) or not self.https else 'https'
|
|
199
206
|
return redir_url(req, self.redir_path, scheme)
|
|
200
207
|
|
|
201
208
|
def login_link(self, req, scope=None, state=None): return self.cli.login_link(self.redir_url(req), scope=scope, state=state)
|
|
@@ -33,9 +33,17 @@ toast_css = """
|
|
|
33
33
|
.fh-toast-error { background-color: #F44336; }
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
|
+
js = '''htmx.onLoad(() => {
|
|
37
|
+
if (!htmx.find('#fh-toast-container')) {
|
|
38
|
+
const ctn = document.createElement('div');
|
|
39
|
+
ctn.id = 'fh-toast-container';
|
|
40
|
+
document.body.appendChild(ctn);
|
|
41
|
+
}
|
|
42
|
+
});'''
|
|
43
|
+
|
|
36
44
|
def Toast(message: str, typ: str = "info", dismiss: bool = False, duration:int=5000):
|
|
37
45
|
x_btn = Button('x', cls="fh-toast-dismiss", onclick="htmx.remove(this?.parentElement);") if dismiss else None
|
|
38
|
-
return Div(Span(message), x_btn, cls=f"fh-toast fh-toast-{typ}", hx_on_transitionend="setTimeout(() => this?.remove(),
|
|
46
|
+
return Div(Span(message), x_btn, cls=f"fh-toast fh-toast-{typ}", hx_on_transitionend=f"setTimeout(() => this?.remove(), {duration});")
|
|
39
47
|
|
|
40
48
|
def add_toast(sess, message: str, typ: str = "info", dismiss: bool = False):
|
|
41
49
|
assert typ in ("info", "success", "warning", "error"), '`typ` not in ("info", "success", "warning", "error")'
|
|
@@ -43,7 +51,7 @@ def add_toast(sess, message: str, typ: str = "info", dismiss: bool = False):
|
|
|
43
51
|
|
|
44
52
|
def render_toasts(sess):
|
|
45
53
|
toasts = [Toast(msg, typ, dismiss, sess['toast_duration']) for msg, typ, dismiss in sess.pop(sk, [])]
|
|
46
|
-
return Div(*toasts,
|
|
54
|
+
return Div(*toasts, hx_swap_oob=f'beforeend:#{tcid}')
|
|
47
55
|
|
|
48
56
|
def toast_after(resp, req, sess):
|
|
49
57
|
if sk in sess and (not resp or isinstance(resp, (tuple,FT,FtResponse))):
|
|
@@ -52,5 +60,5 @@ def toast_after(resp, req, sess):
|
|
|
52
60
|
|
|
53
61
|
def setup_toasts(app, duration=5000):
|
|
54
62
|
app.state.toast_duration = duration
|
|
55
|
-
app.hdrs += [Style(toast_css)]
|
|
63
|
+
app.hdrs += [Style(toast_css), Script(js)]
|
|
56
64
|
app.after.append(toast_after)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-fasthtml
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.22
|
|
4
4
|
Summary: The fastest way to create an HTML app
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/fasthtml
|
|
6
6
|
Author: Jeremy Howard and contributors
|
|
@@ -187,7 +187,7 @@ repo’s notebooks and the official FastHTML examples repo:
|
|
|
187
187
|
Then explore the small but growing third-party ecosystem of FastHTML
|
|
188
188
|
tutorials, notebooks, libraries, and components:
|
|
189
189
|
|
|
190
|
-
- [FastHTML Gallery](https://fastht.ml
|
|
190
|
+
- [FastHTML Gallery](https://gallery.fastht.ml): Learn from minimal
|
|
191
191
|
examples of components (ie chat bubbles, click-to-edit, infinite
|
|
192
192
|
scroll, etc)
|
|
193
193
|
- [Creating Custom FastHTML Tags for Markdown
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[DEFAULT]
|
|
2
2
|
repo = fasthtml
|
|
3
3
|
lib_name = python-fasthtml
|
|
4
|
-
version = 0.12.
|
|
4
|
+
version = 0.12.22
|
|
5
5
|
min_python = 3.10
|
|
6
6
|
license = apache2
|
|
7
7
|
requirements = fastcore>=1.8.1 python-dateutil starlette>0.33 oauthlib itsdangerous uvicorn[standard]>=0.30 httpx fastlite>=0.1.1 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
|
{python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/python_fasthtml.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{python_fasthtml-0.12.20 → python_fasthtml-0.12.22}/python_fasthtml.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|