python-fasthtml 0.5.1__tar.gz → 0.5.2__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.5.1/python_fasthtml.egg-info → python-fasthtml-0.5.2}/PKG-INFO +27 -2
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/README.md +25 -0
- python-fasthtml-0.5.2/fasthtml/__init__.py +2 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/_modidx.py +35 -1
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/components.py +2 -2
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/components.pyi +5 -1
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/core.py +61 -6
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/core.pyi +36 -4
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/fastapp.py +1 -3
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/oauth.py +6 -6
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/starlette.py +1 -1
- python-fasthtml-0.5.2/fasthtml/svg.py +173 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/toaster.py +4 -3
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2/python_fasthtml.egg-info}/PKG-INFO +27 -2
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/python_fasthtml.egg-info/requires.txt +1 -1
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/settings.ini +2 -2
- python-fasthtml-0.5.1/fasthtml/__init__.py +0 -2
- python-fasthtml-0.5.1/fasthtml/svg.py +0 -4
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/CONTRIBUTING.md +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/LICENSE +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/MANIFEST.in +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/authmw.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/basics.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/cli.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/common.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/ft.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/js.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/katex.js +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/live_reload.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/pico.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/xtend.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/fasthtml/xtend.pyi +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/python_fasthtml.egg-info/SOURCES.txt +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/python_fasthtml.egg-info/dependency_links.txt +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/python_fasthtml.egg-info/entry_points.txt +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/python_fasthtml.egg-info/not-zip-safe +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/python_fasthtml.egg-info/top_level.txt +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/setup.cfg +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/setup.py +0 -0
- {python-fasthtml-0.5.1 → python-fasthtml-0.5.2}/tests/test_toaster.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-fasthtml
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
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
|
|
@@ -15,7 +15,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: fastcore>=1.7.
|
|
18
|
+
Requires-Dist: fastcore>=1.7.5
|
|
19
19
|
Requires-Dist: python-dateutil
|
|
20
20
|
Requires-Dist: starlette>0.33
|
|
21
21
|
Requires-Dist: oauthlib
|
|
@@ -114,6 +114,31 @@ the new version returned by the second route.
|
|
|
114
114
|
This “hypermedia-based” approach to web development is a powerful way to
|
|
115
115
|
build web applications.
|
|
116
116
|
|
|
117
|
+
### Getting help from AI
|
|
118
|
+
|
|
119
|
+
Because FastHTML is newer than most LLMs, AI systems like Cursor,
|
|
120
|
+
ChatGPT, Claude, and Copilot won’t give useful answers about it. To fix
|
|
121
|
+
that problem, we’ve provided an LLM-friendly guide that teaches them how
|
|
122
|
+
to use FastHTML. To use it, add this link for your AI helper to use:
|
|
123
|
+
|
|
124
|
+
- [/llms-ctx.txt](https://docs.fastht.ml/llms-ctx.txt)
|
|
125
|
+
|
|
126
|
+
This example is in a format based on recommendations from Anthropic for
|
|
127
|
+
use with [Claude
|
|
128
|
+
Projects](https://support.anthropic.com/en/articles/9517075-what-are-projects).
|
|
129
|
+
This works so well that we’ve actually found that Claude can provide
|
|
130
|
+
even better information than our own documentation! For instance, read
|
|
131
|
+
through [this annotated Claude
|
|
132
|
+
chat](https://gist.github.com/jph00/9559b0a563f6a370029bec1d1cc97b74)
|
|
133
|
+
for some great getting-started information, entirely generated from a
|
|
134
|
+
project using the above text file as context.
|
|
135
|
+
|
|
136
|
+
If you use Cursor, type `@doc` then choose “*Add new doc*”, and use the
|
|
137
|
+
/llms-ctx.txt link above. The context file is auto-generated from our
|
|
138
|
+
[`llms.txt`](https://llmstxt.org/) (our proposed standard for providing
|
|
139
|
+
AI-friendly information)—you can generate alternative versions suitable
|
|
140
|
+
for other models as needed.
|
|
141
|
+
|
|
117
142
|
## Next Steps
|
|
118
143
|
|
|
119
144
|
Start with the official sources to learn more about FastHTML:
|
|
@@ -83,6 +83,31 @@ the new version returned by the second route.
|
|
|
83
83
|
This “hypermedia-based” approach to web development is a powerful way to
|
|
84
84
|
build web applications.
|
|
85
85
|
|
|
86
|
+
### Getting help from AI
|
|
87
|
+
|
|
88
|
+
Because FastHTML is newer than most LLMs, AI systems like Cursor,
|
|
89
|
+
ChatGPT, Claude, and Copilot won’t give useful answers about it. To fix
|
|
90
|
+
that problem, we’ve provided an LLM-friendly guide that teaches them how
|
|
91
|
+
to use FastHTML. To use it, add this link for your AI helper to use:
|
|
92
|
+
|
|
93
|
+
- [/llms-ctx.txt](https://docs.fastht.ml/llms-ctx.txt)
|
|
94
|
+
|
|
95
|
+
This example is in a format based on recommendations from Anthropic for
|
|
96
|
+
use with [Claude
|
|
97
|
+
Projects](https://support.anthropic.com/en/articles/9517075-what-are-projects).
|
|
98
|
+
This works so well that we’ve actually found that Claude can provide
|
|
99
|
+
even better information than our own documentation! For instance, read
|
|
100
|
+
through [this annotated Claude
|
|
101
|
+
chat](https://gist.github.com/jph00/9559b0a563f6a370029bec1d1cc97b74)
|
|
102
|
+
for some great getting-started information, entirely generated from a
|
|
103
|
+
project using the above text file as context.
|
|
104
|
+
|
|
105
|
+
If you use Cursor, type `@doc` then choose “*Add new doc*”, and use the
|
|
106
|
+
/llms-ctx.txt link above. The context file is auto-generated from our
|
|
107
|
+
[`llms.txt`](https://llmstxt.org/) (our proposed standard for providing
|
|
108
|
+
AI-friendly information)—you can generate alternative versions suitable
|
|
109
|
+
for other models as needed.
|
|
110
|
+
|
|
86
111
|
## Next Steps
|
|
87
112
|
|
|
88
113
|
Start with the official sources to learn more about FastHTML:
|
|
@@ -25,18 +25,27 @@ d = { 'settings': { 'branch': 'main',
|
|
|
25
25
|
'fasthtml.components.sse_message': ('api/components.html#sse_message', 'fasthtml/components.py')},
|
|
26
26
|
'fasthtml.core': { 'fasthtml.core.Beforeware': ('api/core.html#beforeware', 'fasthtml/core.py'),
|
|
27
27
|
'fasthtml.core.Beforeware.__init__': ('api/core.html#beforeware.__init__', 'fasthtml/core.py'),
|
|
28
|
+
'fasthtml.core.Client': ('api/core.html#client', 'fasthtml/core.py'),
|
|
29
|
+
'fasthtml.core.Client.__init__': ('api/core.html#client.__init__', 'fasthtml/core.py'),
|
|
30
|
+
'fasthtml.core.Client._sync': ('api/core.html#client._sync', 'fasthtml/core.py'),
|
|
28
31
|
'fasthtml.core.EventStream': ('api/core.html#eventstream', 'fasthtml/core.py'),
|
|
29
32
|
'fasthtml.core.FastHTML': ('api/core.html#fasthtml', 'fasthtml/core.py'),
|
|
30
33
|
'fasthtml.core.FastHTML.__init__': ('api/core.html#fasthtml.__init__', 'fasthtml/core.py'),
|
|
31
34
|
'fasthtml.core.FastHTML.route': ('api/core.html#fasthtml.route', 'fasthtml/core.py'),
|
|
35
|
+
'fasthtml.core.FastHTML.static_route': ('api/core.html#fasthtml.static_route', 'fasthtml/core.py'),
|
|
36
|
+
'fasthtml.core.FastHTML.static_route_exts': ('api/core.html#fasthtml.static_route_exts', 'fasthtml/core.py'),
|
|
32
37
|
'fasthtml.core.FastHTML.ws': ('api/core.html#fasthtml.ws', 'fasthtml/core.py'),
|
|
33
38
|
'fasthtml.core.HTTPConnection.url_path_for': ( 'api/core.html#httpconnection.url_path_for',
|
|
34
39
|
'fasthtml/core.py'),
|
|
35
40
|
'fasthtml.core.HtmxHeaders': ('api/core.html#htmxheaders', 'fasthtml/core.py'),
|
|
36
41
|
'fasthtml.core.HtmxHeaders.__bool__': ('api/core.html#htmxheaders.__bool__', 'fasthtml/core.py'),
|
|
42
|
+
'fasthtml.core.HtmxResponseHeaders': ('api/core.html#htmxresponseheaders', 'fasthtml/core.py'),
|
|
37
43
|
'fasthtml.core.HttpHeader': ('api/core.html#httpheader', 'fasthtml/core.py'),
|
|
38
44
|
'fasthtml.core.MiddlewareBase': ('api/core.html#middlewarebase', 'fasthtml/core.py'),
|
|
39
45
|
'fasthtml.core.MiddlewareBase.__call__': ('api/core.html#middlewarebase.__call__', 'fasthtml/core.py'),
|
|
46
|
+
'fasthtml.core.Redirect': ('api/core.html#redirect', 'fasthtml/core.py'),
|
|
47
|
+
'fasthtml.core.Redirect.__init__': ('api/core.html#redirect.__init__', 'fasthtml/core.py'),
|
|
48
|
+
'fasthtml.core.Redirect.__response__': ('api/core.html#redirect.__response__', 'fasthtml/core.py'),
|
|
40
49
|
'fasthtml.core.RouteX': ('api/core.html#routex', 'fasthtml/core.py'),
|
|
41
50
|
'fasthtml.core.RouteX.__init__': ('api/core.html#routex.__init__', 'fasthtml/core.py'),
|
|
42
51
|
'fasthtml.core.RouteX._endp': ('api/core.html#routex._endp', 'fasthtml/core.py'),
|
|
@@ -65,6 +74,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
65
74
|
'fasthtml.core._resp': ('api/core.html#_resp', 'fasthtml/core.py'),
|
|
66
75
|
'fasthtml.core._send_ws': ('api/core.html#_send_ws', 'fasthtml/core.py'),
|
|
67
76
|
'fasthtml.core._sig': ('api/core.html#_sig', 'fasthtml/core.py'),
|
|
77
|
+
'fasthtml.core._to_htmx_header': ('api/core.html#_to_htmx_header', 'fasthtml/core.py'),
|
|
68
78
|
'fasthtml.core._to_xml': ('api/core.html#_to_xml', 'fasthtml/core.py'),
|
|
69
79
|
'fasthtml.core._url_for': ('api/core.html#_url_for', 'fasthtml/core.py'),
|
|
70
80
|
'fasthtml.core._wrap_call': ('api/core.html#_wrap_call', 'fasthtml/core.py'),
|
|
@@ -142,7 +152,31 @@ d = { 'settings': { 'branch': 'main',
|
|
|
142
152
|
'fasthtml.pico.Search': ('api/pico.html#search', 'fasthtml/pico.py'),
|
|
143
153
|
'fasthtml.pico.set_pico_cls': ('api/pico.html#set_pico_cls', 'fasthtml/pico.py')},
|
|
144
154
|
'fasthtml.starlette': {},
|
|
145
|
-
'fasthtml.svg': {
|
|
155
|
+
'fasthtml.svg': { 'fasthtml.svg.Circle': ('api/svg.html#circle', 'fasthtml/svg.py'),
|
|
156
|
+
'fasthtml.svg.Ellipse': ('api/svg.html#ellipse', 'fasthtml/svg.py'),
|
|
157
|
+
'fasthtml.svg.Line': ('api/svg.html#line', 'fasthtml/svg.py'),
|
|
158
|
+
'fasthtml.svg.Path': ('api/svg.html#path', 'fasthtml/svg.py'),
|
|
159
|
+
'fasthtml.svg.PathFT': ('api/svg.html#pathft', 'fasthtml/svg.py'),
|
|
160
|
+
'fasthtml.svg.PathFT.A': ('api/svg.html#pathft.a', 'fasthtml/svg.py'),
|
|
161
|
+
'fasthtml.svg.PathFT.C': ('api/svg.html#pathft.c', 'fasthtml/svg.py'),
|
|
162
|
+
'fasthtml.svg.PathFT.H': ('api/svg.html#pathft.h', 'fasthtml/svg.py'),
|
|
163
|
+
'fasthtml.svg.PathFT.L': ('api/svg.html#pathft.l', 'fasthtml/svg.py'),
|
|
164
|
+
'fasthtml.svg.PathFT.M': ('api/svg.html#pathft.m', 'fasthtml/svg.py'),
|
|
165
|
+
'fasthtml.svg.PathFT.Q': ('api/svg.html#pathft.q', 'fasthtml/svg.py'),
|
|
166
|
+
'fasthtml.svg.PathFT.S': ('api/svg.html#pathft.s', 'fasthtml/svg.py'),
|
|
167
|
+
'fasthtml.svg.PathFT.T': ('api/svg.html#pathft.t', 'fasthtml/svg.py'),
|
|
168
|
+
'fasthtml.svg.PathFT.V': ('api/svg.html#pathft.v', 'fasthtml/svg.py'),
|
|
169
|
+
'fasthtml.svg.PathFT.Z': ('api/svg.html#pathft.z', 'fasthtml/svg.py'),
|
|
170
|
+
'fasthtml.svg.PathFT._append_cmd': ('api/svg.html#pathft._append_cmd', 'fasthtml/svg.py'),
|
|
171
|
+
'fasthtml.svg.Polygon': ('api/svg.html#polygon', 'fasthtml/svg.py'),
|
|
172
|
+
'fasthtml.svg.Polyline': ('api/svg.html#polyline', 'fasthtml/svg.py'),
|
|
173
|
+
'fasthtml.svg.Rect': ('api/svg.html#rect', 'fasthtml/svg.py'),
|
|
174
|
+
'fasthtml.svg.Svg': ('api/svg.html#svg', 'fasthtml/svg.py'),
|
|
175
|
+
'fasthtml.svg.SvgInb': ('api/svg.html#svginb', 'fasthtml/svg.py'),
|
|
176
|
+
'fasthtml.svg.SvgOob': ('api/svg.html#svgoob', 'fasthtml/svg.py'),
|
|
177
|
+
'fasthtml.svg.Text': ('api/svg.html#text', 'fasthtml/svg.py'),
|
|
178
|
+
'fasthtml.svg.ft_svg': ('api/svg.html#ft_svg', 'fasthtml/svg.py'),
|
|
179
|
+
'fasthtml.svg.transformd': ('api/svg.html#transformd', 'fasthtml/svg.py')},
|
|
146
180
|
'fasthtml.toaster': {},
|
|
147
181
|
'fasthtml.xtend': { 'fasthtml.xtend.A': ('api/xtend.html#a', 'fasthtml/xtend.py'),
|
|
148
182
|
'fasthtml.xtend.AX': ('api/xtend.html#ax', 'fasthtml/xtend.py'),
|
|
@@ -52,13 +52,13 @@ fh_cfg['attrmap']=attrmap_x
|
|
|
52
52
|
fh_cfg['valmap' ]=valmap
|
|
53
53
|
|
|
54
54
|
# %% ../nbs/api/01_components.ipynb
|
|
55
|
-
def ft_html(tag: str, *c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, **kwargs):
|
|
55
|
+
def ft_html(tag: str, *c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=FT, **kwargs):
|
|
56
56
|
if attrmap is None: attrmap=fh_cfg.attrmap
|
|
57
57
|
if valmap is None: valmap =fh_cfg.valmap
|
|
58
58
|
kwargs['id'],kwargs['cls'],kwargs['title'],kwargs['style'] = id,cls,title,style
|
|
59
59
|
tag,c,kw = ft(tag, *c, attrmap=attrmap, valmap=valmap, **kwargs).list
|
|
60
60
|
if tag in named and 'id' in kw and 'name' not in kw: kw['name'] = kw['id']
|
|
61
|
-
return
|
|
61
|
+
return ft_cls(tag,c,kw, void_=tag in voids)
|
|
62
62
|
|
|
63
63
|
# %% ../nbs/api/01_components.ipynb
|
|
64
64
|
@use_kwargs(hx_attrs, keep=True)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""`ft_html` and `ft_hx` functions to add some conveniences to `ft`, along with a full set of basic HTML components, and functions to work with forms and `FT` conversion"""
|
|
2
|
-
__all__ = ['named', 'html_attrs', 'hx_attrs', 'show', 'attrmap_x', 'ft_html', 'ft_hx', 'File', 'fill_form', 'fill_dataclass', 'find_inputs', 'html2ft', 'A', 'Abbr', 'Address', 'Area', 'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi', 'Bdo', 'Blockquote', 'Body', 'Br', 'Button', 'Canvas', 'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dd', 'Del', 'Details', 'Dfn', 'Dialog', 'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe', 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header', 'Hgroup', 'Hr', 'Html', 'I', 'Iframe', 'Img', 'Input', 'Ins', 'Kbd', 'Label', 'Legend', 'Li', 'Link', 'Main', 'Map', 'Mark', 'Menu', 'Meta', 'Meter', 'Nav', 'Noscript', 'Object', 'Ol', 'Optgroup', 'Option', 'Output', 'P', 'Picture', 'PortalExperimental', 'Pre', 'Progress', 'Q', 'Rp', 'Rt', 'Ruby', 'S', 'Samp', 'Script', 'Search', 'Section', 'Select', 'Slot', 'Small', 'Source', 'Span', 'Strong', 'Style', 'Sub', 'Summary', 'Sup', 'Table', 'Tbody', 'Td', 'Template', 'Textarea', 'Tfoot', 'Th', 'Thead', 'Time', 'Title', 'Tr', 'Track', 'U', 'Ul', 'Var', 'Video', 'Wbr']
|
|
2
|
+
__all__ = ['named', 'html_attrs', 'hx_attrs', 'show', 'attrmap_x', 'ft_html', 'ft_hx', 'File', 'fill_form', 'fill_dataclass', 'find_inputs', 'html2ft', 'sse_message', 'A', 'Abbr', 'Address', 'Area', 'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi', 'Bdo', 'Blockquote', 'Body', 'Br', 'Button', 'Canvas', 'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dd', 'Del', 'Details', 'Dfn', 'Dialog', 'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe', 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header', 'Hgroup', 'Hr', 'Html', 'I', 'Iframe', 'Img', 'Input', 'Ins', 'Kbd', 'Label', 'Legend', 'Li', 'Link', 'Main', 'Map', 'Mark', 'Menu', 'Meta', 'Meter', 'Nav', 'Noscript', 'Object', 'Ol', 'Optgroup', 'Option', 'Output', 'P', 'Picture', 'PortalExperimental', 'Pre', 'Progress', 'Q', 'Rp', 'Rt', 'Ruby', 'S', 'Samp', 'Script', 'Search', 'Section', 'Select', 'Slot', 'Small', 'Source', 'Span', 'Strong', 'Style', 'Sub', 'Summary', 'Sup', 'Table', 'Tbody', 'Td', 'Template', 'Textarea', 'Tfoot', 'Th', 'Thead', 'Time', 'Title', 'Tr', 'Track', 'U', 'Ul', 'Var', 'Video', 'Wbr']
|
|
3
3
|
from dataclasses import dataclass, asdict, is_dataclass, make_dataclass, replace, astuple, MISSING
|
|
4
4
|
from bs4 import BeautifulSoup, Comment
|
|
5
5
|
from fastcore.utils import *
|
|
@@ -63,6 +63,10 @@ _re_h2x_attr_key = re.compile('^[A-Za-z_-][\\w-]*$')
|
|
|
63
63
|
def html2ft(html, attr1st=False):
|
|
64
64
|
"""Convert HTML to an `ft` expression"""
|
|
65
65
|
...
|
|
66
|
+
|
|
67
|
+
def sse_message(elm, event='message'):
|
|
68
|
+
"""Convert element `elm` into a format suitable for SSE streaming"""
|
|
69
|
+
...
|
|
66
70
|
def ft_html(tag: str, *c, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Any=None, hx_post:Any=None, hx_put:Any=None, hx_delete:Any=None, hx_patch:Any=None, hx_trigger:Any=None, hx_target:Any=None, hx_swap:Any=None, hx_swap_oob:Any=None, hx_include:Any=None, hx_select:Any=None, hx_select_oob:Any=None, hx_indicator:Any=None, hx_push_url:Any=None, hx_confirm:Any=None, hx_disable:Any=None, hx_replace_url:Any=None, hx_vals:Any=None, hx_disabled_elt:Any=None, hx_ext:Any=None, hx_headers:Any=None, hx_history:Any=None, hx_history_elt:Any=None, hx_indicator:Any=None, hx_inherit:Any=None, hx_params:Any=None, hx_preserve:Any=None, hx_prompt:Any=None, hx_replace_url:Any=None, hx_request:Any=None, hx_sync:Any=None, hx_validate:Any=None, **kwargs): ...
|
|
67
71
|
def ft_hx(tag: str, *c, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Any=None, hx_post:Any=None, hx_put:Any=None, hx_delete:Any=None, hx_patch:Any=None, hx_trigger:Any=None, hx_target:Any=None, hx_swap:Any=None, hx_swap_oob:Any=None, hx_include:Any=None, hx_select:Any=None, hx_select_oob:Any=None, hx_indicator:Any=None, hx_push_url:Any=None, hx_confirm:Any=None, hx_disable:Any=None, hx_replace_url:Any=None, hx_vals:Any=None, hx_disabled_elt:Any=None, hx_ext:Any=None, hx_headers:Any=None, hx_history:Any=None, hx_history_elt:Any=None, hx_indicator:Any=None, hx_inherit:Any=None, hx_params:Any=None, hx_preserve:Any=None, hx_prompt:Any=None, hx_replace_url:Any=None, hx_request:Any=None, hx_sync:Any=None, hx_validate:Any=None, **kwargs): ...
|
|
68
72
|
def A(*c, name:Any=None, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Any=None, hx_post:Any=None, hx_put:Any=None, hx_delete:Any=None, hx_patch:Any=None, hx_trigger:Any=None, hx_target:Any=None, hx_swap:Any=None, hx_swap_oob:Any=None, hx_include:Any=None, hx_select:Any=None, hx_select_oob:Any=None, hx_indicator:Any=None, hx_push_url:Any=None, hx_confirm:Any=None, hx_disable:Any=None, hx_replace_url:Any=None, hx_vals:Any=None, hx_disabled_elt:Any=None, hx_ext:Any=None, hx_headers:Any=None, hx_history:Any=None, hx_history_elt:Any=None, hx_indicator:Any=None, hx_inherit:Any=None, hx_params:Any=None, hx_preserve:Any=None, hx_prompt:Any=None, hx_replace_url:Any=None, hx_request:Any=None, hx_sync:Any=None, hx_validate:Any=None, **kwargs): ...
|
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/00_core.ipynb.
|
|
4
4
|
|
|
5
5
|
# %% auto 0
|
|
6
|
-
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmxsrc', 'htmxwssrc', 'fhjsscr', 'htmxctsrc', 'surrsrc', 'scopesrc',
|
|
7
|
-
'charset', 'all_meths', 'date', 'snake2hyphens', 'HtmxHeaders', 'str2int', 'HttpHeader',
|
|
8
|
-
'
|
|
9
|
-
'
|
|
6
|
+
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmxsrc', 'htmxwssrc', 'fhjsscr', 'htmxctsrc', 'surrsrc', 'scopesrc',
|
|
7
|
+
'viewport', 'charset', 'all_meths', 'date', 'snake2hyphens', 'HtmxHeaders', 'str2int', 'HttpHeader',
|
|
8
|
+
'HtmxResponseHeaders', 'form2dict', 'flat_xt', 'Beforeware', 'EventStream', 'signal_shutdown', 'WS_RouteX',
|
|
9
|
+
'uri', 'decode_uri', 'flat_tuple', 'Redirect', 'RouteX', 'RouterX', 'get_key', 'FastHTML', 'serve', 'cookie',
|
|
10
|
+
'reg_re_param', 'MiddlewareBase', 'Client']
|
|
10
11
|
|
|
11
12
|
# %% ../nbs/api/00_core.ipynb
|
|
12
|
-
import json,uuid,inspect,types,uvicorn,signal,asyncio
|
|
13
|
-
from starlette.datastructures import URLPath
|
|
13
|
+
import json,uuid,inspect,types,uvicorn,signal,asyncio,threading
|
|
14
14
|
|
|
15
15
|
from fastcore.utils import *
|
|
16
16
|
from fastcore.xml import *
|
|
17
|
+
from fastcore.meta import use_kwargs_dict
|
|
17
18
|
|
|
18
19
|
from types import UnionType, SimpleNamespace as ns, GenericAlias
|
|
19
20
|
from typing import Optional, get_type_hints, get_args, get_origin, Union, Mapping, TypedDict, List, Any
|
|
@@ -27,6 +28,7 @@ from urllib.parse import urlencode, parse_qs, quote, unquote
|
|
|
27
28
|
from copy import copy,deepcopy
|
|
28
29
|
from warnings import warn
|
|
29
30
|
from dateutil import parser as dtparse
|
|
31
|
+
from httpx import ASGITransport, AsyncClient
|
|
30
32
|
|
|
31
33
|
from .starlette import *
|
|
32
34
|
|
|
@@ -106,6 +108,20 @@ def _form_arg(k, v, d):
|
|
|
106
108
|
@dataclass
|
|
107
109
|
class HttpHeader: k:str;v:str
|
|
108
110
|
|
|
111
|
+
# %% ../nbs/api/00_core.ipynb
|
|
112
|
+
def _to_htmx_header(s):
|
|
113
|
+
return 'HX-' + s.replace('_', '-').title()
|
|
114
|
+
|
|
115
|
+
htmx_resps = dict(location=None, push_url=None, redirect=None, refresh=None, replace_url=None,
|
|
116
|
+
reswap=None, retarget=None, reselect=None, trigger=None, trigger_after_settle=None, trigger_after_swap=None)
|
|
117
|
+
|
|
118
|
+
# %% ../nbs/api/00_core.ipynb
|
|
119
|
+
@use_kwargs_dict(**htmx_resps)
|
|
120
|
+
def HtmxResponseHeaders(**kwargs):
|
|
121
|
+
"HTMX response headers"
|
|
122
|
+
res = tuple(HttpHeader(_to_htmx_header(k), v) for k,v in kwargs.items())
|
|
123
|
+
return res[0] if len(res)==1 else res
|
|
124
|
+
|
|
109
125
|
# %% ../nbs/api/00_core.ipynb
|
|
110
126
|
def _annotations(anno):
|
|
111
127
|
"Same as `get_annotations`, but also works on namedtuples"
|
|
@@ -356,6 +372,7 @@ def _xt_resp(req, resp):
|
|
|
356
372
|
# %% ../nbs/api/00_core.ipynb
|
|
357
373
|
def _resp(req, resp, cls=empty):
|
|
358
374
|
if not resp: resp=()
|
|
375
|
+
if hasattr(resp, '__response__'): resp = resp.__response__(req)
|
|
359
376
|
if cls in (Any,FT): cls=empty
|
|
360
377
|
if isinstance(resp, FileResponse) and not os.path.exists(resp.path): raise HTTPException(404, resp.path)
|
|
361
378
|
if isinstance(resp, Response): return resp
|
|
@@ -368,6 +385,14 @@ def _resp(req, resp, cls=empty):
|
|
|
368
385
|
cls = HTMLResponse
|
|
369
386
|
return cls(resp)
|
|
370
387
|
|
|
388
|
+
# %% ../nbs/api/00_core.ipynb
|
|
389
|
+
class Redirect:
|
|
390
|
+
"Use HTMX or Starlette RedirectResponse as required to redirect to `loc`"
|
|
391
|
+
def __init__(self, loc): self.loc = loc
|
|
392
|
+
def __response__(self, req):
|
|
393
|
+
if 'hx-request' in req.headers: return HtmxResponseHeaders(redirect=self.loc)
|
|
394
|
+
return RedirectResponse(self.loc, status_code=303)
|
|
395
|
+
|
|
371
396
|
# %% ../nbs/api/00_core.ipynb
|
|
372
397
|
async def _wrap_call(f, req, params):
|
|
373
398
|
wreq = await _wrap_req(req, params)
|
|
@@ -565,6 +590,19 @@ def reg_re_param(m, s):
|
|
|
565
590
|
reg_re_param("path", ".*?")
|
|
566
591
|
reg_re_param("static", "ico|gif|jpg|jpeg|webm|css|js|woff|png|svg|mp4|webp|ttf|otf|eot|woff2|txt|html|map")
|
|
567
592
|
|
|
593
|
+
@patch
|
|
594
|
+
def static_route_exts(self:FastHTML, prefix='/', static_path='.', exts='static'):
|
|
595
|
+
"Add a static route at URL path `prefix` with files from `static_path` and `exts` defined by `reg_re_param()`"
|
|
596
|
+
@self.route(f"{prefix}{{fname:path}}.{{ext:{exts}}}")
|
|
597
|
+
async def get(fname:str, ext:str): return FileResponse(f'{static_path}/{fname}.{ext}')
|
|
598
|
+
|
|
599
|
+
# %% ../nbs/api/00_core.ipynb
|
|
600
|
+
@patch
|
|
601
|
+
def static_route(self:FastHTML, ext='', prefix='/', static_path='.'):
|
|
602
|
+
"Add a static route at URL path `prefix` with files from `static_path` and single `ext` (including the '.')"
|
|
603
|
+
@self.route(f"{prefix}{{fname:path}}{ext}")
|
|
604
|
+
async def get(fname:str): return FileResponse(f'{static_path}/{fname}{ext}')
|
|
605
|
+
|
|
568
606
|
# %% ../nbs/api/00_core.ipynb
|
|
569
607
|
class MiddlewareBase:
|
|
570
608
|
async def __call__(self, scope, receive, send) -> None:
|
|
@@ -572,3 +610,20 @@ class MiddlewareBase:
|
|
|
572
610
|
await self._app(scope, receive, send)
|
|
573
611
|
return
|
|
574
612
|
return HTTPConnection(scope)
|
|
613
|
+
|
|
614
|
+
# %% ../nbs/api/00_core.ipynb
|
|
615
|
+
class Client:
|
|
616
|
+
"An httpx ASGI client that doesn't require `async`"
|
|
617
|
+
def __init__(self, app, url="http://testserver"):
|
|
618
|
+
self.cli = AsyncClient(transport=ASGITransport(app), base_url=url)
|
|
619
|
+
|
|
620
|
+
def _sync(self, method, url, **kwargs):
|
|
621
|
+
@threaded
|
|
622
|
+
def f():
|
|
623
|
+
async def _request(): return await self.cli.request(method, url, **kwargs)
|
|
624
|
+
return asyncio.run(_request())
|
|
625
|
+
r = f()
|
|
626
|
+
r.join()
|
|
627
|
+
return r.result
|
|
628
|
+
|
|
629
|
+
for o in ('get', 'post', 'delete', 'put', 'patch', 'options'): setattr(Client, o, partialmethod(Client._sync, o))
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""The `FastHTML` subclass of `Starlette`, along with the `RouterX` and `RouteX` classes it automatically uses."""
|
|
2
|
-
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmxsrc', 'htmxwssrc', 'fhjsscr', 'htmxctsrc', 'surrsrc', 'scopesrc', 'viewport', 'charset', 'all_meths', 'date', 'snake2hyphens', 'HtmxHeaders', 'str2int', 'HttpHeader', 'form2dict', 'flat_xt', 'Beforeware', 'WS_RouteX', 'uri', 'decode_uri', 'flat_tuple', 'RouteX', 'RouterX', 'get_key', 'FastHTML', 'serve', 'cookie', 'reg_re_param', 'MiddlewareBase']
|
|
3
|
-
import json, uuid, inspect, types, uvicorn
|
|
4
|
-
from starlette.datastructures import URLPath
|
|
2
|
+
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmxsrc', 'htmxwssrc', 'fhjsscr', 'htmxctsrc', 'surrsrc', 'scopesrc', 'viewport', 'charset', 'all_meths', 'date', 'snake2hyphens', 'HtmxHeaders', 'str2int', 'HttpHeader', 'HtmxResponseHeaders', 'form2dict', 'flat_xt', 'Beforeware', 'EventStream', 'signal_shutdown', 'WS_RouteX', 'uri', 'decode_uri', 'flat_tuple', 'Redirect', 'RouteX', 'RouterX', 'get_key', 'FastHTML', 'serve', 'cookie', 'reg_re_param', 'MiddlewareBase']
|
|
3
|
+
import json, uuid, inspect, types, uvicorn, signal, asyncio
|
|
5
4
|
from fastcore.utils import *
|
|
6
5
|
from fastcore.xml import *
|
|
6
|
+
from fastcore.meta import use_kwargs_dict
|
|
7
7
|
from types import UnionType, SimpleNamespace as ns, GenericAlias
|
|
8
8
|
from typing import Optional, get_type_hints, get_args, get_origin, Union, Mapping, TypedDict, List, Any
|
|
9
9
|
from datetime import datetime
|
|
@@ -16,7 +16,6 @@ from urllib.parse import urlencode, parse_qs, quote, unquote
|
|
|
16
16
|
from copy import copy, deepcopy
|
|
17
17
|
from warnings import warn
|
|
18
18
|
from dateutil import parser as dtparse
|
|
19
|
-
from starlette.requests import HTTPConnection
|
|
20
19
|
from .starlette import *
|
|
21
20
|
empty = Parameter.empty
|
|
22
21
|
|
|
@@ -70,6 +69,15 @@ class HttpHeader:
|
|
|
70
69
|
k: str
|
|
71
70
|
v: str
|
|
72
71
|
|
|
72
|
+
def _to_htmx_header(s):
|
|
73
|
+
...
|
|
74
|
+
htmx_resps = dict(location=None, push_url=None, redirect=None, refresh=None, replace_url=None, reswap=None, retarget=None, reselect=None, trigger=None, trigger_after_settle=None, trigger_after_swap=None)
|
|
75
|
+
|
|
76
|
+
@use_kwargs_dict(**htmx_resps)
|
|
77
|
+
def HtmxResponseHeaders(**kwargs):
|
|
78
|
+
"""HTMX response headers"""
|
|
79
|
+
...
|
|
80
|
+
|
|
73
81
|
def _annotations(anno):
|
|
74
82
|
"""Same as `get_annotations`, but also works on namedtuples"""
|
|
75
83
|
...
|
|
@@ -120,6 +128,13 @@ async def _send_ws(ws, resp):
|
|
|
120
128
|
def _ws_endp(recv, conn=None, disconn=None):
|
|
121
129
|
...
|
|
122
130
|
|
|
131
|
+
def EventStream(s):
|
|
132
|
+
"""Create a text/event-stream response from `s`"""
|
|
133
|
+
...
|
|
134
|
+
|
|
135
|
+
def signal_shutdown():
|
|
136
|
+
...
|
|
137
|
+
|
|
123
138
|
class WS_RouteX(WebSocketRoute):
|
|
124
139
|
|
|
125
140
|
def __init__(self, app, path: str, recv, conn: callable=None, disconn: callable=None, *, name=None, middleware=None):
|
|
@@ -164,6 +179,15 @@ def _xt_resp(req, resp):
|
|
|
164
179
|
def _resp(req, resp, cls=empty):
|
|
165
180
|
...
|
|
166
181
|
|
|
182
|
+
class Redirect:
|
|
183
|
+
"""Use HTMX or Starlette RedirectResponse as required to redirect to `loc`"""
|
|
184
|
+
|
|
185
|
+
def __init__(self, loc):
|
|
186
|
+
...
|
|
187
|
+
|
|
188
|
+
def __response__(self, req):
|
|
189
|
+
...
|
|
190
|
+
|
|
167
191
|
async def _wrap_call(f, req, params):
|
|
168
192
|
...
|
|
169
193
|
|
|
@@ -218,6 +242,14 @@ class FastHTML(Starlette):
|
|
|
218
242
|
def route(self, path: str=None, methods=None, name=None, include_in_schema=True):
|
|
219
243
|
"""Add a route at `path`"""
|
|
220
244
|
...
|
|
245
|
+
|
|
246
|
+
def static_route_exts(self, prefix='/', static_path='.', exts='static'):
|
|
247
|
+
"""Add a static route at URL path `prefix` with files from `static_path` and `exts` defined by `reg_re_param()`"""
|
|
248
|
+
...
|
|
249
|
+
|
|
250
|
+
def static_route(self, ext='', prefix='/', static_path='.'):
|
|
251
|
+
"""Add a static route at URL path `prefix` with files from `static_path` and single `ext` (including the '.')"""
|
|
252
|
+
...
|
|
221
253
|
all_meths = 'get post put delete patch head trace options'.split()
|
|
222
254
|
for o in all_meths:
|
|
223
255
|
setattr(FastHTML, o, partialmethod(FastHTML.route, methods=o))
|
|
@@ -78,9 +78,7 @@ def fast_app(
|
|
|
78
78
|
session_cookie=session_cookie, max_age=max_age, sess_path=sess_path, same_site=same_site, sess_https_only=sess_https_only,
|
|
79
79
|
sess_domain=sess_domain, key_fname=key_fname, ws_hdr=ws_hdr, surreal=surreal, htmx=htmx, htmlkw=htmlkw,
|
|
80
80
|
reload_attempts=reload_attempts, reload_interval=reload_interval, **(bodykw or {}))
|
|
81
|
-
|
|
82
|
-
@app.route("/{fname:path}.{ext:static}")
|
|
83
|
-
async def get(fname:str, ext:str): return FileResponse(f'{static_path or "."}/{fname}.{ext}')
|
|
81
|
+
app.static_route_exts()
|
|
84
82
|
if not db_file: return app,app.route
|
|
85
83
|
|
|
86
84
|
db = database(db_file)
|
|
@@ -143,7 +143,7 @@ class OAuth:
|
|
|
143
143
|
auth = req.scope['auth'] = session.get('auth')
|
|
144
144
|
if not auth: return RedirectResponse(self.login_path, status_code=303)
|
|
145
145
|
info = AttrDictDefault(cli.get_info(auth))
|
|
146
|
-
if not self._chk_auth(info): return RedirectResponse(self.login_path, status_code=303)
|
|
146
|
+
if not self._chk_auth(info, session): return RedirectResponse(self.login_path, status_code=303)
|
|
147
147
|
app.before.append(Beforeware(before, skip=skip))
|
|
148
148
|
|
|
149
149
|
@app.get(redir_path)
|
|
@@ -152,7 +152,7 @@ class OAuth:
|
|
|
152
152
|
base_url = f"{req.url.scheme}://{req.url.netloc}"
|
|
153
153
|
print(base_url)
|
|
154
154
|
info = AttrDictDefault(cli.retr_info(code, base_url+redir_path))
|
|
155
|
-
if not self._chk_auth(info): return RedirectResponse(self.login_path, status_code=303)
|
|
155
|
+
if not self._chk_auth(info, session): return RedirectResponse(self.login_path, status_code=303)
|
|
156
156
|
session['auth'] = cli.token['access_token']
|
|
157
157
|
return self.login(info, state)
|
|
158
158
|
|
|
@@ -162,11 +162,11 @@ class OAuth:
|
|
|
162
162
|
return self.logout(session)
|
|
163
163
|
|
|
164
164
|
def redir_url(self, req): return f"{req.url.scheme}://{req.url.netloc}{self.redir_path}"
|
|
165
|
-
def login_link(self, req): return self.cli.login_link(self.redir_url(req))
|
|
165
|
+
def login_link(self, req, scope=None, state=None): return self.cli.login_link(self.redir_url(req), scope=scope, state=state)
|
|
166
166
|
|
|
167
167
|
def login(self, info, state): raise NotImplementedError()
|
|
168
168
|
def logout(self, session): return RedirectResponse(self.login_path, status_code=303)
|
|
169
|
-
def chk_auth(self, info, ident): raise NotImplementedError()
|
|
170
|
-
def _chk_auth(self, info):
|
|
169
|
+
def chk_auth(self, info, ident, session): raise NotImplementedError()
|
|
170
|
+
def _chk_auth(self, info, session):
|
|
171
171
|
ident = info.get(self.cli.id_key)
|
|
172
|
-
return ident and self.chk_auth(info, ident)
|
|
172
|
+
return ident and self.chk_auth(info, ident, session)
|
|
@@ -15,6 +15,6 @@ from starlette.routing import Route, Router, Mount, WebSocketRoute
|
|
|
15
15
|
from starlette.exceptions import HTTPException,WebSocketException
|
|
16
16
|
from starlette.endpoints import HTTPEndpoint,WebSocketEndpoint
|
|
17
17
|
from starlette.config import Config
|
|
18
|
-
from starlette.datastructures import CommaSeparatedStrings, Secret, UploadFile
|
|
18
|
+
from starlette.datastructures import CommaSeparatedStrings, Secret, UploadFile, URLPath
|
|
19
19
|
from starlette.types import ASGIApp, Receive, Scope, Send
|
|
20
20
|
from starlette.concurrency import run_in_threadpool
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Simple SVG FT elements"""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/05_svg.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto 0
|
|
6
|
+
__all__ = ['g', 'svg_inb', 'Svg', 'ft_svg', 'Rect', 'Circle', 'Ellipse', 'transformd', 'Line', 'Polyline', 'Polygon', 'Text',
|
|
7
|
+
'PathFT', 'Path', 'SvgOob', 'SvgInb', 'AltGlyph', 'AltGlyphDef', 'AltGlyphItem', 'Animate', 'AnimateColor',
|
|
8
|
+
'AnimateMotion', 'AnimateTransform', 'ClipPath', 'Color_profile', 'Cursor', 'Defs', 'Desc', 'FeBlend',
|
|
9
|
+
'FeColorMatrix', 'FeComponentTransfer', 'FeComposite', 'FeConvolveMatrix', 'FeDiffuseLighting',
|
|
10
|
+
'FeDisplacementMap', 'FeDistantLight', 'FeFlood', 'FeFuncA', 'FeFuncB', 'FeFuncG', 'FeFuncR',
|
|
11
|
+
'FeGaussianBlur', 'FeImage', 'FeMerge', 'FeMergeNode', 'FeMorphology', 'FeOffset', 'FePointLight',
|
|
12
|
+
'FeSpecularLighting', 'FeSpotLight', 'FeTile', 'FeTurbulence', 'Filter', 'Font', 'Font_face',
|
|
13
|
+
'Font_face_format', 'Font_face_name', 'Font_face_src', 'Font_face_uri', 'ForeignObject', 'G', 'Glyph',
|
|
14
|
+
'GlyphRef', 'Hkern', 'Image', 'LinearGradient', 'Marker', 'Mask', 'Metadata', 'Missing_glyph', 'Mpath',
|
|
15
|
+
'Pattern', 'RadialGradient', 'Set', 'Stop', 'Switch', 'Symbol', 'TextPath', 'Tref', 'Tspan', 'Use', 'View',
|
|
16
|
+
'Vkern', 'Template']
|
|
17
|
+
|
|
18
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
19
|
+
from fastcore.utils import *
|
|
20
|
+
from fastcore.meta import delegates
|
|
21
|
+
from fastcore.xml import FT
|
|
22
|
+
from .components import *
|
|
23
|
+
from .xtend import *
|
|
24
|
+
|
|
25
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
26
|
+
_all_ = ['AltGlyph', 'AltGlyphDef', 'AltGlyphItem', 'Animate', 'AnimateColor', 'AnimateMotion', 'AnimateTransform', 'ClipPath', 'Color_profile', 'Cursor', 'Defs', 'Desc', 'FeBlend', 'FeColorMatrix', 'FeComponentTransfer', 'FeComposite', 'FeConvolveMatrix', 'FeDiffuseLighting', 'FeDisplacementMap', 'FeDistantLight', 'FeFlood', 'FeFuncA', 'FeFuncB', 'FeFuncG', 'FeFuncR', 'FeGaussianBlur', 'FeImage', 'FeMerge', 'FeMergeNode', 'FeMorphology', 'FeOffset', 'FePointLight', 'FeSpecularLighting', 'FeSpotLight', 'FeTile', 'FeTurbulence', 'Filter', 'Font', 'Font_face', 'Font_face_format', 'Font_face_name', 'Font_face_src', 'Font_face_uri', 'ForeignObject', 'G', 'Glyph', 'GlyphRef', 'Hkern', 'Image', 'LinearGradient', 'Marker', 'Mask', 'Metadata', 'Missing_glyph', 'Mpath', 'Pattern', 'RadialGradient', 'Set', 'Stop', 'Switch', 'Symbol', 'TextPath', 'Tref', 'Tspan', 'Use', 'View', 'Vkern', 'Template']
|
|
27
|
+
|
|
28
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
29
|
+
g = globals()
|
|
30
|
+
for o in _all_: g[o] = partial(ft_hx, o[0].lower() + o[1:])
|
|
31
|
+
|
|
32
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
33
|
+
def Svg(*args, viewBox=None, h=None, w=None, height=None, width=None, **kwargs):
|
|
34
|
+
"An SVG tag; xmlns is added automatically, and viewBox defaults to height and width if not provided"
|
|
35
|
+
if h: height=h
|
|
36
|
+
if w: width=w
|
|
37
|
+
if not viewBox and height and width: viewBox=f'0 0 {width} {height}'
|
|
38
|
+
return ft_svg('svg', *args, xmlns="http://www.w3.org/2000/svg", viewBox=viewBox, height=height, width=width, **kwargs)
|
|
39
|
+
|
|
40
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
41
|
+
@delegates(ft_hx)
|
|
42
|
+
def ft_svg(tag: str, *c, transform=None, opacity=None, clip=None, mask=None, filter=None,
|
|
43
|
+
vector_effect=None, pointer_events=None, **kwargs):
|
|
44
|
+
"Create a standard `FT` element with some SVG-specific attrs"
|
|
45
|
+
return ft_hx(tag, *c, transform=transform, opacity=opacity, clip=clip, mask=mask, filter=filter,
|
|
46
|
+
vector_effect=vector_effect, pointer_events=pointer_events, **kwargs)
|
|
47
|
+
|
|
48
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
49
|
+
@delegates(ft_svg)
|
|
50
|
+
def Rect(width, height, x=0, y=0, fill=None, stroke=None, stroke_width=None, rx=None, ry=None, **kwargs):
|
|
51
|
+
"A standard SVG `rect` element"
|
|
52
|
+
return ft_svg('rect', width=width, height=height, x=x, y=y, fill=fill,
|
|
53
|
+
stroke=stroke, stroke_width=stroke_width, rx=rx, ry=ry, **kwargs)
|
|
54
|
+
|
|
55
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
56
|
+
@delegates(ft_svg)
|
|
57
|
+
def Circle(r, cx=0, cy=0, fill=None, stroke=None, stroke_width=None, **kwargs):
|
|
58
|
+
"A standard SVG `circle` element"
|
|
59
|
+
return ft_svg('circle', r=r, cx=cx, cy=cy, fill=fill, stroke=stroke, stroke_width=stroke_width, **kwargs)
|
|
60
|
+
|
|
61
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
62
|
+
@delegates(ft_svg)
|
|
63
|
+
def Ellipse(rx, ry, cx=0, cy=0, fill=None, stroke=None, stroke_width=None, **kwargs):
|
|
64
|
+
"A standard SVG `ellipse` element"
|
|
65
|
+
return ft_svg('ellipse', rx=rx, ry=ry, cx=cx, cy=cy, fill=fill, stroke=stroke, stroke_width=stroke_width, **kwargs)
|
|
66
|
+
|
|
67
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
68
|
+
def transformd(translate=None, scale=None, rotate=None, skewX=None, skewY=None, matrix=None):
|
|
69
|
+
"Create an SVG `transform` kwarg dict"
|
|
70
|
+
funcs = []
|
|
71
|
+
if translate is not None: funcs.append(f"translate{translate}")
|
|
72
|
+
if scale is not None: funcs.append(f"scale{scale}")
|
|
73
|
+
if rotate is not None: funcs.append(f"rotate({','.join(map(str,rotate))})")
|
|
74
|
+
if skewX is not None: funcs.append(f"skewX({skewX})")
|
|
75
|
+
if skewY is not None: funcs.append(f"skewY({skewY})")
|
|
76
|
+
if matrix is not None: funcs.append(f"matrix{matrix}")
|
|
77
|
+
return dict(transform=' '.join(funcs)) if funcs else {}
|
|
78
|
+
|
|
79
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
80
|
+
@delegates(ft_svg)
|
|
81
|
+
def Line(x1, y1, x2=0, y2=0, stroke='black', w=None, stroke_width=1, **kwargs):
|
|
82
|
+
"A standard SVG `line` element"
|
|
83
|
+
if w: stroke_width=w
|
|
84
|
+
return ft_svg('line', x1=x1, y1=y1, x2=x2, y2=y2, stroke=stroke, stroke_width=stroke_width, **kwargs)
|
|
85
|
+
|
|
86
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
87
|
+
@delegates(ft_svg)
|
|
88
|
+
def Polyline(*args, points=None, fill=None, stroke=None, stroke_width=None, **kwargs):
|
|
89
|
+
"A standard SVG `polyline` element"
|
|
90
|
+
if points is None: points = ' '.join(f"{x},{y}" for x, y in args)
|
|
91
|
+
return ft_svg('polyline', points=points, fill=fill, stroke=stroke, stroke_width=stroke_width, **kwargs)
|
|
92
|
+
|
|
93
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
94
|
+
@delegates(ft_svg)
|
|
95
|
+
def Polygon(*args, points=None, fill=None, stroke=None, stroke_width=None, **kwargs):
|
|
96
|
+
"A standard SVG `polygon` element"
|
|
97
|
+
if points is None: points = ' '.join(f"{x},{y}" for x, y in args)
|
|
98
|
+
return ft_svg('polygon', points=points, fill=fill, stroke=stroke, stroke_width=stroke_width, **kwargs)
|
|
99
|
+
|
|
100
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
101
|
+
@delegates(ft_svg)
|
|
102
|
+
def Text(*args, x=0, y=0, font_family=None, font_size=None, fill=None, text_anchor=None,
|
|
103
|
+
dominant_baseline=None, font_weight=None, font_style=None, text_decoration=None, **kwargs):
|
|
104
|
+
"A standard SVG `text` element"
|
|
105
|
+
return ft_svg('text', *args, x=x, y=y, font_family=font_family, font_size=font_size, fill=fill,
|
|
106
|
+
text_anchor=text_anchor, dominant_baseline=dominant_baseline, font_weight=font_weight,
|
|
107
|
+
font_style=font_style, text_decoration=text_decoration, **kwargs)
|
|
108
|
+
|
|
109
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
110
|
+
class PathFT(FT):
|
|
111
|
+
def _append_cmd(self, cmd):
|
|
112
|
+
if not isinstance(getattr(self, 'd'), str): self.d = cmd
|
|
113
|
+
else: self.d += f' {cmd}'
|
|
114
|
+
return self
|
|
115
|
+
|
|
116
|
+
def M(self, x, y):
|
|
117
|
+
"Move to."
|
|
118
|
+
return self._append_cmd(f'M{x} {y}')
|
|
119
|
+
|
|
120
|
+
def L(self, x, y):
|
|
121
|
+
"Line to."
|
|
122
|
+
return self._append_cmd(f'L{x} {y}')
|
|
123
|
+
|
|
124
|
+
def H(self, x):
|
|
125
|
+
"Horizontal line to."
|
|
126
|
+
return self._append_cmd(f'H{x}')
|
|
127
|
+
|
|
128
|
+
def V(self, y):
|
|
129
|
+
"Vertical line to."
|
|
130
|
+
return self._append_cmd(f'V{y}')
|
|
131
|
+
|
|
132
|
+
def Z(self):
|
|
133
|
+
"Close path."
|
|
134
|
+
return self._append_cmd('Z')
|
|
135
|
+
|
|
136
|
+
def C(self, x1, y1, x2, y2, x, y):
|
|
137
|
+
"Cubic Bézier curve."
|
|
138
|
+
return self._append_cmd(f'C{x1} {y1} {x2} {y2} {x} {y}')
|
|
139
|
+
|
|
140
|
+
def S(self, x2, y2, x, y):
|
|
141
|
+
"Smooth cubic Bézier curve."
|
|
142
|
+
return self._append_cmd(f'S{x2} {y2} {x} {y}')
|
|
143
|
+
|
|
144
|
+
def Q(self, x1, y1, x, y):
|
|
145
|
+
"Quadratic Bézier curve."
|
|
146
|
+
return self._append_cmd(f'Q{x1} {y1} {x} {y}')
|
|
147
|
+
|
|
148
|
+
def T(self, x, y):
|
|
149
|
+
"Smooth quadratic Bézier curve."
|
|
150
|
+
return self._append_cmd(f'T{x} {y}')
|
|
151
|
+
|
|
152
|
+
def A(self, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y):
|
|
153
|
+
"Elliptical Arc."
|
|
154
|
+
return self._append_cmd(f'A{rx} {ry} {x_axis_rotation} {large_arc_flag} {sweep_flag} {x} {y}')
|
|
155
|
+
|
|
156
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
157
|
+
@delegates(ft_svg)
|
|
158
|
+
def Path(d='', fill=None, stroke=None, stroke_width=None, **kwargs):
|
|
159
|
+
"Create a standard `path` SVG element. This is a special object"
|
|
160
|
+
return ft_svg('path', fill=fill, stroke=stroke, stroke_width=stroke_width, ft_cls=PathFT, **kwargs)
|
|
161
|
+
|
|
162
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
163
|
+
svg_inb = dict(hx_select="svg>*")
|
|
164
|
+
|
|
165
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
166
|
+
def SvgOob(*args, **kwargs):
|
|
167
|
+
"Wraps an SVG shape as required for an HTMX OOB swap"
|
|
168
|
+
return Template(Svg(*args, **kwargs))
|
|
169
|
+
|
|
170
|
+
# %% ../nbs/api/05_svg.ipynb
|
|
171
|
+
def SvgInb(*args, **kwargs):
|
|
172
|
+
"Wraps an SVG shape as required for an HTMX inband swap"
|
|
173
|
+
return Svg(*args, **kwargs), HtmxResponseHeaders(hx_reselect='svg>*')
|
|
@@ -51,8 +51,9 @@ def render_toasts(sess):
|
|
|
51
51
|
hx_swap_oob="afterbegin:body")
|
|
52
52
|
|
|
53
53
|
def toast_after(resp, req, sess):
|
|
54
|
-
if sk in sess and isinstance(resp, FT): req.injects.append(render_toasts(sess))
|
|
54
|
+
if sk in sess and (not resp or isinstance(resp, (tuple,FT))): req.injects.append(render_toasts(sess))
|
|
55
55
|
|
|
56
56
|
def setup_toasts(app):
|
|
57
|
-
app.
|
|
58
|
-
app.
|
|
57
|
+
app.hdrs += (Style(toast_css), Script(toast_js, type="module"))
|
|
58
|
+
app.after.append(toast_after)
|
|
59
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-fasthtml
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
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
|
|
@@ -15,7 +15,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: fastcore>=1.7.
|
|
18
|
+
Requires-Dist: fastcore>=1.7.5
|
|
19
19
|
Requires-Dist: python-dateutil
|
|
20
20
|
Requires-Dist: starlette>0.33
|
|
21
21
|
Requires-Dist: oauthlib
|
|
@@ -114,6 +114,31 @@ the new version returned by the second route.
|
|
|
114
114
|
This “hypermedia-based” approach to web development is a powerful way to
|
|
115
115
|
build web applications.
|
|
116
116
|
|
|
117
|
+
### Getting help from AI
|
|
118
|
+
|
|
119
|
+
Because FastHTML is newer than most LLMs, AI systems like Cursor,
|
|
120
|
+
ChatGPT, Claude, and Copilot won’t give useful answers about it. To fix
|
|
121
|
+
that problem, we’ve provided an LLM-friendly guide that teaches them how
|
|
122
|
+
to use FastHTML. To use it, add this link for your AI helper to use:
|
|
123
|
+
|
|
124
|
+
- [/llms-ctx.txt](https://docs.fastht.ml/llms-ctx.txt)
|
|
125
|
+
|
|
126
|
+
This example is in a format based on recommendations from Anthropic for
|
|
127
|
+
use with [Claude
|
|
128
|
+
Projects](https://support.anthropic.com/en/articles/9517075-what-are-projects).
|
|
129
|
+
This works so well that we’ve actually found that Claude can provide
|
|
130
|
+
even better information than our own documentation! For instance, read
|
|
131
|
+
through [this annotated Claude
|
|
132
|
+
chat](https://gist.github.com/jph00/9559b0a563f6a370029bec1d1cc97b74)
|
|
133
|
+
for some great getting-started information, entirely generated from a
|
|
134
|
+
project using the above text file as context.
|
|
135
|
+
|
|
136
|
+
If you use Cursor, type `@doc` then choose “*Add new doc*”, and use the
|
|
137
|
+
/llms-ctx.txt link above. The context file is auto-generated from our
|
|
138
|
+
[`llms.txt`](https://llmstxt.org/) (our proposed standard for providing
|
|
139
|
+
AI-friendly information)—you can generate alternative versions suitable
|
|
140
|
+
for other models as needed.
|
|
141
|
+
|
|
117
142
|
## Next Steps
|
|
118
143
|
|
|
119
144
|
Start with the official sources to learn more about FastHTML:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[DEFAULT]
|
|
2
2
|
repo = fasthtml
|
|
3
3
|
lib_name = fasthtml
|
|
4
|
-
version = 0.5.
|
|
4
|
+
version = 0.5.2
|
|
5
5
|
min_python = 3.10
|
|
6
6
|
license = apache2
|
|
7
|
-
requirements = fastcore>=1.7.
|
|
7
|
+
requirements = fastcore>=1.7.5 python-dateutil starlette>0.33 oauthlib itsdangerous uvicorn[standard]>=0.30 httpx fastlite>=0.0.9 python-multipart beautifulsoup4
|
|
8
8
|
dev_requirements = ipython lxml
|
|
9
9
|
black_formatting = False
|
|
10
10
|
conda_user = fastai
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
from fasthtml.components import ft_hx, Svg, AltGlyph, AltGlyphDef, AltGlyphItem, Animate, AnimateColor, AnimateMotion, AnimateTransform, Circle, ClipPath, Color_profile, Cursor, Defs, Desc, Ellipse, FeBlend, FeColorMatrix, FeComponentTransfer, FeComposite, FeConvolveMatrix, FeDiffuseLighting, FeDisplacementMap, FeDistantLight, FeFlood, FeFuncA, FeFuncB, FeFuncG, FeFuncR, FeGaussianBlur, FeImage, FeMerge, FeMergeNode, FeMorphology, FeOffset, FePointLight, FeSpecularLighting, FeSpotLight, FeTile, FeTurbulence, Filter, Font, Font_face, Font_face_format, Font_face_name, Font_face_src, Font_face_uri, ForeignObject, G, Glyph, GlyphRef, Hkern, Image, Line, LinearGradient, Marker, Mask, Metadata, Missing_glyph, Mpath, Pattern, Polygon, Polyline, RadialGradient, Rect, Set, Stop, Switch, Symbol, Text, TextPath, Tref, Tspan, Use, View, Vkern
|
|
2
|
-
|
|
3
|
-
def Path(*args, **kwargs): return ft_hx('path', *args, **kwargs)
|
|
4
|
-
|
|
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.5.1 → python-fasthtml-0.5.2}/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
|