AItool commited on
Commit
99ac817
·
verified ·
1 Parent(s): 120d592

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +282 -0
app.py ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fasthtml.common import *
2
+ from home_components import *
3
+ from content import *
4
+
5
+ _blank = dict(target="_blank", rel="noopener noreferrer")
6
+ description = 'Modern web applications in pure Python'
7
+
8
+ def benefit(title, content):
9
+ return Div(
10
+ H3(title, cls=f"text-black heading-3"),
11
+ P(content, cls=f"l-body mt-6 lg:mt-6"),
12
+ cls="w-full p-6 bg-soft-yellow rounded-2xl xl:p-12 lg:h-[22rem] lg:w-[26rem]")
13
+
14
+ def faq_item(question, answer, id):
15
+ return accordion(
16
+ id=id, question=question, answer=answer,
17
+ question_cls="text-black s-body",
18
+ answer_cls="s-body text-black/80 col-span-full",
19
+ container_cls=f"{col} justify-between bg-soft-blue rounded-[1.25rem] {bnset}"
20
+ )
21
+
22
+ def hero_section():
23
+ return (
24
+ Header(
25
+ Nav(
26
+ A(
27
+ Img(src='/assets/logo.svg', alt='FastHTML', width='105', height='24'),
28
+ href='#'),
29
+ A('Read docs', href='/docs', **_blank,
30
+ cls=f'bg-black text-white py-2 px-4 s-body rounded-[62.5rem] hover:bg-black/80 transition-colors duration-300 px-4 py-1 h-10 {center} justify-center'),
31
+ cls=f'py-2 px-4 {between} items-center rounded-full w-full max-w-[400px] bg-white/50 backdrop-blur-lg border border-white/20'),
32
+ cls=f'fixed top-0 w-full left-0 p-4 {center} justify-center z-50'),
33
+ Section(
34
+ Div(
35
+ File('assets/hero-shapes.svg'),
36
+ cls='absolute z-0 lg:-top-[15%] top-0 left-1/2 -translate-x-1/2 grid grid-cols-1 grid-rows-1 w-[120%] aspect-square max-w-[2048px] min-w-[900px]'),
37
+ Div(
38
+ Div(cls='lg:flex-1 max-lg:basis-[152px]'),
39
+ Div(
40
+ H1(description, cls='heading-1 max-w-3xl'),
41
+ P('Built on solid web foundations, not the latest fads - with\nFastHTML you can get started on anything from simple dashboards to\nscalable web applications in minutes.',
42
+ cls='m-body max-w-[40rem] text-center'),
43
+ cls=f'flex-1 {col} items-center justify-center gap-6 text-center w-full text-black'
44
+ ),
45
+ Div(
46
+ A('Learn more', href='https://fastht.ml/about', **_blank,
47
+ cls=f'{bnset} m-body px-4 py-1 rounded-full bg-black hover:bg-black/80 transition-colors duration-300 text-white h-[76px] w-full max-w-[350px] flex items-center justify-center'),
48
+ video_button('Watch intro', '/assets/thumb.png', '7min 30sec', "QqZUzkPcU7A?si=lTtHuMT5HPC66-49"),
49
+ cls=f'flex-1 {center} justify-center content-center flex-wrap lg:gap-6 gap-4 m-body'),
50
+ video_player('Try now'),
51
+ cls=f'{col} flex-1 relative px-4 lg:px-16'),
52
+ cls=f'{col} relative w-full h-screen max-h-[1024px] min-h-[720px] overflow-hidden bg-grey')
53
+ )
54
+
55
+ def code_display(file_name, code_snippet, snippet_id):
56
+ return Div(
57
+ Div(
58
+ Div(
59
+ P(file_name, cls=f"xs-mono-body {center}"),
60
+ Button(
61
+ Div(
62
+ Img(cls="button-content w-4 h-4", src="assets/icons/copy-icon.svg", alt="Copy"),
63
+ Span("Copied!", cls=f"absolute bg-inherit rounded-l-[0.5rem] {center} py-2 h-full right-0 copied-text overflow-hidden w-0 peer-clicked:w-fit s-body text-white/80 transition-transform transition-all duration-600 ease-out"),
64
+ cls=f"relative bg-black/20 rounded-[0.5rem] {center} p-2 h-8 w-fit"),
65
+ cls="copy-button"),
66
+ cls=f"w-full {between}"),
67
+ cls="bg-black/20 text-white/80 w-full max-w-2xl lg:max-w-xl p-4 rounded-2xl"),
68
+ Pre(
69
+ Code(code_snippet, cls="python w-full mono-body"),
70
+ id=snippet_id,
71
+ cls="code-snippet relative max-h-[25rem] overflow-hidden hide-scrollbar"),
72
+ Div(cls="absolute code-fade-out bottom-0 left-0 right-0 h-16 bg-gradient-to-b from-transparent to-[#3a2234] pointer-events-none"),
73
+ cls=f"relative {col} gap-6 lg:max-w-[45rem] w-full overflow-hidden"
74
+ )
75
+
76
+ def code_demo(title, file_name, code_snippet, demo_content, is_active=False):
77
+ snippet_id = f"{title.lower().replace(' ', '-')}-code-snippet"
78
+ demo_cls = f"{center} my-11 p-4 flex-none whitespace-normal justify-center h-96 rounded-3xl bg-soft-purple lg:p-8 w-full max-w-2xl lg:max-w-md lg:mx-28 lg:my-8 overflow-y-auto {'lg:items-start hide-scrollbar' if snippet_id == 'reusable-code-snippet' else ''}"
79
+ return Div(
80
+ code_display(file_name, code_snippet, snippet_id),
81
+ Div(demo_content, cls=demo_cls),
82
+ aria_labelledby=f"tab-{title.lower().replace(' ', '-')}", role="tabpanel",
83
+ cls=f"code-container pt-8 lg:pt-16 tab-panel relative hide-scrollbar toggle-element {col} lg:flex-row lg:justify-between overflow-hidden w-full lg:max-w-[1440px] xl:mx-auto {'hidden' if not is_active else ''}",
84
+ id=f"{title.lower().replace(' ', '-')}-code-demo")
85
+
86
+ def tab_button(title, is_active=False):
87
+ classes = f"z-10 button-container flex-none relative px-8 py-2 w-[10.59375rem] h-11 rounded-full {center}"
88
+ active = f"{classes} current active transition-all duration-300 text-white"
89
+ inactive = f"{classes} text-white/80 hover:text-white"
90
+ return Li(
91
+ Button(
92
+ Div(title, cls="m-body w-max mx-auto"),
93
+ id=f"tab-{title.lower().replace(' ', '-')}",
94
+ cls="toggle-button w-full cursor-pointer",
95
+ data_target=f"{title.lower().replace(' ', '-')}-code-demo",
96
+ tabindex="-1" if not is_active else None,
97
+ name=f"tab-{title.lower().replace(' ', '-')}"),
98
+ role="tab", aria_selected="true" if is_active else "false",
99
+ cls=active if is_active else inactive)
100
+
101
+ async def component_preview_section():
102
+ cs = await components()
103
+ return Section(
104
+ Div(
105
+ Div(
106
+ H2("This home page is a FastHTML app.", cls="text-white heading-2 pt-8"),
107
+ P("Click the buttons below to see four small, live components in action.", cls=f"text-white pt-8 l-body {maxrem(32)}"),
108
+ cls=f"{maxrem(50)} mx-auto {col} {center} text-center gap-6"
109
+ ),
110
+ *[code_demo(title, file_name, code_snippet, demo_content, i == 0) for i, (title, file_name, code_snippet, demo_content) in enumerate(cs)],
111
+ Ul(
112
+ *[tab_button(title, i == 0) for i, (title, _, _, _) in enumerate(cs)],
113
+ Div(id="highlighter", cls=f"{inset} z-0 highlighter w-[10.59375rem] absolute bg-white/20 h-[2.75rem] rounded-[62.5rem] transition-transform duration-300"),
114
+ role="tablist", id="tab-list",
115
+ cls=f"relative mt-12 text-white/80 flex-none rounded-[62.5rem] bg-black/20 p-2 max-w-full overflow-x-auto lg:mx-auto {center} hide-scrollbar lg:max-w-[43.375rem]"),
116
+ cls=f"{col} {center}"),
117
+ cls="relative bg-purple px-4 lg:px-16 pb-24 -mt-8 lg:-mt-10 flex-col xl:items-center items-start gap-6 lg:gap-16 rounded-t-3xl lg:rounded-t-[2.5rem] overflow-x-hidden")
118
+
119
+ def stacked_cards_section():
120
+ return Section(
121
+ Div(
122
+ Div(
123
+ Div(
124
+ P("TECH STACK", cls="mono-body text-opacity-60"),
125
+ H2("FastHTML scales up and scales down.", cls="text-black heading-2"),
126
+ P("Read more about our ",
127
+ A("design philosophy here", href="https://fastht.ml/about/vision", **_blank,
128
+ cls="border-b-2 border-b-black/30 hover:border-b-black/80"),
129
+ ", or click a button below:",
130
+ cls=f"l-body {maxrem(32)}"),
131
+ cls=f"{maxrem(50)} mx-auto {col} {center} text-center gap-6"),
132
+ cls="py-8 lg:pt-16 px-4 lg:px-16 rounded-t-3xl lg:rounded-t-[2.5rem] bg-green"),
133
+ cls="bg-yellow sticky top-0 bottom-[calc(100%-300px)] w-full"),
134
+ Div(
135
+ Div(
136
+ *[stacked_card(title, description, tech_stack, "bg-soft-green") for title, description, tech_stack in stacked],
137
+ id="stacked-cards", cls=f"{maxrem(50)} mx-auto"),
138
+ cls="px-4 lg:px-16 w-full bg-green pt-8"),
139
+ id="stacked-cards-section", cls="relative")
140
+
141
+ def samples_section():
142
+ text = "FastHTML can be used for everything from collaborative games to multi-modal UI. We've selected small self-contained examples for you to learn from."
143
+ return section_wrapper(
144
+ (section_header("SAMPLES", "See FastHTML in action", text, max_width=40),
145
+ Div(
146
+ *[Div(
147
+ A(
148
+ File(f"assets/{svg}"),
149
+ Div(
150
+ P(name, cls="border-b-2 border-b-black/30 hover:border-b-black/80 regular-body"),
151
+ Img(src=f"{icons}/arrow-up-right.svg", alt="Arrow right icon", cls="group-hover:translate-y-[-0.1rem] transition-all ease-in-out duration-300"),
152
+ cls=f"{gap2} transition-transform transform relative items-center mt-4 lg:mt-6"),
153
+ href=url, cls=f"{col} items-center"),
154
+ cls="group px-2"
155
+ ) for name, svg, url in samples],
156
+ cls="grid max-w-5xl lg:grid-cols-4 lg:max-w-7xl lg:gap-x-12 grid-cols-2 gap-x-4 gap-y-8 w-full mx-auto"),
157
+ A("Discover all", href=eg_url,
158
+ cls=f"{bnset} bg-black text-white py-2 px-4 s-body rounded-full hover:bg-black/70 transition-colors duration-300")),
159
+ bg_color="grey")
160
+
161
+ def how_it_works_section():
162
+ msg = "With FastHTML you create good-looking modern web applications in pure Python and deploy them in minutes."
163
+ return section_wrapper(
164
+ (Div(
165
+ section_header( "GET STARTED IN MINUTES", "The fastest way to create a real web application.", msg),
166
+ cls="max-w-3xl w-full mx-auto flex-col items-center text-center gap-6 mb-8 lg:mb-8"),
167
+ Div(*[benefit(title, content) for title, content in benefits],
168
+ cls=f"{col} w-full lg:flex-row gap-4 items-center lg:gap-8 max-w-7xl mx-auto justify-center"),),
169
+ bg_color="yellow", flex=False)
170
+
171
+ def faq_section():
172
+ return section_wrapper(
173
+ Div(
174
+ section_header( "FAQ", "Questions? Answers.", "Your top FastHTML questions clarified.",
175
+ max_width=21, center=False),
176
+ Div(
177
+ *[faq_item(question, answer, i+3) for i, (question, answer) in enumerate(faqs)],
178
+ cls=f"{col} gap-4 {maxrem(32)} transition ease-out delay-[300ms]"),
179
+ cls=f"{section_base} w-full mx-auto lg:flex-row items-start max-w-7xl"),
180
+ bg_color="blue")
181
+
182
+ def testimonials_section():
183
+ testimonial_cards = [testimonial_card(i, *args) for i, args in enumerate(testimonials)]
184
+ return section_wrapper(
185
+ Div(
186
+ section_header(
187
+ "LOVE IS IN THE AIR", "What the experts say",
188
+ "Top web programmers tell us that they love working with FastHTML.",
189
+ max_width=21, center=False),
190
+ carousel(testimonial_cards),
191
+ cls=f"{section_base} {maxrem(90)} mx-auto lg:flex-row items-start"),
192
+ bg_color="pink")
193
+
194
+ def footer_link(text, href, **kw):
195
+ return Li(A(
196
+ Span(text, cls="border-b border-b-transparent border-b-white/50"),
197
+ Img(src=f"{icons}/arrow-up-right-white.svg", alt="Arrow right icon", width="16", height="16", cls="w-4 h-4"),
198
+ href=href, cls=f"{gap2} items-center hover:text-white border-b border-b-transparent hover:border-b-white", **kw))
199
+
200
+ def footer():
201
+ return Section(
202
+ Div(
203
+ Div(
204
+ P("© 2024 onwards AnswerDotAI. All rights reserved.", cls="mr-auto"),
205
+ Nav(
206
+ Ul(
207
+ footer_link("Github", "https://github.com/AnswerDotAI/fasthtml", **_blank),
208
+ footer_link("Join Discord", "https://discord.gg/qcXvcxMhdP", **_blank),
209
+ footer_link("Docs", "/docs", **_blank),
210
+ footer_link("Site design", "https://tinloof.com/", **_blank),
211
+ cls="flex max-lg:flex-col max-lg:items-start gap-4 lg:gap-6 flex-wrap")),
212
+ cls="relative z-[1] mono-s flex max-lg:flex-col gap-6 text-white/80 px-4 lg:px-16 pb-16"),
213
+ Div(
214
+ Div(
215
+ File("assets/footer-shapes.svg"),
216
+ cls=f"absolute z-0 lg:-top-[15%] top-0 left-1/2 -translate-x-1/2 grid grid-cols-1 grid-rows-1 lg:w-[150%] w-[200%] aspect-square max-w-none min-w-max"),
217
+ File("assets/footer-path.svg"),
218
+ cls=f"relative z-0 w-full px-4 lg:px-16 pb-1 {col} flex-1 justify-end"),
219
+ cls=f"relative w-full h-[420px] lg:h-[600px] {col} pt-8 lg:pt-12 rounded-t-3xl lg:rounded-t-[2.5rem] bg-black overflow-hidden -mt-8 lg:-mt-10"))
220
+
221
+ hdrs = [
222
+ Meta(charset='UTF-8'),
223
+ Meta(name='viewport', content='width=device-width, initial-scale=1.0, maximum-scale=1.0'),
224
+ Meta(name='description', content=description),
225
+ *Favicon('favicon.ico', 'favicon-dark.ico'),
226
+ *Socials(title='FastHTML',
227
+ description=description,
228
+ site_name='fastht.ml',
229
+ twitter_site='@answerdotai',
230
+ image=f'/assets/og-sq.png',
231
+ url=''),
232
+ # surrsrc,
233
+ Script(src='https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js'),
234
+ scopesrc,
235
+ Link(href='css/main.css', rel='stylesheet'),
236
+ Link(href='css/tailwind.css', rel='stylesheet'),
237
+ Link(href='css/stack.css', rel='stylesheet'),
238
+ Link(href='css/preview-stack.css', rel='stylesheet'),
239
+ Link(href='css/highlighter-theme.css', rel='stylesheet')]
240
+
241
+ from about.main import app as about_app
242
+ routes = [Mount(f"/about", about_app),
243
+ Mount('/docs', StaticFiles(directory='docs', html=True)),
244
+ ]
245
+
246
+ bodykw = {"class": "relative bg-black font-geist text-black/80 font-details-off"}
247
+ app,rt = fast_app(hdrs=hdrs, default_hdrs=False, bodykw=bodykw, on_startup=[startup], routes=routes)
248
+
249
+
250
+ scripts = (
251
+ Script(src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'),
252
+ Script(src='https://cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/highlightjs-line-numbers.min.js'),
253
+ # Script(src='js/videoPopup.js'),
254
+ Script(src='js/pythonHighlighter.js'),
255
+ Script(src='js/carouselScroll.js'),
256
+ Script(src='js/stack.js'),
257
+ Script(src='js/togglePreview.js'),
258
+ Script(src='js/codeOverflow.js'),
259
+ Script(src='js/copyCode.js'),
260
+ )
261
+
262
+ from fastcore.xtras import timed_cache
263
+
264
+ @timed_cache(seconds=60)
265
+ async def home():
266
+ return (Title(f"FastHTML - {description}"),
267
+ Main(
268
+ hero_section(),
269
+ await component_preview_section(),
270
+ how_it_works_section(),
271
+ stacked_cards_section(),
272
+ samples_section(),
273
+ faq_section(),
274
+ testimonials_section(),
275
+ footer()),
276
+ *scripts)
277
+
278
+ @rt("/")
279
+ async def get(): return await home()
280
+
281
+ serve()
282
+