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 @@
-
-
+
+