forked from zauberzeug/nicegui
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
executable file
·133 lines (104 loc) · 5.25 KB
/
main.py
File metadata and controls
executable file
·133 lines (104 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python3
import os
from pathlib import Path
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import Response
from nicegui import app, core, ui
from nicegui.page_arguments import RouteMatch
from website import design as d
from website import documentation, examples_page, fly, header, imprint_privacy, main_page, rate_limits, svg
from website.components import footer_section
from website.documentation.intersection_observer import IntersectionObserver as intersection_observer
@app.add_middleware
class DocsSetCacheControlMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
response = await call_next(request)
if request.url.path.startswith('/fonts/') or request.url.path.startswith('/static/'):
response.headers['Cache-Control'] = core.app.config.cache_control_directives
elif request.url.path.startswith('/examples/images/'):
response.headers['Cache-Control'] = 'public, max-age=86400' # 1 day
return response
# session middleware is required for demo in documentation
app.add_middleware(SessionMiddleware, secret_key=os.environ.get('NICEGUI_SECRET_KEY', ''))
rate_limits.setup()
on_fly = fly.setup()
app.add_static_files('/favicon', str(Path(__file__).parent / 'website' / 'favicon'))
app.add_static_files('/fonts', str(Path(__file__).parent / 'website' / 'fonts'))
app.add_static_files('/static', str(Path(__file__).parent / 'website' / 'static'))
app.add_static_file(local_file=svg.PATH / 'logo.png', url_path='/logo.png')
app.add_static_file(local_file=svg.PATH / 'logo_square.png', url_path='/logo_square.png')
documentation.build_search_index()
documentation.build_tree()
@app.post('/dark_mode')
async def _post_dark_mode(request: Request) -> None:
app.storage.browser['dark_mode'] = (await request.json()).get('value')
class custom_sub_pages(ui.sub_pages):
def _render_page(self, match: RouteMatch) -> bool:
if match.path == '/' and match.remaining_path:
return False
return super()._render_page(match)
@ui.page('/')
@ui.page('/examples')
@ui.page('/documentation')
@ui.page('/documentation/{path:path}')
@ui.page('/imprint_privacy')
def _main_page() -> None:
ui.context.client.content.classes('p-0 gap-0')
header.add_head_html()
with ui.left_drawer().classes(f'column no-wrap gap-1 {d.BG_FOOTER} {d.BORDER_R} p-8') as menu:
tree = ui.tree([], label_key='title', on_select=lambda e: ui.navigate.to(f'/documentation/{e.value}')) \
.classes(r'w-full [&_.q-tree\_\_children]:pl-4') \
.props('accordion no-connectors no-selection-unset icon=chevron_right color=primary')
tree.visible = False
spinner = ui.image('/static/loading.gif').classes('w-8 h-8 m-auto').props('no-spinner no-transition')
@intersection_observer
def update_tree() -> None:
tree.props['nodes'] = documentation.tree.nodes
tree.visible = True
spinner.delete()
menu_button = header.add_header(menu)
window_state = {'is_desktop': None}
ui.on('is_desktop', lambda v: window_state.update(is_desktop=v.args))
ui.add_head_html('''
<script>
const mediaQuery = window.matchMedia('(min-width: 1024px)');
mediaQuery.addEventListener('change', e => emitEvent('is_desktop', e.matches));
window.addEventListener('load', () => emitEvent('is_desktop', mediaQuery.matches));
</script>
''')
custom_sub_pages({
'/': main_page.create,
'/examples': examples_page.create,
'/documentation': lambda: documentation.render_page(documentation.registry['']),
'/documentation/{name}': lambda name: _documentation_detail_page(name, tree),
'/imprint_privacy': imprint_privacy.create,
}, show_404=False).classes('w-full')
footer_section.create()
def _update_menu(path: str):
if path.startswith('/documentation/'):
menu_button.visible = True
if window_state['is_desktop'] is not None:
menu.value = window_state['is_desktop']
else:
menu_button.visible = False
menu.value = False
ui.context.client.sub_pages_router.on_path_changed(_update_menu)
_update_menu(ui.context.client.sub_pages_router.current_path)
def _documentation_detail_page(name: str, tree: ui.tree) -> None:
tree.props.update(expanded=documentation.tree.ancestors(name))
tree.update()
if name in documentation.registry:
documentation.render_page(documentation.registry[name])
elif name in documentation.redirects:
ui.navigate.to('/documentation/' + documentation.redirects[name])
else:
ui.status_code(404)
with ui.column().classes('w-full min-h-[50vh] items-center justify-center text-center p-16'):
ui.label(f'Documentation for "{name}" could not be found.')
@app.get('/status')
def _status():
return 'Ok'
# NOTE: do not reload on fly.io (see https://github.com/zauberzeug/nicegui/discussions/1720#discussioncomment-7288741)
ui.run(uvicorn_reload_includes='*.py, *.css, *.html', reload=not on_fly, reconnect_timeout=10.0)