python-fasthtml 0.12.9__tar.gz → 0.12.34__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.
Files changed (45) hide show
  1. {python_fasthtml-0.12.9/python_fasthtml.egg-info → python_fasthtml-0.12.34}/PKG-INFO +23 -7
  2. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/README.md +4 -4
  3. python_fasthtml-0.12.34/fasthtml/__init__.py +2 -0
  4. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/_modidx.py +29 -4
  5. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/cli.py +1 -1
  6. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/common.py +1 -0
  7. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/components.py +42 -25
  8. python_fasthtml-0.12.34/fasthtml/components.pyi +209 -0
  9. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/core.py +165 -67
  10. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/core.pyi +30 -14
  11. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/fastapp.py +4 -2
  12. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/jupyter.py +31 -15
  13. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/oauth.py +43 -18
  14. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/starlette.py +1 -1
  15. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/toaster.py +13 -7
  16. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/xtend.py +97 -10
  17. python_fasthtml-0.12.34/fasthtml/xtend.pyi +130 -0
  18. python_fasthtml-0.12.34/pyproject.toml +11 -0
  19. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34/python_fasthtml.egg-info}/PKG-INFO +23 -7
  20. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/python_fasthtml.egg-info/requires.txt +1 -1
  21. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/settings.ini +6 -5
  22. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/setup.py +2 -2
  23. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/tests/test_toaster.py +6 -1
  24. python_fasthtml-0.12.9/fasthtml/__init__.py +0 -2
  25. python_fasthtml-0.12.9/fasthtml/components.pyi +0 -204
  26. python_fasthtml-0.12.9/fasthtml/xtend.pyi +0 -124
  27. python_fasthtml-0.12.9/pyproject.toml +0 -3
  28. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/CONTRIBUTING.md +0 -0
  29. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/LICENSE +0 -0
  30. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/MANIFEST.in +0 -0
  31. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/authmw.py +0 -0
  32. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/basics.py +0 -0
  33. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/ft.py +0 -0
  34. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/js.py +0 -0
  35. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/katex.js +0 -0
  36. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/live_reload.py +0 -0
  37. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/pico.py +0 -0
  38. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/stripe_otp.py +0 -0
  39. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/fasthtml/svg.py +0 -0
  40. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/python_fasthtml.egg-info/SOURCES.txt +0 -0
  41. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/python_fasthtml.egg-info/dependency_links.txt +0 -0
  42. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/python_fasthtml.egg-info/entry_points.txt +0 -0
  43. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/python_fasthtml.egg-info/not-zip-safe +0 -0
  44. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/python_fasthtml.egg-info/top_level.txt +0 -0
  45. {python_fasthtml-0.12.9 → python_fasthtml-0.12.34}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-fasthtml
3
- Version: 0.12.9
3
+ Version: 0.12.34
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
@@ -11,11 +11,14 @@ Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: Natural Language :: English
13
13
  Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
14
17
  Classifier: License :: OSI Approved :: Apache Software License
15
18
  Requires-Python: >=3.10
16
19
  Description-Content-Type: text/markdown
17
20
  License-File: LICENSE
18
- Requires-Dist: fastcore>=1.7.18
21
+ Requires-Dist: fastcore>=1.8.1
19
22
  Requires-Dist: python-dateutil
20
23
  Requires-Dist: starlette>0.33
21
24
  Requires-Dist: oauthlib
@@ -30,6 +33,19 @@ Requires-Dist: ipython; extra == "dev"
30
33
  Requires-Dist: lxml; extra == "dev"
31
34
  Requires-Dist: pysymbol_llm; extra == "dev"
32
35
  Requires-Dist: monsterui; extra == "dev"
36
+ Dynamic: author
37
+ Dynamic: author-email
38
+ Dynamic: classifier
39
+ Dynamic: description
40
+ Dynamic: description-content-type
41
+ Dynamic: home-page
42
+ Dynamic: keywords
43
+ Dynamic: license
44
+ Dynamic: license-file
45
+ Dynamic: provides-extra
46
+ Dynamic: requires-dist
47
+ Dynamic: requires-python
48
+ Dynamic: summary
33
49
 
34
50
  # FastHTML
35
51
 
@@ -123,7 +139,7 @@ ChatGPT, Claude, and Copilot won’t give useful answers about it. To fix
123
139
  that problem, we’ve provided an LLM-friendly guide that teaches them how
124
140
  to use FastHTML. To use it, add this link for your AI helper to use:
