Spaces:
Running
Running
# Disable import sort ordering due to the hack needed | |
# to ensure local imports. | |
# ruff: noqa: E402 | |
import base64 | |
import inspect | |
import os | |
import sys | |
from dataclasses import dataclass | |
from typing import Literal | |
import mesop as me | |
# Append the current directory to sys.path to ensure local imports work | |
# This is required so mesop/examples/__init__.py can import the modules | |
# imported below. | |
current_dir = os.path.dirname(os.path.abspath(__file__)) | |
if current_dir not in sys.path: | |
sys.path.append(current_dir) | |
import glob | |
import audio as audio | |
import autocomplete as autocomplete | |
import badge as badge | |
import basic_animation as basic_animation | |
import bootstrap as bootstrap | |
import box as box | |
import button as button | |
import button_toggle as button_toggle | |
import card as card | |
import chat as chat | |
import chat_inputs as chat_inputs | |
import checkbox as checkbox | |
import code_demo as code_demo # cannot call it code due to python library naming conflict | |
import date_picker as date_picker | |
import date_range_picker as date_range_picker | |
import density as density | |
import dialog as dialog | |
import divider as divider | |
import embed as embed | |
import expansion_panel as expansion_panel | |
import fancy_chat as fancy_chat | |
import feedback as feedback | |
import form_billing as form_billing | |
import form_profile as form_profile | |
import grid_table as grid_table | |
import headers as headers | |
import html_demo as html_demo | |
import icon as icon | |
import image as image | |
import input as input | |
import link as link | |
import llm_playground as llm_playground | |
import llm_rewriter as llm_rewriter | |
import markdown_demo as markdown_demo # cannot call it markdown due to python library naming conflict | |
import markdown_editor as markdown_editor | |
import plot as plot | |
import progress_bar as progress_bar | |
import progress_spinner as progress_spinner | |
import radio as radio | |
import select_demo as select_demo # cannot call it select due to python library naming conflict | |
import sidenav as sidenav | |
import slide_toggle as slide_toggle | |
import slider as slider | |
import snackbar as snackbar | |
import table as table | |
import tailwind as tailwind | |
import text as text | |
import text_to_image as text_to_image | |
import text_to_text as text_to_text | |
import textarea as textarea | |
import tooltip as tooltip | |
import uploader as uploader | |
import video as video | |
class Example: | |
# module_name (should also be the path name) | |
name: str | |
class Section: | |
name: str | |
examples: list[Example] | |
FIRST_SECTIONS = [ | |
Section( | |
name="Quick start", | |
examples=[ | |
Example(name="chat"), | |
Example(name="text_to_image"), | |
Example(name="text_to_text"), | |
], | |
), | |
Section( | |
name="Use cases", | |
examples=[ | |
Example(name="fancy_chat"), | |
Example(name="llm_rewriter"), | |
Example(name="llm_playground"), | |
Example(name="markdown_editor"), | |
], | |
), | |
Section( | |
name="Patterns", | |
examples=[ | |
Example(name="dialog"), | |
Example(name="grid_table"), | |
Example(name="headers"), | |
Example(name="snackbar"), | |
Example(name="chat_inputs"), | |
Example(name="form_billing"), | |
Example(name="form_profile"), | |
], | |
), | |
Section( | |
name="Features", | |
examples=[ | |
Example(name="density"), | |
], | |
), | |
Section( | |
name="Misc", | |
examples=[ | |
Example(name="basic_animation"), | |
Example(name="feedback"), | |
], | |
), | |
Section( | |
name="Integrations", | |
examples=[ | |
Example(name="bootstrap"), | |
Example(name="tailwind"), | |
], | |
), | |
] | |
COMPONENTS_SECTIONS = [ | |
Section( | |
name="Layout", | |
examples=[ | |
Example(name="box"), | |
Example(name="sidenav"), | |
], | |
), | |
Section( | |
name="Text", | |
examples=[ | |
Example(name="text"), | |
Example(name="markdown_demo"), | |
Example(name="code_demo"), | |
], | |
), | |
Section( | |
name="Media", | |
examples=[ | |
Example(name="image"), | |
Example(name="audio"), | |
Example(name="video"), | |
], | |
), | |
Section( | |
name="Form", | |
examples=[ | |
Example(name="autocomplete"), | |
Example(name="button"), | |
Example(name="button_toggle"), | |
Example(name="checkbox"), | |
Example(name="date_picker"), | |
Example(name="date_range_picker"), | |
Example(name="input"), | |
Example(name="textarea"), | |
Example(name="radio"), | |
Example(name="select_demo"), | |
Example(name="slide_toggle"), | |
Example(name="slider"), | |
Example(name="uploader"), | |
], | |
), | |
Section( | |
name="Visual", | |
examples=[ | |
Example(name="badge"), | |
Example(name="card"), | |
Example(name="divider"), | |
Example(name="expansion_panel"), | |
Example(name="icon"), | |
Example(name="progress_bar"), | |
Example(name="progress_spinner"), | |
Example(name="table"), | |
Example(name="tooltip"), | |
], | |
), | |
Section( | |
name="Web", | |
examples=[ | |
Example(name="embed"), | |
Example(name="html_demo"), | |
Example(name="link"), | |
], | |
), | |
Section( | |
name="Others", | |
examples=[ | |
Example(name="plot"), | |
], | |
), | |
] | |
ALL_SECTIONS = FIRST_SECTIONS + COMPONENTS_SECTIONS | |
BORDER_SIDE = me.BorderSide( | |
style="solid", | |
width=1, | |
color="#dcdcdc", | |
) | |
class State: | |
current_demo: str | |
panel_fullscreen: Literal["preview", "editor", None] = None | |
screenshots: dict[str, str] = {} | |
def load_home_page(e: me.LoadEvent): | |
if me.state(ThemeState).dark_mode: | |
me.set_theme_mode("dark") | |
else: | |
me.set_theme_mode("system") | |
yield | |
screenshot_dir = os.path.join(current_dir, "screenshots") | |
screenshot_files = glob.glob(os.path.join(screenshot_dir, "*.webp")) | |
for screenshot_file in screenshot_files: | |
image_name = os.path.basename(screenshot_file).split(".")[0] | |
with open(screenshot_file, "rb") as image_file: | |
encoded_string = base64.b64encode(image_file.read()).decode() | |
screenshots[image_name] = "data:image/webp;base64," + encoded_string | |
yield | |
def main_page(): | |
header() | |
with me.box( | |
style=me.Style( | |
background=me.theme_var("background"), | |
flex_grow=1, | |
display="flex", | |
) | |
): | |
if is_desktop(): | |
side_menu() | |
with me.box( | |
style=me.Style( | |
width="calc(100% - 150px)" if is_desktop() else "100%", | |
display="flex", | |
gap=24, | |
flex_direction="column", | |
padding=me.Padding.all(24), | |
overflow_y="auto", | |
) | |
): | |
with me.box( | |
style=me.Style( | |
height="calc(100vh - 120px)", | |
) | |
): | |
for section in ALL_SECTIONS: | |
with me.box(style=me.Style(margin=me.Margin(bottom=28))): | |
me.text( | |
section.name, | |
style=me.Style( | |
font_weight=500, | |
font_size=20, | |
margin=me.Margin( | |
bottom=16, | |
), | |
), | |
) | |
with me.box( | |
style=me.Style( | |
display="flex", | |
flex_direction="row", | |
flex_wrap="wrap", | |
gap=28, | |
) | |
): | |
for example in section.examples: | |
example_card(example.name) | |
def navigate_example_card(e: me.ClickEvent): | |
me.navigate("/embed/" + e.key) | |
def example_card(name: str): | |
with me.box( | |
key=name, | |
on_click=navigate_example_card, | |
style=me.Style( | |
border=me.Border.all( | |
me.BorderSide( | |
width=1, | |
color="rgb(220, 220, 220)", | |
style="solid", | |
) | |
), | |
box_shadow="rgba(0, 0, 0, 0.2) 0px 3px 1px -2px, rgba(0, 0, 0, 0.14) 0px 2px 2px, rgba(0, 0, 0, 0.12) 0px 1px 5px", | |
cursor="pointer", | |
width="min(100%, 150px)", | |
border_radius=12, | |
background=me.theme_var("background"), | |
), | |
): | |
image_url = screenshots.get(name, "") | |
me.box( | |
style=me.Style( | |
background=f'url("{image_url}") center / cover', | |
height=112, | |
width=150, | |
) | |
) | |
me.text( | |
format_example_name(name), | |
style=me.Style( | |
font_weight=500, | |
font_size=18, | |
padding=me.Padding.all(12), | |
border=me.Border( | |
top=me.BorderSide( | |
width=1, | |
style="solid", | |
color="rgb(220, 220, 220)", | |
) | |
), | |
), | |
) | |
def on_load_embed(e: me.LoadEvent): | |
if me.state(ThemeState).dark_mode: | |
me.set_theme_mode("dark") | |
else: | |
me.set_theme_mode("system") | |
if not is_desktop(): | |
me.state(State).panel_fullscreen = "preview" | |
def create_main_fn(example: Example): | |
def main(): | |
with me.box( | |
style=me.Style( | |
height="100%", | |
display="flex", | |
flex_direction="column", | |
background=me.theme_var("background"), | |
) | |
): | |
header(demo_name=example.name) | |
body(example.name) | |
return main | |
for section in FIRST_SECTIONS + COMPONENTS_SECTIONS: | |
for example in section.examples: | |
create_main_fn(example) | |
def body(current_demo: str): | |
state = me.state(State) | |
with me.box( | |
style=me.Style( | |
flex_grow=1, | |
display="flex", | |
) | |
): | |
if is_desktop(): | |
side_menu() | |
src = "/" + current_demo | |
with me.box( | |
style=me.Style( | |
width="calc(100% - 150px)" if is_desktop() else "100%", | |
display="grid", | |
grid_template_columns="1fr 1fr" | |
if state.panel_fullscreen is None | |
else "1fr", | |
) | |
): | |
if state.panel_fullscreen != "editor": | |
demo_ui(src) | |
if state.panel_fullscreen != "preview": | |
demo_code(inspect.getsource(get_module(current_demo))) | |
def demo_ui(src: str): | |
state = me.state(State) | |
with me.box( | |
style=me.Style(flex_grow=1), | |
): | |
with me.box( | |
style=me.Style( | |
display="flex", | |
justify_content="space-between", | |
align_items="center", | |
border=me.Border(bottom=BORDER_SIDE), | |
) | |
): | |
me.text( | |
"Preview", | |
style=me.Style( | |
font_weight=500, | |
padding=me.Padding.all(14), | |
), | |
) | |
if is_desktop(): | |
with me.tooltip( | |
position="above", | |
message="Minimize" | |
if state.panel_fullscreen == "preview" | |
else "Maximize", | |
): | |
with me.content_button(type="icon", on_click=toggle_fullscreen): | |
me.icon( | |
"close_fullscreen" | |
if state.panel_fullscreen == "preview" | |
else "fullscreen" | |
) | |
else: | |
swap_button() | |
me.embed( | |
src=src, | |
style=me.Style( | |
border=me.Border.all(me.BorderSide(width=0)), | |
border_radius=2, | |
height="calc(100vh - 106px)", | |
width="100%", | |
), | |
) | |
def swap_button(): | |
state = me.state(State) | |
with me.tooltip( | |
position="above", | |
message="Swap for code" | |
if state.panel_fullscreen == "preview" | |
else "Swap for preview", | |
): | |
with me.content_button(type="icon", on_click=swap_fullscreen): | |
me.icon("swap_horiz") | |
def swap_fullscreen(e: me.ClickEvent): | |
state = me.state(State) | |
if state.panel_fullscreen == "preview": | |
state.panel_fullscreen = "editor" | |
else: | |
state.panel_fullscreen = "preview" | |
def toggle_fullscreen(e: me.ClickEvent): | |
state = me.state(State) | |
if state.panel_fullscreen == "preview": | |
state.panel_fullscreen = None | |
else: | |
state.panel_fullscreen = "preview" | |
def demo_code(code_arg: str): | |
with me.box( | |
style=me.Style( | |
flex_grow=1, | |
overflow_x="hidden", | |
overflow_y="hidden", | |
border=me.Border( | |
left=BORDER_SIDE, | |
), | |
background=me.theme_var("surface-container-low"), | |
) | |
): | |
with me.box( | |
style=me.Style( | |
display="flex", | |
justify_content="space-between", | |
align_items="center", | |
border=me.Border(bottom=BORDER_SIDE), | |
background=me.theme_var("background"), | |
) | |
): | |
me.text( | |
"Code", | |
style=me.Style( | |
font_weight=500, | |
padding=me.Padding.all(14), | |
), | |
) | |
if not is_desktop(): | |
swap_button() | |
# Use four backticks for code fence to avoid conflicts with backticks being used | |
# within the displayed code. | |
me.markdown( | |
f"""````python | |
{code_arg} | |
```` | |
""", | |
style=me.Style( | |
border=me.Border( | |
right=BORDER_SIDE, | |
), | |
font_size=13, | |
height="calc(100vh - 106px)", | |
overflow_y="auto", | |
width="100%", | |
), | |
) | |
def header(demo_name: str | None = None): | |
with me.box( | |
style=me.Style( | |
border=me.Border( | |
bottom=me.BorderSide( | |
style="solid", | |
width=1, | |
color="#dcdcdc", | |
) | |
), | |
overflow_x="clip", | |
) | |
): | |
with me.box( | |
style=me.Style( | |
display="flex", | |
align_items="end", | |
justify_content="space-between", | |
margin=me.Margin(left=12, right=12, bottom=12), | |
font_size=24, | |
) | |
): | |
with me.box(style=me.Style(display="flex")): | |
with me.box( | |
style=me.Style(display="flex", cursor="pointer"), | |
on_click=navigate_home, | |
): | |
me.text( | |
"Mesop", style=me.Style(font_weight=700, margin=me.Margin(right=8)) | |
) | |
me.text("Demos ") | |
if demo_name: | |
me.text( | |
"— " + format_example_name(demo_name), | |
style=me.Style(white_space="nowrap", text_overflow="ellipsis"), | |
) | |
with me.box(style=me.Style(display="flex", align_items="baseline")): | |
with me.box( | |
style=me.Style( | |
display="flex", | |
align_items="baseline", | |
), | |
): | |
me.link( | |
text="google/mesop", | |
url="https://github.com/google/mesop/", | |
open_in_new_tab=True, | |
style=me.Style( | |
font_size=18, | |
color=me.theme_var("primary"), | |
text_decoration="none", | |
margin=me.Margin(left=8, right=4, bottom=-16, top=-16), | |
), | |
) | |
me.text( | |
"v" + me.__version__, | |
style=me.Style(font_size=18, margin=me.Margin(left=16)), | |
) | |
with me.content_button( | |
type="icon", | |
style=me.Style(left=8, right=4, top=4), | |
on_click=toggle_theme, | |
): | |
me.icon( | |
"light_mode" if me.theme_brightness() == "dark" else "dark_mode" | |
) | |
class ThemeState: | |
dark_mode: bool | |
def toggle_theme(e: me.ClickEvent): | |
if me.theme_brightness() == "light": | |
me.set_theme_mode("dark") | |
me.state(ThemeState).dark_mode = True | |
else: | |
me.set_theme_mode("light") | |
me.state(ThemeState).dark_mode = False | |
def navigate_home(e: me.ClickEvent): | |
me.navigate("/") | |
def side_menu(): | |
with me.box( | |
style=me.Style( | |
padding=me.Padding.all(12), | |
width=150, | |
flex_grow=0, | |
line_height="1.5", | |
border=me.Border(right=BORDER_SIDE), | |
overflow_x="hidden", | |
height="calc(100vh - 60px)", | |
overflow_y="auto", | |
) | |
): | |
for section in FIRST_SECTIONS: | |
nav_section(section) | |
with me.box( | |
style=me.Style( | |
margin=me.Margin.symmetric( | |
horizontal=-16, | |
vertical=16, | |
), | |
) | |
): | |
me.divider() | |
me.text( | |
"Components", | |
style=me.Style( | |
letter_spacing="0.5px", | |
margin=me.Margin(bottom=6), | |
), | |
) | |
for section in COMPONENTS_SECTIONS: | |
nav_section(section) | |
def nav_section(section: Section): | |
with me.box(style=me.Style(margin=me.Margin(bottom=12))): | |
me.text(section.name, style=me.Style(font_weight=700)) | |
for example in section.examples: | |
example_name = format_example_name(example.name) | |
path = f"/embed/{example.name}" | |
with me.box( | |
style=me.Style(color=me.theme_var("primary"), cursor="pointer"), | |
on_click=set_demo, | |
key=path, | |
): | |
me.text(example_name) | |
def set_demo(e: me.ClickEvent): | |
me.navigate(e.key) | |
def format_example_name(name: str): | |
return ( | |
(" ".join(name.split("_"))) | |
.capitalize() | |
.replace("Llm", "LLM") | |
.replace(" demo", "") | |
) | |
def get_module(module_name: str): | |
if module_name in globals(): | |
return globals()[module_name] | |
raise me.MesopDeveloperException(f"Module {module_name} not supported") | |
def is_desktop(): | |
return me.viewport_size().width > 760 | |