|
@@ -10,9 +10,10 @@ if True:
|
|
|
|
|
|
import os
|
|
|
from pathlib import Path
|
|
|
+from typing import Awaitable, Callable, Optional
|
|
|
|
|
|
from fastapi import Request
|
|
|
-from fastapi.responses import FileResponse, RedirectResponse
|
|
|
+from fastapi.responses import FileResponse, RedirectResponse, Response
|
|
|
from starlette.middleware.sessions import SessionMiddleware
|
|
|
|
|
|
import prometheus
|
|
@@ -28,24 +29,25 @@ from website.style import example_link, features, heading, link_target, section_
|
|
|
prometheus.start_monitor(app)
|
|
|
|
|
|
# session middleware is required for demo in documentation and prometheus
|
|
|
-app.add_middleware(SessionMiddleware, secret_key='NiceGUI is awesome!')
|
|
|
+app.add_middleware(SessionMiddleware, secret_key=os.environ.get('NICEGUI_SECRET_KEY', ''))
|
|
|
|
|
|
app.add_static_files('/favicon', str(Path(__file__).parent / 'website' / 'favicon'))
|
|
|
app.add_static_files('/fonts', str(Path(__file__).parent / 'website' / 'fonts'))
|
|
|
|
|
|
|
|
|
@app.get('/logo.png')
|
|
|
-def logo():
|
|
|
+def logo() -> FileResponse:
|
|
|
return FileResponse(svg.PATH / 'logo.png', media_type='image/png')
|
|
|
|
|
|
|
|
|
@app.get('/logo_square.png')
|
|
|
-def logo():
|
|
|
+def logo_square() -> FileResponse:
|
|
|
return FileResponse(svg.PATH / 'logo_square.png', media_type='image/png')
|
|
|
|
|
|
|
|
|
@app.middleware('http')
|
|
|
-async def redirect_reference_to_documentation(request: Request, call_next):
|
|
|
+async def redirect_reference_to_documentation(request: Request,
|
|
|
+ call_next: Callable[[Request], Awaitable[Response]]) -> Response:
|
|
|
if request.url.path == '/reference':
|
|
|
return RedirectResponse('/documentation')
|
|
|
return await call_next(request)
|
|
@@ -61,10 +63,10 @@ def add_head_html() -> None:
|
|
|
ui.add_head_html(f"<style>{(Path(__file__).parent / 'website' / 'static' / 'style.css').read_text()}</style>")
|
|
|
|
|
|
|
|
|
-def add_header() -> None:
|
|
|
+def add_header(menu: Optional[ui.left_drawer] = None) -> None:
|
|
|
menu_items = {
|
|
|
- 'Features': '/#features',
|
|
|
'Installation': '/#installation',
|
|
|
+ 'Features': '/#features',
|
|
|
'Demos': '/#demos',
|
|
|
'Documentation': '/documentation',
|
|
|
'Examples': '/#examples',
|
|
@@ -73,6 +75,8 @@ def add_header() -> None:
|
|
|
with ui.header() \
|
|
|
.classes('items-center duration-200 p-0 px-4 no-wrap') \
|
|
|
.style('box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)'):
|
|
|
+ if menu:
|
|
|
+ ui.button(on_click=menu.toggle).props('flat color=white icon=menu round').classes('lg:hidden')
|
|
|
with ui.link(target=index_page).classes('row gap-4 items-center no-wrap mr-auto'):
|
|
|
svg.face().classes('w-8 stroke-white stroke-2')
|
|
|
svg.word().classes('w-24')
|
|
@@ -83,15 +87,22 @@ def add_header() -> None:
|
|
|
with ui.row().classes('max-lg:hidden'):
|
|
|
for title, target in menu_items.items():
|
|
|
ui.link(title, target).classes(replace='text-lg text-white')
|
|
|
- with ui.link(target='https://discord.gg/TEpFeAaF4f'):
|
|
|
+ with ui.link(target='https://discord.gg/TEpFeAaF4f').classes('max-[435px]:hidden').tooltip('Discord'):
|
|
|
svg.discord().classes('fill-white scale-125 m-1')
|
|
|
- with ui.link(target='https://github.com/zauberzeug/nicegui/'):
|
|
|
+ with ui.link(target='https://www.reddit.com/r/nicegui/').classes('max-[385px]:hidden').tooltip('Reddit'):
|
|
|
+ svg.reddit().classes('fill-white scale-125 m-1')
|
|
|
+ with ui.link(target='https://github.com/zauberzeug/nicegui/').tooltip('GitHub'):
|
|
|
svg.github().classes('fill-white scale-125 m-1')
|
|
|
- add_star()
|
|
|
+ add_star().classes('max-[480px]:hidden')
|
|
|
+ with ui.row().classes('lg:hidden'):
|
|
|
+ with ui.button().props('flat color=white icon=more_vert round'):
|
|
|
+ with ui.menu().classes('bg-primary text-white text-lg').props(remove='no-parent-event'):
|
|
|
+ for title, target in menu_items.items():
|
|
|
+ ui.menu_item(title, on_click=lambda _, target=target: ui.open(target))
|
|
|
|
|
|
|
|
|
@ui.page('/')
|
|
|
-async def index_page(client: Client):
|
|
|
+async def index_page(client: Client) -> None:
|
|
|
client.content.classes('p-0 gap-0')
|
|
|
add_head_html()
|
|
|
add_header()
|
|
@@ -114,19 +125,71 @@ async def index_page(client: Client):
|
|
|
with ui.column().classes('text-white max-w-4xl'):
|
|
|
heading('Interact with Python through buttons, dialogs, 3D scenes, plots and much more.')
|
|
|
with ui.column().classes('gap-2 bold-links arrow-links text-lg'):
|
|
|
- ui.markdown(
|
|
|
- 'NiceGUI handles all the web development details for you. '
|
|
|
- 'So you can focus on writing Python code. '
|
|
|
- 'Anything from short scripts and dashboards to full robotics projects, IoT solutions, '
|
|
|
- 'smart home automations and machine learning projects can benefit from having all code in one place.'
|
|
|
- )
|
|
|
- ui.markdown(
|
|
|
- 'Available as '
|
|
|
- '[PyPI package](https://pypi.org/project/nicegui/), '
|
|
|
- '[Docker image](https://hub.docker.com/r/zauberzeug/nicegui) and on '
|
|
|
- '[GitHub](https://github.com/zauberzeug/nicegui).')
|
|
|
+ ui.markdown('''
|
|
|
+ NiceGUI manages web development details, letting you focus on Python code for diverse applications,
|
|
|
+ including robotics, IoT solutions, smart home automation, and machine learning.
|
|
|
+ Designed to work smoothly with connected peripherals like webcams and GPIO pins in IoT setups,
|
|
|
+ NiceGUI streamlines the management of all your code in one place.
|
|
|
+ <br><br>
|
|
|
+ With a gentle learning curve, NiceGUI is user-friendly for beginners
|
|
|
+ and offers advanced customization for experienced users,
|
|
|
+ ensuring simplicity for basic tasks and feasibility for complex projects.
|
|
|
+ <br><br><br>
|
|
|
+ Available as
|
|
|
+ [PyPI package](https://pypi.org/project/nicegui/),
|
|
|
+ [Docker image](https://hub.docker.com/r/zauberzeug/nicegui) and on
|
|
|
+ [GitHub](https://github.com/zauberzeug/nicegui).
|
|
|
+ ''')
|
|
|
example_card.create()
|
|
|
|
|
|
+ with ui.column().classes('w-full text-lg p-8 lg:p-16 max-w-[1600px] mx-auto'):
|
|
|
+ link_target('installation', '-50px')
|
|
|
+ section_heading('Installation', 'Get *started*')
|
|
|
+ with ui.row().classes('w-full text-lg leading-tight grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-8'):
|
|
|
+ with ui.column().classes('w-full max-w-md gap-2'):
|
|
|
+ ui.html('<em>1.</em>').classes('text-3xl font-bold')
|
|
|
+ ui.markdown('Create __main.py__').classes('text-lg')
|
|
|
+ with python_window(classes='w-full h-52'):
|
|
|
+ ui.markdown('''
|
|
|
+ ```python\n
|
|
|
+ from nicegui import ui
|
|
|
+
|
|
|
+ ui.label('Hello NiceGUI!')
|
|
|
+
|
|
|
+ ui.run()
|
|
|
+ ```
|
|
|
+ ''')
|
|
|
+ with ui.column().classes('w-full max-w-md gap-2'):
|
|
|
+ ui.html('<em>2.</em>').classes('text-3xl font-bold')
|
|
|
+ ui.markdown('Install and launch').classes('text-lg')
|
|
|
+ with bash_window(classes='w-full h-52'):
|
|
|
+ ui.markdown('''
|
|
|
+ ```bash
|
|
|
+ pip3 install nicegui
|
|
|
+ python3 main.py
|
|
|
+ ```
|
|
|
+ ''')
|
|
|
+ with ui.column().classes('w-full max-w-md gap-2'):
|
|
|
+ ui.html('<em>3.</em>').classes('text-3xl font-bold')
|
|
|
+ ui.markdown('Enjoy!').classes('text-lg')
|
|
|
+ with browser_window(classes='w-full h-52'):
|
|
|
+ ui.label('Hello NiceGUI!')
|
|
|
+ with ui.expansion('...or use Docker to run your main.py').classes('w-full gap-2 bold-links arrow-links'):
|
|
|
+ with ui.row().classes('mt-8 w-full justify-center items-center gap-8'):
|
|
|
+ ui.markdown('''
|
|
|
+ With our [multi-arch Docker image](https://hub.docker.com/repository/docker/zauberzeug/nicegui)
|
|
|
+ you can start the server without installing any packages.
|
|
|
+
|
|
|
+ The command searches for `main.py` in in your current directory and makes the app available at http://localhost:8888.
|
|
|
+ ''').classes('max-w-xl')
|
|
|
+ with bash_window(classes='max-w-lg w-full h-52'):
|
|
|
+ ui.markdown('''
|
|
|
+ ```bash
|
|
|
+ docker run -it --rm -p 8888:8080 \\
|
|
|
+ -v "$PWD":/app zauberzeug/nicegui
|
|
|
+ ```
|
|
|
+ ''')
|
|
|
+
|
|
|
with ui.column().classes('w-full p-8 lg:p-16 bold-links arrow-links max-w-[1600px] mx-auto'):
|
|
|
link_target('features', '-50px')
|
|
|
section_heading('Features', 'Code *nicely*')
|
|
@@ -134,12 +197,12 @@ async def index_page(client: Client):
|
|
|
features('swap_horiz', 'Interaction', [
|
|
|
'buttons, switches, sliders, inputs, ...',
|
|
|
'notifications, dialogs and menus',
|
|
|
- 'keyboard input',
|
|
|
- 'on-screen joystick',
|
|
|
+ 'interactive images with SVG overlays',
|
|
|
+ 'web pages and native window apps',
|
|
|
])
|
|
|
features('space_dashboard', 'Layout', [
|
|
|
'navigation bars, tabs, panels, ...',
|
|
|
- 'grouping with rows, columns and cards',
|
|
|
+ 'grouping with rows, columns, grids and cards',
|
|
|
'HTML and Markdown elements',
|
|
|
'flex layout by default',
|
|
|
])
|
|
@@ -156,10 +219,10 @@ async def index_page(client: Client):
|
|
|
'[Tailwind CSS](https://tailwindcss.com/) auto-completion',
|
|
|
])
|
|
|
features('source', 'Coding', [
|
|
|
- 'live-cycle events',
|
|
|
- 'implicit reload on code change',
|
|
|
+ 'routing for multiple pages',
|
|
|
+ 'auto-reload on code change',
|
|
|
'straight-forward data binding',
|
|
|
- 'execute javascript from Python',
|
|
|
+ 'Jupyter notebook compatibility',
|
|
|
])
|
|
|
features('anchor', 'Foundation', [
|
|
|
'generic [Vue](https://vuejs.org/) to Python bridge',
|
|
@@ -168,44 +231,6 @@ async def index_page(client: Client):
|
|
|
'Python 3.7+',
|
|
|
])
|
|
|
|
|
|
- with ui.column().classes('w-full text-lg p-8 lg:p-16 max-w-[1600px] mx-auto'):
|
|
|
- link_target('installation', '-50px')
|
|
|
- section_heading('Installation', 'Get *started*')
|
|
|
- with ui.row().classes('w-full text-lg leading-tight grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-8'):
|
|
|
- with ui.column().classes('w-full max-w-md gap-2'):
|
|
|
- ui.html('<em>1.</em>').classes('text-3xl font-bold')
|
|
|
- ui.markdown('Create __main.py__').classes('text-lg')
|
|
|
- with python_window(classes='w-full h-52'):
|
|
|
- ui.markdown('''```python\n
|
|
|
-from nicegui import ui
|
|
|
-
|
|
|
-ui.label('Hello NiceGUI!')
|
|
|
-
|
|
|
-ui.run()
|
|
|
-```''')
|
|
|
- with ui.column().classes('w-full max-w-md gap-2'):
|
|
|
- ui.html('<em>2.</em>').classes('text-3xl font-bold')
|
|
|
- ui.markdown('Install and launch').classes('text-lg')
|
|
|
- with bash_window(classes='w-full h-52'):
|
|
|
- ui.markdown('```bash\npip3 install nicegui\npython3 main.py\n```')
|
|
|
- with ui.column().classes('w-full max-w-md gap-2'):
|
|
|
- ui.html('<em>3.</em>').classes('text-3xl font-bold')
|
|
|
- ui.markdown('Enjoy!').classes('text-lg')
|
|
|
- with browser_window(classes='w-full h-52'):
|
|
|
- ui.label('Hello NiceGUI!')
|
|
|
- with ui.expansion('...or use Docker to run your main.py').classes('w-full gap-2 bold-links arrow-links'):
|
|
|
- with ui.row().classes('mt-8 w-full justify-center items-center gap-8'):
|
|
|
- ui.markdown('''
|
|
|
-With our [multi-arch Docker image](https://hub.docker.com/repository/docker/zauberzeug/nicegui)
|
|
|
-you can start the server without installing any packages.
|
|
|
-
|
|
|
-The command searches for `main.py` in in your current directory and makes the app available at http://localhost:8888.
|
|
|
-''').classes('max-w-xl')
|
|
|
- with bash_window(classes='max-w-lg w-full h-52'):
|
|
|
- ui.markdown('```bash\n'
|
|
|
- 'docker run -it --rm -p 8888:8080 \\\n -v "$PWD":/app zauberzeug/nicegui\n'
|
|
|
- '```')
|
|
|
-
|
|
|
with ui.column().classes('w-full p-8 lg:p-16 max-w-[1600px] mx-auto'):
|
|
|
link_target('demos', '-50px')
|
|
|
section_heading('Demos', 'Try *this*')
|
|
@@ -228,18 +253,15 @@ The command searches for `main.py` in in your current directory and makes the ap
|
|
|
with ui.row().classes('w-full text-lg leading-tight grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4'):
|
|
|
example_link('Slideshow', 'implements a keyboard-controlled image slideshow')
|
|
|
example_link('Authentication', 'shows how to use sessions to build a login screen')
|
|
|
- example_link(
|
|
|
- 'Modularization',
|
|
|
- 'provides an example of how to modularize your application into multiple files and reuse code')
|
|
|
- example_link(
|
|
|
- 'FastAPI',
|
|
|
- 'illustrates the integration of NiceGUI with an existing FastAPI application')
|
|
|
- example_link(
|
|
|
- 'Map',
|
|
|
- 'demonstrates wrapping the JavaScript library [leaflet](https://leafletjs.com/) to display a map at specific locations')
|
|
|
- example_link(
|
|
|
- 'AI Interface',
|
|
|
- 'utilizes the [replicate](https://replicate.com) library to perform voice-to-text transcription and generate images from prompts with Stable Diffusion')
|
|
|
+ example_link('Modularization',
|
|
|
+ 'provides an example of how to modularize your application into multiple files and reuse code')
|
|
|
+ example_link('FastAPI', 'illustrates the integration of NiceGUI with an existing FastAPI application')
|
|
|
+ example_link('Map',
|
|
|
+ 'demonstrates wrapping the JavaScript library [leaflet](https://leafletjs.com/) '
|
|
|
+ 'to display a map at specific locations')
|
|
|
+ example_link('AI Interface',
|
|
|
+ 'utilizes the [replicate](https://replicate.com) library to perform voice-to-text '
|
|
|
+ 'transcription and generate images from prompts with Stable Diffusion')
|
|
|
example_link('3D Scene', 'creates a webGL view and loads an STL mesh illuminated with a spotlight')
|
|
|
example_link('Custom Vue Component', 'shows how to write and integrate a custom Vue component')
|
|
|
example_link('Image Mask Overlay', 'shows how to overlay an image with a mask')
|
|
@@ -252,11 +274,15 @@ The command searches for `main.py` in in your current directory and makes the ap
|
|
|
example_link('Local File Picker', 'demonstrates a dialog for selecting files locally on the server')
|
|
|
example_link('Search as you type', 'using public API of thecocktaildb.com to search for cocktails')
|
|
|
example_link('Menu and Tabs', 'uses Quasar to create foldable menu and tabs inside a header bar')
|
|
|
+ example_link('Todo list', 'shows a simple todo list with checkboxes and text input')
|
|
|
example_link('Trello Cards', 'shows Trello-like cards that can be dragged and dropped into columns')
|
|
|
example_link('Slots', 'shows how to use scoped slots to customize Quasar elements')
|
|
|
example_link('Table and slots', 'shows how to use component slots in a table')
|
|
|
example_link('Single Page App', 'navigate without reloading the page')
|
|
|
example_link('Chat App', 'a simple chat app')
|
|
|
+ example_link('Chat with AI', 'a simple chat app with AI')
|
|
|
+ example_link('SQLite Database', 'CRUD operations on a SQLite database')
|
|
|
+ example_link('Pandas DataFrame', 'displays an editable [pandas](https://pandas.pydata.org) DataFrame')
|
|
|
|
|
|
with ui.row().classes('bg-primary w-full min-h-screen mt-16'):
|
|
|
link_target('why')
|
|
@@ -297,28 +323,32 @@ The command searches for `main.py` in in your current directory and makes the ap
|
|
|
|
|
|
|
|
|
@ui.page('/documentation')
|
|
|
-def documentation_page():
|
|
|
+def documentation_page() -> None:
|
|
|
add_head_html()
|
|
|
- add_header()
|
|
|
- side_menu()
|
|
|
+ menu = side_menu()
|
|
|
+ add_header(menu)
|
|
|
ui.add_head_html('<style>html {scroll-behavior: auto;}</style>')
|
|
|
with ui.column().classes('w-full p-8 lg:p-16 max-w-[1250px] mx-auto'):
|
|
|
section_heading('Reference, Demos and more', '*NiceGUI* Documentation')
|
|
|
- ui.markdown(
|
|
|
- 'This is the documentation for NiceGUI >= 1.0. '
|
|
|
- 'Documentation for older versions can be found at [https://0.9.nicegui.io/](https://0.9.nicegui.io/reference).'
|
|
|
- ).classes('bold-links arrow-links')
|
|
|
+ ui.markdown('''
|
|
|
+ This is the documentation for NiceGUI >= 1.0.
|
|
|
+ Documentation for older versions can be found at [https://0.9.nicegui.io/](https://0.9.nicegui.io/reference).
|
|
|
+ ''').classes('bold-links arrow-links')
|
|
|
documentation.create_full()
|
|
|
|
|
|
|
|
|
@ui.page('/documentation/{name}')
|
|
|
-def documentation_page_more(name: str):
|
|
|
+async def documentation_page_more(name: str, client: Client) -> None:
|
|
|
if not hasattr(ui, name):
|
|
|
name = name.replace('_', '') # NOTE: "AG Grid" leads to anchor name "ag_grid", but class is `ui.aggrid`
|
|
|
module = importlib.import_module(f'website.more_documentation.{name}_documentation')
|
|
|
- api = getattr(ui, name)
|
|
|
more = getattr(module, 'more', None)
|
|
|
- back_link_target = str(api.__doc__ or api.__init__.__doc__).splitlines()[0].strip()
|
|
|
+ if hasattr(ui, name):
|
|
|
+ api = getattr(ui, name)
|
|
|
+ back_link_target = str(api.__doc__ or api.__init__.__doc__).splitlines()[0].strip()
|
|
|
+ else:
|
|
|
+ api = name
|
|
|
+ back_link_target = name
|
|
|
|
|
|
add_head_html()
|
|
|
add_header()
|
|
@@ -336,6 +366,7 @@ def documentation_page_more(name: str):
|
|
|
ui.markdown('**Reference**').classes('mt-4')
|
|
|
ui.markdown('## Reference').classes('mt-16')
|
|
|
generate_class_doc(api)
|
|
|
-
|
|
|
+ await client.connected()
|
|
|
+ await ui.run_javascript(f'document.title = "{name} • NiceGUI";', respond=False)
|
|
|
|
|
|
ui.run(uvicorn_reload_includes='*.py, *.css, *.html')
|