mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(desktop-ui): improve macOS sidebar layout (PR #1001)
This commit is contained in:
@@ -13,6 +13,9 @@
|
||||
|
||||
/* OpenClaw × ClawHub Color Palette */
|
||||
:root {
|
||||
--desktop-safe-top: 0px;
|
||||
--desktop-safe-bottom: 0px;
|
||||
|
||||
/* Primary - Coral Red (OpenClaw) */
|
||||
--color-primary: #e54d5e;
|
||||
--color-primary-hover: #c93d4e;
|
||||
@@ -152,6 +155,11 @@ body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body.electron-macos {
|
||||
--desktop-safe-top: 30px;
|
||||
--desktop-safe-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Selection — with fallback for browsers that don't support color-mix() */
|
||||
::selection {
|
||||
background-color: rgba(229, 77, 94, 0.22);
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
OPENAI_COMPATIBLE_PREFIX,
|
||||
ANTHROPIC_COMPATIBLE_PREFIX,
|
||||
} from "@/shared/constants/providers";
|
||||
import { useIsElectron } from "@/shared/hooks/useElectron";
|
||||
|
||||
const isE2EMode = process.env.NEXT_PUBLIC_OMNIROUTE_E2E_MODE === "1";
|
||||
|
||||
@@ -122,8 +123,11 @@ function usePageInfo(pathname: string | null): {
|
||||
export default function Header({ onMenuClick, showMenuButton = true }) {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const isElectron = useIsElectron();
|
||||
const t = useTranslations("header");
|
||||
const { title, description, breadcrumbs } = usePageInfo(pathname);
|
||||
const isMacElectron =
|
||||
isElectron && typeof window !== "undefined" && window.electronAPI?.platform === "darwin";
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
@@ -138,7 +142,14 @@ export default function Header({ onMenuClick, showMenuButton = true }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="flex items-center justify-between px-8 py-5 border-b border-black/5 dark:border-white/5 bg-bg/80 backdrop-blur-xl z-10 sticky top-0">
|
||||
<header
|
||||
className="sticky top-0 z-10 flex items-center justify-between border-b border-black/5 bg-bg/80 px-8 py-5 backdrop-blur-xl dark:border-white/5"
|
||||
style={{
|
||||
paddingTop: isMacElectron
|
||||
? "calc(1.25rem + var(--desktop-safe-top))"
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
{/* Mobile menu button */}
|
||||
<div className="flex items-center gap-3 lg:hidden">
|
||||
{showMenuButton && (
|
||||
|
||||
@@ -24,10 +24,12 @@ export default function Sidebar({
|
||||
onClose,
|
||||
collapsed = false,
|
||||
onToggleCollapse,
|
||||
isMacElectron = false,
|
||||
}: {
|
||||
onClose?: any;
|
||||
collapsed?: boolean;
|
||||
onToggleCollapse?: any;
|
||||
isMacElectron?: boolean;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
const t = useTranslations("sidebar");
|
||||
@@ -192,9 +194,12 @@ export default function Sidebar({
|
||||
<>
|
||||
<aside
|
||||
className={cn(
|
||||
"flex flex-col h-full border-r border-black/5 dark:border-white/5 bg-vibrancy backdrop-blur-xl transition-all duration-300 ease-in-out",
|
||||
collapsed ? "w-16" : "w-72"
|
||||
"flex h-full min-h-0 flex-col border-r border-black/5 bg-vibrancy backdrop-blur-xl transition-all duration-300 ease-in-out dark:border-white/5",
|
||||
collapsed ? "w-16" : "w-80"
|
||||
)}
|
||||
style={{
|
||||
paddingTop: isMacElectron ? "var(--desktop-safe-top)" : undefined,
|
||||
}}
|
||||
>
|
||||
<a
|
||||
href="#main-content"
|
||||
@@ -202,34 +207,42 @@ export default function Sidebar({
|
||||
>
|
||||
Skip to content
|
||||
</a>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-2 pt-5 pb-2",
|
||||
collapsed ? "px-3 justify-center" : "px-6"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="w-3 h-3 rounded-full bg-[#FF5F56]" />
|
||||
<div className="w-3 h-3 rounded-full bg-[#FFBD2E]" />
|
||||
<div className="w-3 h-3 rounded-full bg-[#27C93F]" />
|
||||
{!collapsed && <div className="flex-1" />}
|
||||
{onToggleCollapse && (
|
||||
<button
|
||||
onClick={onToggleCollapse}
|
||||
title={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||
aria-expanded={!collapsed}
|
||||
aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||
className={cn(
|
||||
"p-1 rounded-md text-text-muted/50 hover:text-text-muted hover:bg-black/5 dark:hover:bg-white/5 transition-colors",
|
||||
collapsed && "mt-2"
|
||||
)}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px]" aria-hidden="true">
|
||||
{collapsed ? "chevron_right" : "chevron_left"}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{(onToggleCollapse || !isMacElectron) && (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-2 pb-2",
|
||||
isMacElectron ? "pt-3" : "pt-5",
|
||||
collapsed ? "px-3 justify-center" : "px-6"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{!isMacElectron && (
|
||||
<>
|
||||
<div className="w-3 h-3 rounded-full bg-[#FF5F56]" />
|
||||
<div className="w-3 h-3 rounded-full bg-[#FFBD2E]" />
|
||||
<div className="w-3 h-3 rounded-full bg-[#27C93F]" />
|
||||
</>
|
||||
)}
|
||||
{!collapsed && <div className="flex-1" />}
|
||||
{onToggleCollapse && (
|
||||
<button
|
||||
onClick={onToggleCollapse}
|
||||
title={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||
aria-expanded={!collapsed}
|
||||
aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||
className={cn(
|
||||
"rounded-md p-1 text-text-muted/50 transition-colors hover:bg-black/5 hover:text-text-muted dark:hover:bg-white/5",
|
||||
collapsed && !isMacElectron && "mt-2",
|
||||
isMacElectron && "ml-auto"
|
||||
)}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px]" aria-hidden="true">
|
||||
{collapsed ? "chevron_right" : "chevron_left"}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={cn("py-4", collapsed ? "px-2" : "px-6")}>
|
||||
<Link
|
||||
@@ -261,7 +274,7 @@ export default function Sidebar({
|
||||
<nav
|
||||
aria-label="Main navigation"
|
||||
className={cn(
|
||||
"flex-1 py-2 space-y-1 overflow-y-auto custom-scrollbar",
|
||||
"min-h-0 flex-1 space-y-1 overflow-y-auto py-2 custom-scrollbar",
|
||||
collapsed ? "px-2" : "px-4"
|
||||
)}
|
||||
>
|
||||
@@ -288,9 +301,14 @@ export default function Sidebar({
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"border-t border-black/5 dark:border-white/5",
|
||||
"shrink-0 border-t border-black/5 dark:border-white/5",
|
||||
collapsed ? "p-2 flex flex-col gap-1" : "p-3 flex gap-2"
|
||||
)}
|
||||
style={{
|
||||
paddingBottom: isMacElectron
|
||||
? "calc(0.75rem + var(--desktop-safe-bottom))"
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => setShowRestartModal(true)}
|
||||
@@ -298,7 +316,7 @@ export default function Sidebar({
|
||||
className={cn(
|
||||
"flex items-center justify-center gap-2 rounded-lg font-medium transition-all",
|
||||
"text-amber-500 hover:bg-amber-500/10 border border-amber-500/20 hover:border-amber-500/40",
|
||||
collapsed ? "p-2" : "flex-1 px-3 py-2 text-sm"
|
||||
collapsed ? "p-2" : "flex-1 min-w-0 px-3 py-2 text-xs"
|
||||
)}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[18px]">restart_alt</span>
|
||||
@@ -310,7 +328,7 @@ export default function Sidebar({
|
||||
className={cn(
|
||||
"flex items-center justify-center gap-2 rounded-lg font-medium transition-all",
|
||||
"text-red-500 hover:bg-red-500/10 border border-red-500/20 hover:border-red-500/40",
|
||||
collapsed ? "p-2" : "flex-1 px-3 py-2 text-sm"
|
||||
collapsed ? "p-2" : "flex-1 min-w-0 px-3 py-2 text-xs"
|
||||
)}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[18px]">power_settings_new</span>
|
||||
@@ -367,4 +385,5 @@ Sidebar.propTypes = {
|
||||
onClose: PropTypes.func,
|
||||
collapsed: PropTypes.bool,
|
||||
onToggleCollapse: PropTypes.func,
|
||||
isMacElectron: PropTypes.bool,
|
||||
};
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import Sidebar from "../Sidebar";
|
||||
import Header from "../Header";
|
||||
import Breadcrumbs from "../Breadcrumbs";
|
||||
import NotificationToast from "../NotificationToast";
|
||||
import MaintenanceBanner from "../MaintenanceBanner";
|
||||
import { useIsElectron } from "@/shared/hooks/useElectron";
|
||||
|
||||
const SIDEBAR_COLLAPSED_KEY = "sidebar-collapsed";
|
||||
const isE2EMode = process.env.NEXT_PUBLIC_OMNIROUTE_E2E_MODE === "1";
|
||||
|
||||
export default function DashboardLayout({ children }) {
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const isElectron = useIsElectron();
|
||||
const [collapsed, setCollapsed] = useState(() => {
|
||||
if (typeof window === "undefined") return false;
|
||||
try {
|
||||
@@ -21,6 +23,19 @@ export default function DashboardLayout({ children }) {
|
||||
}
|
||||
});
|
||||
|
||||
const isMacElectron =
|
||||
isElectron && typeof window !== "undefined" && window.electronAPI?.platform === "darwin";
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof document === "undefined") return;
|
||||
|
||||
document.body.classList.toggle("electron-macos", isMacElectron);
|
||||
|
||||
return () => {
|
||||
document.body.classList.remove("electron-macos");
|
||||
};
|
||||
}, [isMacElectron]);
|
||||
|
||||
const handleToggleCollapse = () => {
|
||||
const next = !collapsed;
|
||||
setCollapsed(next);
|
||||
@@ -28,7 +43,7 @@ export default function DashboardLayout({ children }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-full overflow-hidden bg-bg">
|
||||
<div className="flex h-dvh min-h-0 w-full overflow-hidden bg-bg">
|
||||
{/* Mobile sidebar overlay */}
|
||||
{sidebarOpen && (
|
||||
<div
|
||||
@@ -38,8 +53,12 @@ export default function DashboardLayout({ children }) {
|
||||
)}
|
||||
|
||||
{/* Sidebar - Desktop */}
|
||||
<div className="hidden lg:flex">
|
||||
<Sidebar collapsed={collapsed} onToggleCollapse={handleToggleCollapse} />
|
||||
<div className="hidden min-h-0 lg:flex">
|
||||
<Sidebar
|
||||
collapsed={collapsed}
|
||||
onToggleCollapse={handleToggleCollapse}
|
||||
isMacElectron={isMacElectron}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Sidebar - Mobile: full viewport height with proper scroll containment */}
|
||||
@@ -48,17 +67,17 @@ export default function DashboardLayout({ children }) {
|
||||
sidebarOpen ? "translate-x-0" : "-translate-x-full"
|
||||
}`}
|
||||
>
|
||||
<Sidebar onClose={() => setSidebarOpen(false)} />
|
||||
<Sidebar onClose={() => setSidebarOpen(false)} isMacElectron={isMacElectron} />
|
||||
</div>
|
||||
|
||||
{/* Main content */}
|
||||
<main
|
||||
id="main-content"
|
||||
className="flex flex-col flex-1 h-full min-w-0 relative transition-colors duration-300"
|
||||
className="relative flex min-h-0 flex-1 min-w-0 flex-col transition-colors duration-300"
|
||||
>
|
||||
<Header onMenuClick={() => setSidebarOpen(true)} />
|
||||
{!isE2EMode && <MaintenanceBanner />}
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden custom-scrollbar p-4 sm:p-6 lg:p-10">
|
||||
<div className="flex-1 min-h-0 overflow-y-auto overflow-x-hidden custom-scrollbar p-4 sm:p-6 lg:p-10">
|
||||
<div className="max-w-7xl mx-auto w-full">
|
||||
<Breadcrumbs />
|
||||
{children}
|
||||
|
||||
2
test-cherry.js
Normal file
2
test-cherry.js
Normal file
@@ -0,0 +1,2 @@
|
||||
const { convertOpenAIContentToParts } = require('./open-sse/translator/helpers/geminiHelper.ts');
|
||||
// since it's typescript/ESM, we might need a ts-node or vitest
|
||||
9
test_gemini.mjs
Normal file
9
test_gemini.mjs
Normal file
@@ -0,0 +1,9 @@
|
||||
import { convertOpenAIContentToParts } from "./open-sse/translator/helpers/geminiHelper.ts";
|
||||
|
||||
const cherryMsg = [
|
||||
{ type: "text", text: "What runs on this?" },
|
||||
{ type: "file", file: { data: "ABCDEFG", type: "application/pdf" } },
|
||||
{ type: "file", file_url: { url: "data:application/pdf;base64,ABCDEFG" } },
|
||||
];
|
||||
|
||||
console.log(convertOpenAIContentToParts(cherryMsg));
|
||||
Reference in New Issue
Block a user