125
141
 
126
- - [/llms-ctx.txt](https://docs.fastht.ml/llms-ctx.txt)
142
+ - [/llms-ctx.txt](https://www.fastht.ml/docs/llms-ctx.txt)
127
143
 
128
144
  This example is in a format based on recommendations from Anthropic for
129
145
  use with [Claude
@@ -147,8 +163,8 @@ Start with the official sources to learn more about FastHTML:
147
163
 
148
164
  - [About](https://fastht.ml/about): Learn about the core ideas behind
149
165
  FastHTML
150
- - [Documentation](https://docs.fastht.ml): Learn from examples how to
151
- write FastHTML code
166
+ - [Documentation](https://www.fastht.ml/docs): Learn from examples how
167
+ to write FastHTML code
152
168
  - [Idiomatic
153
169
  app](https://github.com/AnswerDotAI/fasthtml/blob/main/examples/adv_app.py):
154
170
  Heavily commented source code walking through a complete application,
@@ -171,7 +187,7 @@ repo’s notebooks and the official FastHTML examples repo:
171
187
  Then explore the small but growing third-party ecosystem of FastHTML
172
188
  tutorials, notebooks, libraries, and components:
173
189
 
174
- - [FastHTML Gallery](https://fastht.ml/gallery): Learn from minimal
190
+ - [FastHTML Gallery](https://gallery.fastht.ml): Learn from minimal
175
191
  examples of components (ie chat bubbles, click-to-edit, infinite
176
192
  scroll, etc)
177
193
  - [Creating Custom FastHTML Tags for Markdown
@@ -90,7 +90,7 @@ ChatGPT, Claude, and Copilot won’t give useful answers about it. To fix
90
90
  that problem, we’ve provided an LLM-friendly guide that teaches them how
91
91
  to use FastHTML. To use it, add this link for your AI helper to use:
92
92
 
93
- - [/llms-ctx.txt](https://docs.fastht.ml/llms-ctx.txt)
93
+ - [/llms-ctx.txt](https://www.fastht.ml/docs/llms-ctx.txt)
94
94
 
95
95
  This example is in a format based on recommendations from Anthropic for
96
96
  use with [Claude
@@ -114,8 +114,8 @@ Start with the official sources to learn more about FastHTML:
114
114
 
115
115
  - [About](https://fastht.ml/about): Learn about the core ideas behind
116
116
  FastHTML
117
- - [Documentation](https://docs.fastht.ml): Learn from examples how to
118
- write FastHTML code
117
+ - [Documentation](https://www.fastht.ml/docs): Learn from examples how
118
+ to write FastHTML code
119
119
  - [Idiomatic
120
120
  app](https://github.com/AnswerDotAI/fasthtml/blob/main/examples/adv_app.py):
121
121
  Heavily commented source code walking through a complete application,
@@ -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/gallery): Learn from minimal
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
@@ -0,0 +1,2 @@
1
+ __version__ = "0.12.34"
2
+ from .core import *
@@ -1,8 +1,8 @@
1
1
  # Autogenerated by nbdev
2
2
 
3
3
  d = { 'settings': { 'branch': 'main',
4
- 'doc_baseurl': '/',
5
- 'doc_host': 'https://docs.fastht.ml',
4
+ 'doc_baseurl': '/docs/',
5
+ 'doc_host': 'https://www.fastht.ml',
6
6
  'git_url': 'https://github.com/AnswerDotAI/fasthtml',
7
7
  'lib_path': 'fasthtml'},
8
8
  'syms': { 'fasthtml.authmw': {},
@@ -45,7 +45,11 @@ d = { 'settings': { 'branch': 'main',
45
45
  'fasthtml.core.FastHTML._add_ws': ('api/core.html#fasthtml._add_ws', 'fasthtml/core.py'),
46
46
  'fasthtml.core.FastHTML._endp': ('api/core.html#fasthtml._endp', 'fasthtml/core.py'),
47
47
  'fasthtml.core.FastHTML.add_route': ('api/core.html#fasthtml.add_route', 'fasthtml/core.py'),
48
+ 'fasthtml.core.FastHTML.devtools_json': ('api/core.html#fasthtml.devtools_json', 'fasthtml/core.py'),
49
+ 'fasthtml.core.FastHTML.get_client': ('api/core.html#fasthtml.get_client', 'fasthtml/core.py'),
48
50
  'fasthtml.core.FastHTML.route': ('api/core.html#fasthtml.route', 'fasthtml/core.py'),
51
+ 'fasthtml.core.FastHTML.set_lifespan': ('api/core.html#fasthtml.set_lifespan', 'fasthtml/core.py'),
52
+ 'fasthtml.core.FastHTML.setup_ws': ('api/core.html#fasthtml.setup_ws', 'fasthtml/core.py'),
49
53
  'fasthtml.core.FastHTML.static_route': ('api/core.html#fasthtml.static_route', 'fasthtml/core.py'),
50
54
  'fasthtml.core.FastHTML.static_route_exts': ('api/core.html#fasthtml.static_route_exts', 'fasthtml/core.py'),
51
55
  'fasthtml.core.FastHTML.ws': ('api/core.html#fasthtml.ws', 'fasthtml/core.py'),
@@ -58,6 +62,8 @@ d = { 'settings': { 'branch': 'main',
58
62
  'fasthtml.core.HtmxHeaders.__bool__': ('api/core.html#htmxheaders.__bool__', 'fasthtml/core.py'),
59
63
  'fasthtml.core.HtmxResponseHeaders': ('api/core.html#htmxresponseheaders', 'fasthtml/core.py'),
60
64
  'fasthtml.core.HttpHeader': ('api/core.html#httpheader', 'fasthtml/core.py'),
65
+ 'fasthtml.core.JSONResponse': ('api/core.html#jsonresponse', 'fasthtml/core.py'),
66
+ 'fasthtml.core.JSONResponse.render': ('api/core.html#jsonresponse.render', 'fasthtml/core.py'),
61
67
  'fasthtml.core.MiddlewareBase': ('api/core.html#middlewarebase', 'fasthtml/core.py'),
62
68
  'fasthtml.core.MiddlewareBase.__call__': ('api/core.html#middlewarebase.__call__', 'fasthtml/core.py'),
63
69
  'fasthtml.core.Redirect': ('api/core.html#redirect', 'fasthtml/core.py'),
@@ -68,10 +74,14 @@ d = { 'settings': { 'branch': 'main',
68
74
  'fasthtml.core.RouteFuncs.__getattr__': ('api/core.html#routefuncs.__getattr__', 'fasthtml/core.py'),
69
75
  'fasthtml.core.RouteFuncs.__init__': ('api/core.html#routefuncs.__init__', 'fasthtml/core.py'),
70
76
  'fasthtml.core.RouteFuncs.__setattr__': ('api/core.html#routefuncs.__setattr__', 'fasthtml/core.py'),
77
+ 'fasthtml.core.StaticNoCache': ('api/core.html#staticnocache', 'fasthtml/core.py'),
78
+ 'fasthtml.core.StaticNoCache.file_response': ( 'api/core.html#staticnocache.file_response',
79
+ 'fasthtml/core.py'),
71
80
  'fasthtml.core.StringConvertor.to_string': ('api/core.html#stringconvertor.to_string', 'fasthtml/core.py'),
72
81
  'fasthtml.core._add_ids': ('api/core.html#_add_ids', 'fasthtml/core.py'),
73
82
  'fasthtml.core._annotations': ('api/core.html#_annotations', 'fasthtml/core.py'),
74
83
  'fasthtml.core._apply_ft': ('api/core.html#_apply_ft', 'fasthtml/core.py'),
84
+ 'fasthtml.core._canonical': ('api/core.html#_canonical', 'fasthtml/core.py'),
75
85
  'fasthtml.core._find_p': ('api/core.html#_find_p', 'fasthtml/core.py'),
76
86
  'fasthtml.core._find_targets': ('api/core.html#_find_targets', 'fasthtml/core.py'),
77
87
  'fasthtml.core._find_wsp': ('api/core.html#_find_wsp', 'fasthtml/core.py'),
@@ -87,6 +97,7 @@ d = { 'settings': { 'branch': 'main',
87
97
  'fasthtml.core._mk_list': ('api/core.html#_mk_list', 'fasthtml/core.py'),
88
98
  'fasthtml.core._mk_locfunc': ('api/core.html#_mk_locfunc', 'fasthtml/core.py'),
89
99
  'fasthtml.core._params': ('api/core.html#_params', 'fasthtml/core.py'),
100
+ 'fasthtml.core._part_resp': ('api/core.html#_part_resp', 'fasthtml/core.py'),
90
101
  'fasthtml.core._resp': ('api/core.html#_resp', 'fasthtml/core.py'),
91
102
  'fasthtml.core._send_ws': ('api/core.html#_send_ws', 'fasthtml/core.py'),
92
103
  'fasthtml.core._to_htmx_header': ('api/core.html#_to_htmx_header', 'fasthtml/core.py'),
@@ -98,7 +109,6 @@ d = { 'settings': { 'branch': 'main',
98
109
  'fasthtml.core._wrap_ws': ('api/core.html#_wrap_ws', 'fasthtml/core.py'),
99
110
  'fasthtml.core._ws_endp': ('api/core.html#_ws_endp', 'fasthtml/core.py'),
100
111
  'fasthtml.core._xt_cts': ('api/core.html#_xt_cts', 'fasthtml/core.py'),
101
- 'fasthtml.core._xt_resp': ('api/core.html#_xt_resp', 'fasthtml/core.py'),
102
112
  'fasthtml.core.cookie': ('api/core.html#cookie', 'fasthtml/core.py'),
103
113
  'fasthtml.core.decode_uri': ('api/core.html#decode_uri', 'fasthtml/core.py'),
104
114
  'fasthtml.core.def_hdrs': ('api/core.html#def_hdrs', 'fasthtml/core.py'),
@@ -106,6 +116,7 @@ d = { 'settings': { 'branch': 'main',
106
116
  'fasthtml.core.flat_xt': ('api/core.html#flat_xt', 'fasthtml/core.py'),
107
117
  'fasthtml.core.form2dict': ('api/core.html#form2dict', 'fasthtml/core.py'),
108
118
  'fasthtml.core.get_key': ('api/core.html#get_key', 'fasthtml/core.py'),
119
+ 'fasthtml.core.is_full_page': ('api/core.html#is_full_page', 'fasthtml/core.py'),
109
120
  'fasthtml.core.nested_name': ('api/core.html#nested_name', 'fasthtml/core.py'),
110
121
  'fasthtml.core.noop_body': ('api/core.html#noop_body', 'fasthtml/core.py'),
111
122
  'fasthtml.core.parse_form': ('api/core.html#parse_form', 'fasthtml/core.py'),
@@ -114,7 +125,6 @@ d = { 'settings': { 'branch': 'main',
114
125
  'fasthtml.core.reg_re_param': ('api/core.html#reg_re_param', 'fasthtml/core.py'),
115
126
  'fasthtml.core.respond': ('api/core.html#respond', 'fasthtml/core.py'),
116
127
  'fasthtml.core.serve': ('api/core.html#serve', 'fasthtml/core.py'),
117
- 'fasthtml.core.setup_ws': ('api/core.html#setup_ws', 'fasthtml/core.py'),
118
128
  'fasthtml.core.signal_shutdown': ('api/core.html#signal_shutdown', 'fasthtml/core.py'),
119
129
  'fasthtml.core.snake2hyphens': ('api/core.html#snake2hyphens', 'fasthtml/core.py'),
120
130
  'fasthtml.core.unqid': ('api/core.html#unqid', 'fasthtml/core.py'),
@@ -132,6 +142,7 @@ d = { 'settings': { 'branch': 'main',
132
142
  'fasthtml.jupyter.JupyUvi': ('api/jupyter.html#jupyuvi', 'fasthtml/jupyter.py'),
133
143
  'fasthtml.jupyter.JupyUvi.__init__': ('api/jupyter.html#jupyuvi.__init__', 'fasthtml/jupyter.py'),
134
144
  'fasthtml.jupyter.JupyUvi.start': ('api/jupyter.html#jupyuvi.start', 'fasthtml/jupyter.py'),
145
+ 'fasthtml.jupyter.JupyUvi.start_async': ('api/jupyter.html#jupyuvi.start_async', 'fasthtml/jupyter.py'),
135
146
  'fasthtml.jupyter.JupyUvi.stop': ('api/jupyter.html#jupyuvi.stop', 'fasthtml/jupyter.py'),
136
147
  'fasthtml.jupyter.JupyUviAsync': ('api/jupyter.html#jupyuviasync', 'fasthtml/jupyter.py'),
137
148
  'fasthtml.jupyter.JupyUviAsync.__init__': ( 'api/jupyter.html#jupyuviasync.__init__',
@@ -166,6 +177,8 @@ d = { 'settings': { 'branch': 'main',
166
177
  'fasthtml.oauth.GitHubAppClient.__init__': ('api/oauth.html#githubappclient.__init__', 'fasthtml/oauth.py'),
167
178
  'fasthtml.oauth.GoogleAppClient': ('api/oauth.html#googleappclient', 'fasthtml/oauth.py'),
168
179
  'fasthtml.oauth.GoogleAppClient.__init__': ('api/oauth.html#googleappclient.__init__', 'fasthtml/oauth.py'),
180
+ 'fasthtml.oauth.GoogleAppClient.consent_url': ( 'api/oauth.html#googleappclient.consent_url',
181
+ 'fasthtml/oauth.py'),
169
182
  'fasthtml.oauth.GoogleAppClient.creds': ('api/oauth.html#googleappclient.creds', 'fasthtml/oauth.py'),
170
183
  'fasthtml.oauth.GoogleAppClient.from_file': ( 'api/oauth.html#googleappclient.from_file',
171
184
  'fasthtml/oauth.py'),
@@ -189,6 +202,7 @@ d = { 'settings': { 'branch': 'main',
189
202
  'fasthtml/oauth.py'),
190
203
  'fasthtml.oauth._AppClient.retr_id': ('api/oauth.html#_appclient.retr_id', 'fasthtml/oauth.py'),
191
204
  'fasthtml.oauth._AppClient.retr_info': ('api/oauth.html#_appclient.retr_info', 'fasthtml/oauth.py'),
205
+ 'fasthtml.oauth.get_host': ('api/oauth.html#get_host', 'fasthtml/oauth.py'),
192
206
  'fasthtml.oauth.load_creds': ('api/oauth.html#load_creds', 'fasthtml/oauth.py'),
193
207
  'fasthtml.oauth.redir_url': ('api/oauth.html#redir_url', 'fasthtml/oauth.py'),
194
208
  'fasthtml.oauth.url_match': ('api/oauth.html#url_match', 'fasthtml/oauth.py')},
@@ -247,8 +261,16 @@ d = { 'settings': { 'branch': 'main',
247
261
  'fasthtml.xtend.CheckboxX': ('api/xtend.html#checkboxx', 'fasthtml/xtend.py'),
248
262
  'fasthtml.xtend.Favicon': ('api/xtend.html#favicon', 'fasthtml/xtend.py'),
249
263
  'fasthtml.xtend.Form': ('api/xtend.html#form', 'fasthtml/xtend.py'),
264
+ 'fasthtml.xtend.Fragment': ('api/xtend.html#fragment', 'fasthtml/xtend.py'),
265
+ 'fasthtml.xtend.Fragment.__init__': ('api/xtend.html#fragment.__init__', 'fasthtml/xtend.py'),
250
266
  'fasthtml.xtend.Hidden': ('api/xtend.html#hidden', 'fasthtml/xtend.py'),
251
267
  'fasthtml.xtend.HtmxOn': ('api/xtend.html#htmxon', 'fasthtml/xtend.py'),
268
+ 'fasthtml.xtend.LdContactPoint': ('api/xtend.html#ldcontactpoint', 'fasthtml/xtend.py'),
269
+ 'fasthtml.xtend.LdCourse': ('api/xtend.html#ldcourse', 'fasthtml/xtend.py'),
270
+ 'fasthtml.xtend.LdCourseInstance': ('api/xtend.html#ldcourseinstance', 'fasthtml/xtend.py'),
271
+ 'fasthtml.xtend.LdJson': ('api/xtend.html#ldjson', 'fasthtml/xtend.py'),
272
+ 'fasthtml.xtend.LdOrg': ('api/xtend.html#ldorg', 'fasthtml/xtend.py'),
273
+ 'fasthtml.xtend.LdWebsite': ('api/xtend.html#ldwebsite', 'fasthtml/xtend.py'),
252
274
  'fasthtml.xtend.Nbsp': ('api/xtend.html#nbsp', 'fasthtml/xtend.py'),
253
275
  'fasthtml.xtend.Now': ('api/xtend.html#now', 'fasthtml/xtend.py'),
254
276
  'fasthtml.xtend.On': ('api/xtend.html#on', 'fasthtml/xtend.py'),
@@ -266,6 +288,9 @@ d = { 'settings': { 'branch': 'main',
266
288
  'fasthtml.xtend.jsd': ('api/xtend.html#jsd', 'fasthtml/xtend.py'),
267
289
  'fasthtml.xtend.loose_format': ('api/xtend.html#loose_format', 'fasthtml/xtend.py'),
268
290
  'fasthtml.xtend.replace_css_vars': ('api/xtend.html#replace_css_vars', 'fasthtml/xtend.py'),
291
+ 'fasthtml.xtend.robots_txt': ('api/xtend.html#robots_txt', 'fasthtml/xtend.py'),
269
292
  'fasthtml.xtend.run_js': ('api/xtend.html#run_js', 'fasthtml/xtend.py'),
293
+ 'fasthtml.xtend.sitemap_url': ('api/xtend.html#sitemap_url', 'fasthtml/xtend.py'),
294
+ 'fasthtml.xtend.sitemap_xml': ('api/xtend.html#sitemap_xml', 'fasthtml/xtend.py'),
270
295
  'fasthtml.xtend.undouble_braces': ('api/xtend.html#undouble_braces', 'fasthtml/xtend.py'),
271
296
  'fasthtml.xtend.with_sid': ('api/xtend.html#with_sid', 'fasthtml/xtend.py')}}}
@@ -36,7 +36,7 @@ def railway_deploy(
36
36
  ):
37
37
  """Deploy a FastHTML app to Railway"""
38
38
  nm,ver = check_output("railway --version".split()).decode().split()
39
- assert nm=='railwayapp', f'Unexpected railway version string: {nm}'
39
+ assert nm.startswith('railway'), f'Unexpected railway version string: {nm}'
40
40
  if ver2tuple(ver)<(3,8): return print("Please update your railway CLI version to 3.8 or higher")
41
41
  cp = run("railway status --json".split(), capture_output=True)
42
42
  if not cp.returncode:
@@ -1,5 +1,6 @@
1
1
  import uvicorn
2
2
  from dataclasses import dataclass
3
+ from typing import Any
3
4
 
4
5
  from .starlette import *
5
6
  from fastcore.utils import *
@@ -3,22 +3,23 @@
3
3
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/01_components.ipynb.
4
4
 
5
5
  # %% auto 0
6
- __all__ = ['named', 'html_attrs', 'hx_attrs', 'hx_attrs_annotations', 'show', 'attrmap_x', 'ft_html', 'ft_hx', 'File',
7
- 'fill_form', 'fill_dataclass', 'find_inputs', 'html2ft', 'sse_message', 'A', 'Abbr', 'Address', 'Area',
8
- 'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi', 'Bdo', 'Blockquote', 'Body', 'Br', 'Button', 'Canvas',
9
- 'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dd', 'Del', 'Details', 'Dfn', 'Dialog',
10
- 'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe', 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form', 'H1',
11
- 'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header', 'Hgroup', 'Hr', 'I', 'Iframe', 'Img', 'Input', 'Ins', 'Kbd',
12
- 'Label', 'Legend', 'Li', 'Link', 'Main', 'Map', 'Mark', 'Menu', 'Meta', 'Meter', 'Nav', 'Noscript', 'Object',
13
- 'Ol', 'Optgroup', 'Option', 'Output', 'P', 'Picture', 'PortalExperimental', 'Pre', 'Progress', 'Q', 'Rp',
14
- 'Rt', 'Ruby', 'S', 'Samp', 'Script', 'Search', 'Section', 'Select', 'Slot', 'Small', 'Source', 'Span',
15
- 'Strong', 'Style', 'Sub', 'Summary', 'Sup', 'Table', 'Tbody', 'Td', 'Template', 'Textarea', 'Tfoot', 'Th',
16
- 'Thead', 'Time', 'Title', 'Tr', 'Track', 'U', 'Ul', 'Var', 'Video', 'Wbr']
6
+ __all__ = ['named', 'html_attrs', 'hx_attrs', 'hx_evts', 'js_evts', 'hx_attrs_annotations', 'hx_evt_attrs', 'js_evt_attrs',
7
+ 'evt_attrs', 'attrmap_x', 'ft_html', 'ft_hx', 'File', 'show', 'fill_form', 'fill_dataclass', 'find_inputs',
8
+ 'html2ft', 'sse_message', 'A', 'Abbr', 'Address', 'Area', 'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi',
9
+ 'Bdo', 'Blockquote', 'Body', 'Br', 'Button', 'Canvas', 'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data',
10
+ 'Datalist', 'Dd', 'Del', 'Details', 'Dfn', 'Dialog', 'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe',
11
+ 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header',
12
+ 'Hgroup', 'Hr', 'I', 'Iframe', 'Img', 'Input', 'Ins', 'Kbd', 'Label', 'Legend', 'Li', 'Link', 'Main', 'Map',
13
+ 'Mark', 'Menu', 'Meta', 'Meter', 'Nav', 'Noscript', 'Object', 'Ol', 'Optgroup', 'Option', 'Output', 'P',
14
+ 'Picture', 'PortalExperimental', 'Pre', 'Progress', 'Q', 'Rp', 'Rt', 'Ruby', 'S', 'Samp', 'Script', 'Search',
15
+ 'Section', 'Select', 'Slot', 'Small', 'Source', 'Span', 'Strong', 'Style', 'Sub', 'Summary', 'Sup', 'Table',
16
+ 'Tbody', 'Td', 'Template', 'Textarea', 'Tfoot', 'Th', 'Thead', 'Time', 'Title', 'Tr', 'Track', 'U', 'Ul',
17
+ 'Var', 'Video', 'Wbr']
17
18
 
18
19
  # %% ../nbs/api/01_components.ipynb
19
20
  from dataclasses import dataclass, asdict, is_dataclass, make_dataclass, replace, astuple, MISSING
20
21
  from bs4 import BeautifulSoup, Comment
21
- from typing import Literal, Optional
22
+ from typing import Literal, Mapping, Optional
22
23
 
23
24
  from fastcore.utils import *
24
25
  from fastcore.xml import *
@@ -31,15 +32,9 @@ import types, json
31
32
  try: from IPython import display
32
33
  except ImportError: display=None
33
34
 
34
- # %% ../nbs/api/01_components.ipynb
35
- def show(ft,*rest):
36
- "Renders FT Components into HTML within a Jupyter notebook."
37
- if rest: ft = (ft,)+rest
38
- display.display(display.HTML(to_xml(ft)))
39
-
40
35
  # %% ../nbs/api/01_components.ipynb
41
36
  @patch
42
- def __str__(self:FT): return self.id if self.id else object.__str__(self)
37
+ def __str__(self:FT): return self.id if self.id else to_xml(self, indent=False)
43
38
 
44
39
  # %% ../nbs/api/01_components.ipynb
45
40
  @patch
@@ -53,6 +48,9 @@ def __add__(self:FT, b): return f'{self}{b}'
53
48
  named = set('a button form frame iframe img input map meta object param select textarea'.split())
54
49
  html_attrs = 'id cls title style accesskey contenteditable dir draggable enterkeyhint hidden inert inputmode lang popover spellcheck tabindex translate'.split()
55
50
  hx_attrs = 'get post put delete patch trigger target swap swap_oob include select select_oob indicator push_url confirm disable replace_url vals disabled_elt ext headers history history_elt indicator inherit params preserve prompt replace_url request sync validate'
51
+
52
+ hx_evts = 'abort afterOnLoad afterProcessNode afterRequest afterSettle afterSwap beforeCleanupElement beforeOnLoad beforeProcessNode beforeRequest beforeSwap beforeSend beforeTransition configRequest confirm historyCacheError historyCacheMiss historyCacheMissError historyCacheMissLoad historyRestore beforeHistorySave load noSSESourceError onLoadError oobAfterSwap oobBeforeSwap oobErrorNoTarget prompt pushedIntoHistory replacedInHistory responseError sendAbort sendError sseError sseOpen swapError targetError timeout validation:validate validation:failed validation:halted xhr:abort xhr:loadend xhr:loadstart xhr:progress'
53
+ js_evts = "blur change contextmenu focus input invalid reset select submit keydown keypress keyup click dblclick mousedown mouseenter mouseleave mousemove mouseout mouseover mouseup wheel"
56
54
  hx_attrs = [f'hx_{o}' for o in hx_attrs.split()]
57
55
  hx_attrs_annotations = {
58
56
  "hx_swap": Literal["innerHTML", "outerHTML", "afterbegin", "beforebegin", "beforeend", "afterend", "delete", "none"] | str,
@@ -68,6 +66,10 @@ hx_attrs_annotations |= {o: str for o in set(hx_attrs) - set(hx_attrs_annotation
68
66
  hx_attrs_annotations = {k: Optional[v] for k,v in hx_attrs_annotations.items()}
69
67
  hx_attrs = html_attrs + hx_attrs
70
68
 
69
+ hx_evt_attrs = ['hx_on__'+camel2snake(o).replace(':','_') for o in hx_evts.split()]
70
+ js_evt_attrs = ['hx_on_'+o for o in js_evts.split()]
71
+ evt_attrs = js_evt_attrs+hx_evt_attrs
72
+
71
73
  # %% ../nbs/api/01_components.ipynb
72
74
  def attrmap_x(o):
73
75
  if o.startswith('_at_'): o = '@'+o[4:]
@@ -82,7 +84,7 @@ fh_cfg['auto_name']=True
82
84
 
83
85
  # %% ../nbs/api/01_components.ipynb
84
86
  def ft_html(tag: str, *c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=None, **kwargs):
85
- ds,c = partition(c, risinstance(dict))
87
+ ds,c = partition(c, risinstance(Mapping))
86
88
  for d in ds: kwargs = {**kwargs, **d}
87
89
  if ft_cls is None: ft_cls = fh_cfg.ft_cls
88
90
  if attrmap is None: attrmap=fh_cfg.attrmap
@@ -96,7 +98,7 @@ def ft_html(tag: str, *c, id=None, cls=None, title=None, style=None, attrmap=Non
96
98
  return ft_cls(tag,c,kw, void_=tag in voids)
97
99
 
98
100
  # %% ../nbs/api/01_components.ipynb
99
- @use_kwargs(hx_attrs, keep=True)
101
+ @use_kwargs(hx_attrs+evt_attrs, keep=True)
100
102
  def ft_hx(tag: str, *c, target_id=None, hx_vals=None, hx_target=None, **kwargs):
101
103
  if hx_vals: kwargs['hx_vals'] = json.dumps(hx_vals) if isinstance (hx_vals,dict) else hx_vals
102
104
  if hx_target: kwargs['hx_target'] = '#'+hx_target.id if isinstance(hx_target,FT) else hx_target
@@ -122,6 +124,20 @@ def File(fname):
122
124
  "Use the unescaped text in file `fname` directly"
123
125
  return NotStr(Path(fname).read_text())
124
126
 
127
+ # %% ../nbs/api/01_components.ipynb
128
+ def show(ft, *rest, iframe=False, height='auto', style=None):
129
+ "Renders FT Components into HTML within a Jupyter notebook."
130
+ if isinstance(ft, str): ft = Safe(ft)
131
+ if rest: ft = (ft,)+rest
132
+ res = to_xml(ft)
133
+ if iframe:
134
+ style = "border: none; " + (style or "")
135
+ cfg = dict(frameborder=0, width='100%', height=height, style=style)
136
+ res = to_xml(Iframe(srcdoc=res, **cfg))
137
+ with warnings.catch_warnings():
138
+ warnings.simplefilter("ignore", UserWarning)
139
+ display.display(display.HTML(res))
140
+
125
141
  # %% ../nbs/api/01_components.ipynb
126
142
  def _fill_item(item, obj):
127
143
  if not isinstance(item,FT): return item
@@ -191,18 +207,19 @@ _re_h2x_attr_key = re.compile(r'^[A-Za-z_-][\w-]*$')
191
207
  def html2ft(html, attr1st=False):
192
208
  """Convert HTML to an `ft` expression"""
193
209
  rev_map = {'class': 'cls', 'for': 'fr'}
194
-
210
+
195
211
  def _parse(elm, lvl=0, indent=4):
196
- if isinstance(elm, str): return repr(elm.strip()) if elm.strip() else ''
212
+ if isinstance(elm, str): return repr(elm.strip("\n")) if elm.strip() else ''
197
213
  if isinstance(elm, list): return '\n'.join(_parse(o, lvl) for o in elm)
198
214
  tag_name = elm.name.capitalize().replace("-", "_")
199
215
  if tag_name=='[document]': return _parse(list(elm.children), lvl)
200
216
  cts = elm.contents
201
- cs = [repr(c.strip()) if isinstance(c, str) else _parse(c, lvl+1)
217
+ cs = [repr(c.strip("\n")) if isinstance(c, str) else _parse(c, lvl+1)
202
218
  for c in cts if str(c).strip()]
203
219
  attrs, exotic_attrs = [], {}
204
220
  for key, value in sorted(elm.attrs.items(), key=lambda x: x[0]=='class'):
205
- if isinstance(value,(tuple,list)): value = " ".join(value)
221
+ if value is None or value == True: value = True # handle boolean attributes
222
+ elif isinstance(value,(tuple,list)): value = " ".join(value)
206
223
  key, value = rev_map.get(key, key), value or True
207
224
  if _re_h2x_attr_key.match(key): attrs.append(f'{key.replace("-", "_")}={value!r}')
208
225
  else: exotic_attrs[key] = value