experiments/babel/app/main.py (276 lines of code) (raw):
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Babel Main Mesop Application"""
import os
from typing import TypedDict
# from dataclasses import field
import mesop as me
# from config.default import BabelMetadata, Voice
from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from fastapi.staticfiles import StaticFiles
from pages.about import about_page, settings_page
from pages.chirphd_voices import chirphd_voices_page
from pages.explore import explore_page
from pages.welcome import welcome_page
from set_up.set_up import VoicesSetup
from state.state import AppState
# set up Mesop to be hosted via FastAPI
app = FastAPI()
# mount static files
app.mount("/static", StaticFiles(directory="local_assets"), name="static")
# main FastAPI app
app.mount(
"/",
WSGIMiddleware(
me.create_wsgi_app(debug_mode=os.environ.get("DEBUG_MODE", "") == "true")
),
)
class Page(TypedDict):
"""Page structure"""
id: int
name: str
page_definition: str
content_pages = [
Page(name="home", page_definition="home_page"),
Page(name="about", page_definition="about_page"),
Page(name="settings", page_definition="settings_page"),
Page(name="welcome", page_definiton="welcome_page"),
Page(name="world_tour", page_definition="explore_page"),
]
def show_page(page_name: str):
"""show page switcher"""
match page_name:
case "welcome":
welcome_page_reference()
case "world_tour":
explore_page_reference()
case "home":
home_page()
case "about":
about_page_reference()
case "settings":
settings_page_reference()
case _:
about_page_reference()
def on_click_page_choice(e: me.ClickEvent):
"""Change state to current page"""
state = me.state(AppState)
print(f"Clicked on: {e.key}")
page = next(
(item for item in content_pages if item["name"] == e.key), state.current_page
)
print(f"Found: {page}")
print(f"current page: {page['name']}")
state.current_page = page["name"]
def toggle_theme(e: me.ClickEvent): # pylint: disable=unused-argument
"""Toggle theme event"""
if me.theme_brightness() == "light":
me.set_theme_mode("dark")
else:
me.set_theme_mode("light")
SIDENAV_MIN_WIDTH = 150
SIDENAV_MAX_WIDTH = 76
def on_click_menu(e: me.ClickEvent): # pylint: disable=unused-argument
"""Menu click event handler"""
state = me.state(AppState)
state.sidenav_open = not state.sidenav_open
def load(e: me.LoadEvent): # pylint: disable=unused-argument
"""Page load event"""
# print("load event", e) # this event looks like: LoadEvent(path='/') or LoadEvent(path='/leaderboard')
s = me.state(AppState)
print("theme", s.theme_mode)
if s.theme_mode: # recall state theme mode
me.set_theme_mode(s.theme_mode)
else:
me.set_theme_mode("system")
# get_voices()
s.voices = VoicesSetup.init()
@me.page(
path="/",
title="Fabulae: Babel",
on_load=load,
)
def babel():
"""Main Mesop App Page: Chirp 3: HD Voices"""
state = me.state(AppState)
with me.box(
style=me.Style(
display="flex",
flex_direction="column",
height="100%",
),
):
# header
with me.box(
style=me.Style(
flex_direction="row",
display="flex",
gap=5,
padding=me.Padding(top=10, left=10, right=10, bottom=0),
align_items="",
# background=me.theme_var("surface-container"),
# border=me.Border(bottom=me.BorderSide(width="0.01em", style="solid", color="#c3c5dd",))
),
):
with me.content_button(on_click=on_click_menu):
me.icon("menu")
me.text("Babel", type="headline-5")
me.box(style=me.Style(width=10))
me.button(
label="Experiment",
type="stroked",
disabled="true",
style=me.Style(
font_size="9pt",
height="25px",
color="grey",
text_transform="uppercase",
),
)
# with me.content_button(
# type="icon",
# style=me.Style(position="absolute", right=44, top=8),
# #on_click=link_to_feedback
# ):
# me.icon("feedback")
me.link(
text="feedback",
url="https://cloud.google.com/text-to-speech/docs/chirp3-hd", # your feedback form URL here
open_in_new_tab=True,
style=me.Style(
position="absolute",
right=48,
top=18,
text_decoration="none",
color=me.theme_var("on-surface"),
),
)
with me.content_button(
type="icon",
style=me.Style(position="absolute", right=4, top=8),
on_click=toggle_theme,
):
me.icon(
"light_mode" if me.theme_brightness() == "dark" else "dark_mode"
)
# content area
# side menu
with me.sidenav(
opened=True,
style=me.Style(
width=SIDENAV_MAX_WIDTH if state.sidenav_open else SIDENAV_MIN_WIDTH,
margin=me.Margin(top=60),
padding=me.Padding(top=10, left=5, right=5, bottom=0),
background=me.theme_var("surface-container-lowest"),
border_radius="0 30px 0 0",
),
):
with me.box(style=me.Style(display="flex", flex_direction="column")):
# Home: Journey Voices
with me.content_button(
style=me.Style(align_content="start"),
on_click=on_click_page_choice,
key="home",
):
with me.box(
style=me.Style(
display="flex",
flex_direction="row",
gap=5,
align_items="center",
)
):
me.icon(
"equalizer",
)
if not state.sidenav_open:
me.text(
"Chirp 3: HD",
)
with me.content_button(
style=me.Style(align_content="start"),
on_click=on_click_page_choice,
key="welcome",
):
with me.box(
style=me.Style(
display="flex",
flex_direction="row",
gap=5,
align_items="center",
)
):
me.icon(
"spa",
)
if not state.sidenav_open:
me.text(
"Welcome",
)
with me.content_button(
style=me.Style(align_content="start"),
on_click=on_click_page_choice,
key="world_tour",
):
with me.box(
style=me.Style(
display="flex",
flex_direction="row",
gap=5,
align_items="center",
)
):
me.icon(
"explore",
)
if not state.sidenav_open:
me.text(
"Explore",
)
# Bottom buttons
with me.box(
style=me.Style(
position="absolute",
bottom=8,
)
):
# About
with me.content_button(
style=me.Style(align_content="start"),
on_click=on_click_page_choice,
key="about",
):
with me.box(
style=me.Style(
display="flex",
flex_direction="row",
gap=5,
align_items="center",
)
):
me.icon(
"info",
)
if not state.sidenav_open:
me.text(
"About",
)
# Settings
with me.content_button(
style=me.Style(align_content="start"),
on_click=on_click_page_choice,
key="settings",
):
with me.box(
style=me.Style(
display="flex",
flex_direction="row",
gap=5,
align_items="center",
)
):
me.icon(
"settings",
)
if not state.sidenav_open:
me.text(
"Settings",
)
# primary content
with me.box(
style=me.Style(
margin=me.Margin(
left=SIDENAV_MAX_WIDTH if state.sidenav_open else SIDENAV_MIN_WIDTH
),
padding=me.Padding(top=10, left=10, right=10, bottom=0),
),
):
show_page(state.current_page)
# Home: Journey voices
def home_page():
"""Journey Voices page"""
chirphd_voices_page(me.state(AppState))
# About
def about_page_reference():
"""About page"""
about_page(me.state(AppState))
# Settings
def settings_page_reference():
"""Settings Page"""
settings_page(me.state(AppState))
# Welcome
def welcome_page_reference():
"""Welcome Page"""
welcome_page(me.state(AppState))
# Explore
def explore_page_reference():
"""Describe Page"""
explore_page(me.state(AppState))
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=True,
reload_includes=["*.py", "*.js"],
timeout_graceful_shutdown=0,
)