diff --git a/daras_ai_v2/base.py b/daras_ai_v2/base.py index c3b9b42ca..d8b0a265f 100644 --- a/daras_ai_v2/base.py +++ b/daras_ai_v2/base.py @@ -91,6 +91,7 @@ ) from routers.root import PREVIEW_ROUTE_WORKFLOWS +from widgets.sidebar import render_default_sidebar, sidebar_mobile_header MAX_SEED = 4294967294 gooey_rng = Random() @@ -194,6 +195,9 @@ def __init__( self.tab = tab self.request = request + def render_sidebar(self, request, sidebar_ref): + render_default_sidebar(sidebar_ref, request) + @classmethod def api_endpoint(cls) -> str: return f"/v2/{cls.slug_versions[0]}" @@ -391,7 +395,8 @@ def render(self): self.render_report_form() return - header_placeholder = gui.div(className="my-3 w-100") + sidebar_mobile_header(self.request) + header_placeholder = gui.div(className="my-1 w-100") with ( gui.styled(NAV_TABS_CSS), gui.div(className="position-relative", id="recipe-nav-tabs"), diff --git a/daras_ai_v2/icons.py b/daras_ai_v2/icons.py index 1acaf65db..ea7fa9f3d 100644 --- a/daras_ai_v2/icons.py +++ b/daras_ai_v2/icons.py @@ -53,6 +53,8 @@ info = "" search = '' library = '' +sidebar_flip = "" +arrow_up_right = "" # brands github = '' diff --git a/daras_ai_v2/settings.py b/daras_ai_v2/settings.py index dc139857c..5b837e28e 100644 --- a/daras_ai_v2/settings.py +++ b/daras_ai_v2/settings.py @@ -262,6 +262,7 @@ GOOEY_LOGO_IMG = "https://storage.googleapis.com/dara-c1b52.appspot.com/daras_ai/media/2a3aacb4-0941-11ee-b236-02420a0001fb/thumbs/logo%20black.png_400x400.png" GOOEY_LOGO_IMG_WHITE = "https://storage.googleapis.com/dara-c1b52.appspot.com/daras_ai/media/ea26bc06-7eda-11ef-89fa-02420a0001f6/gooey-white-logo.png" GOOEY_LOGO_RECT = "https://storage.googleapis.com/dara-c1b52.appspot.com/daras_ai/media/d628be8a-9207-11ef-8aee-02420a000186/984x272%20rect%20gooey%20logo.png" +GOOEY_LOGO_FACE = "https://storage.googleapis.com/dara-c1b52.appspot.com/daras_ai/media/bb2587e4-66eb-11f0-a197-02420a00013e/gooey-logo-robo.png" os.environ["REPLICATE_API_TOKEN"] = config("REPLICATE_API_TOKEN", default="") @@ -310,7 +311,18 @@ ("/pricing", "Pricing"), (CONTACT_URL, "Contact"), ] -HEADER_ICONS = {} + +SIDEBAR_LINKS = [ + # ("/explore/", "Search", ""), + (DOCS_URL, "Docs", ""), + ("/api/", "API", ""), + (BLOG_URL, "Blog", ""), + ("/pricing", "Pricing", ""), + (CONTACT_URL, "Contact", ""), +] +SIDEBAR_ICON_SIZE = "32px" + +HEADER_ICONS = {"/explore/": ''} GPU_SERVER_1 = furl(config("GPU_SERVER_1", "http://gpu-1.gooey.ai")) diff --git a/routers/account.py b/routers/account.py index f633eeab3..e7f9c88a8 100644 --- a/routers/account.py +++ b/routers/account.py @@ -27,6 +27,7 @@ from workspaces.models import Workspace, WorkspaceInvite from workspaces.views import invitation_page, workspaces_page from workspaces.widgets import get_current_workspace, SWITCH_WORKSPACE_KEY +from widgets.sidebar import sidebar_mobile_header if typing.TYPE_CHECKING: from app_users.models import AppUser @@ -381,7 +382,7 @@ def account_page_wrapper(request: Request, current_tab: TabData): raise gui.RedirectException(str(redirect_url)) with page_wrapper(request) as current_workspace: - gui.div(className="mt-5") + sidebar_mobile_header(request) with gui.nav_tabs(): for tab in AccountTabs.get_tabs_for_user(request.user, current_workspace): with gui.nav_item(tab.url_path, active=tab == current_tab): diff --git a/routers/root.py b/routers/root.py index 0bae2977c..04f7aa191 100644 --- a/routers/root.py +++ b/routers/root.py @@ -47,6 +47,7 @@ from routers.static_pages import serve_static_file from widgets.workflow_search import SearchFilters, render_search_bar_with_redirect from workspaces.widgets import global_workspace_selector, workspace_selector_link +from widgets.sidebar import render_default_sidebar, sidebar_layout, use_sidebar app = CustomAPIRouter() @@ -250,7 +251,7 @@ def explore_page( ): from widgets import explore - with page_wrapper(request, search_filters=search_filters, show_search_bar=False): + with page_wrapper(request): explore.render(request, search_filters) return { @@ -689,7 +690,7 @@ def render_recipe_page( if not gui.session_state: gui.session_state.update(page.current_sr_to_session_state()) - with page_wrapper(request): + with page_wrapper(request, page=page): page.render() return dict( @@ -709,75 +710,138 @@ def get_og_url_path(request) -> str: def page_wrapper( request: Request, className="", - search_filters: typing.Optional[SearchFilters] = None, - show_search_bar: bool = True, + page=None, ): - from routers.account import explore_in_current_workspace - - context = {"request": request, "block_incognito": True} - - with gui.div(className="d-flex flex-column min-vh-100"): - gui.html(templates.get_template("gtag.html").render(**context)) - - with ( - gui.div(className="header"), - gui.div(className="navbar navbar-expand-xl bg-transparent p-0 m-0"), - gui.div(className="container-xxl my-2"), - gui.div( - className="position-relative w-100 d-flex justify-content-between gap-2" - ), - ): - with ( - gui.div(className="d-md-block"), - gui.tag("a", href="/"), - ): - gui.tag( - "img", - src=settings.GOOEY_LOGO_IMG, - width="300", - height="142", - className="img-fluid logo d-none d-sm-block", - ) - gui.tag( - "img", - src=settings.GOOEY_LOGO_RECT, - width="145", - height="40", - className="img-fluid logo d-sm-none", - ) - - if show_search_bar: - _render_mobile_search_button(request, search_filters) + context = { + "request": request, + "block_incognito": True, + } + sidebar_ref = use_sidebar("main-sidebar", request.session, default_open=True) + sidebar_container, pane_container = sidebar_layout(sidebar_ref) + + container = page if page else None + with sidebar_container: + with gui.styled(""" + .gooey-sidebar-closed:hover { + & .hover-btn { + display: block !important; + } + & .logo-face { + display: none !important; + } + } + """): with gui.div( - className="d-flex gap-2 justify-content-end flex-wrap align-items-center" + className="flex-grow-1 position-relative", + id="sidebar-click-container", ): - for url, label in settings.HEADER_LINKS: - render_header_link( - url=url, label=label, icon=settings.HEADER_ICONS.get(url) + with gui.div( + className="d-flex px-md-2 px-3 py-2 align-items-center justify-content-between text-nowrap", + style={"height": "54px"}, + ): + # sidebar header + gui.tag( + "img", + src=settings.GOOEY_LOGO_FACE, + width=settings.SIDEBAR_ICON_SIZE, + height=settings.SIDEBAR_ICON_SIZE, + className=" logo-face d-none d-md-block", ) - - if request.user and not request.user.is_anonymous: - render_header_link( - url=get_route_path(explore_in_current_workspace), - label="Saved", - icon=icons.save, + open_sidebar_btn = gui.button( + label=icons.sidebar_flip, + className="m-0 d-none hover-btn gooey-btn", + unsafe_allow_html=True, + type="tertiary", ) + if open_sidebar_btn: + sidebar_ref.set_open(True) + raise gui.RerunException() - current_workspace = global_workspace_selector( - request.user, request.session + current_workspace = None + with ( + gui.styled( + """ + & > button { + max-width: 100%; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; + height: 100%; + margin: auto; + } + """, + ), + gui.div( + className="position-relative", style={"maxWidth": "50%"} + ), + ): + if sidebar_ref.is_open: + if request.user and not request.user.is_anonymous: + current_workspace = global_workspace_selector( + request.user, request.session + ) + else: + current_workspace = None + anonymous_login_container(request, context) + + close_mobile_sidebar = gui.button( + label=icons.cancel, + className="m-0 d-md-none gooey-btn", + unsafe_allow_html=True, + type="tertiary", ) + if close_mobile_sidebar: + sidebar_ref.set_mobile_open(False) + raise gui.RerunException() + + if sidebar_ref.is_open: + close_sidebar = gui.button( + label=icons.sidebar_flip, + className="m-0 d-none d-md-block gooey-btn", + unsafe_allow_html=True, + type="tertiary", + ) + if close_sidebar: + sidebar_ref.set_open(False) + raise gui.RerunException() + + if container: + container.render_sidebar(request, sidebar_ref) else: - current_workspace = None - anonymous_login_container(request, context) + render_default_sidebar(sidebar_ref, request) - gui.html(copy_to_clipboard_scripts) + # Bottom section with workspace selector when sidebar is closed + with ( + gui.styled( + "& img { width: 32px !important; height: 32px !important; }" + ), + gui.div( + className="p-3 position-absolute bottom-0 d-md-block d-none", + style={"width": "100%", "zIndex": 1000}, + ), + ): + if not sidebar_ref.is_open: + if request.user and not request.user.is_anonymous: + current_workspace = global_workspace_selector( + request.user, request.session, hide_name=True + ) + else: + current_workspace = None + anonymous_login_container(request, context, hide_sign_in=True) + + # Main content pane + with pane_container: + with gui.div(className="d-flex flex-column min-vh-100 w-100 pt-md-2"): + gui.html(templates.get_template("gtag.html").render(**context)) - with gui.div(id="main-content", className="container-xxl " + className): - yield current_workspace + gui.html(copy_to_clipboard_scripts) - gui.html(templates.get_template("footer.html").render(**context)) - gui.html(templates.get_template("login_scripts.html").render(**context)) + with gui.div(id="main-content", className="px-3 " + className): + yield current_workspace + + gui.html(templates.get_template("footer.html").render(**context)) + gui.html(templates.get_template("login_scripts.html").render(**context)) def _render_mobile_search_button(request: Request, search_filters: SearchFilters): @@ -787,6 +851,7 @@ def _render_mobile_search_button(request: Request, search_filters: SearchFilters gui.button( icons.search, type="tertiary", + unsafe_allow_html=True, className="m-0", onClick=JS_SHOW_MOBILE_SEARCH, ) @@ -829,16 +894,18 @@ def _render_mobile_search_button(request: Request, search_filters: SearchFilters """ -def anonymous_login_container(request: Request, context: dict): +def anonymous_login_container( + request: Request, context: dict, hide_sign_in: bool = False +): next_url = str(furl(request.url).set(origin=None)) login_url = str(furl("/login/", query_params=dict(next=next_url))) - with gui.tag("a", href=login_url, className="pe-2 d-none d-lg-block"): - gui.html("Sign In") - popover, content = gui.popover(interactive=True) - with popover, gui.div(className="d-flex align-items-center"): + with popover, gui.div(className="d-flex align-items-center overflow-hidden"): + if not hide_sign_in: + with gui.tag("a", href=login_url, className="pe-2"): + gui.html("Sign In") gui.html( templates.get_template("google_one_tap_button.html").render(**context) + '' diff --git a/static/css/app.css b/static/css/app.css index 2daec1019..f916ce7ce 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -618,11 +618,6 @@ a.text-primary:hover { font-size: 14px; } -html { - /* fix for jumping scrollbar issue - https://css-tricks.com/elegant-fix-jumping-scrollbar-issue/ */ - margin-left: calc(100vw - 100%); -} - .gui-img { width: 100%; max-width: 450px; diff --git a/templates/google_one_tap_button.html b/templates/google_one_tap_button.html index 397e1e512..44022e742 100644 --- a/templates/google_one_tap_button.html +++ b/templates/google_one_tap_button.html @@ -7,8 +7,8 @@ - - + +