diff --git a/ee/tabby-ui/app/(dashboard)/components/main-content.tsx b/ee/tabby-ui/app/(dashboard)/components/main-content.tsx new file mode 100644 index 000000000000..1e05d9db306b --- /dev/null +++ b/ee/tabby-ui/app/(dashboard)/components/main-content.tsx @@ -0,0 +1,25 @@ +'use client' + +import { ScrollArea } from '@/components/ui/scroll-area' +import { BANNER_HEIGHT, useShowDemoBanner } from '@/components/demo-banner' +import { Header } from '@/components/header' + +export default function MainContent({ + children +}: { + children: React.ReactNode +}) { + const [isShowDemoBanner] = useShowDemoBanner() + const style = isShowDemoBanner + ? { height: `calc(100vh - ${BANNER_HEIGHT})` } + : { height: '100vh' } + return ( + <> + {/* Wraps right hand side into ScrollArea, making scroll bar consistent across all browsers */} + +
+
{children}
+ + + ) +} diff --git a/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx b/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx index 2d5ba85f256c..a6869d596d73 100644 --- a/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx +++ b/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx @@ -22,6 +22,8 @@ import { IconLightingBolt, IconUser } from '@/components/ui/icons' +import { ScrollArea } from '@/components/ui/scroll-area' +import { BANNER_HEIGHT, useShowDemoBanner } from '@/components/demo-banner' export interface SidebarProps { children?: React.ReactNode @@ -30,12 +32,17 @@ export interface SidebarProps { export default function Sidebar({ children, className }: SidebarProps) { const [{ data }] = useMe() + const [isShowDemoBanner] = useShowDemoBanner() const isAdmin = data?.me.isAdmin + const style = isShowDemoBanner + ? { height: `calc(100vh - ${BANNER_HEIGHT})` } + : { height: '100vh' } return ( -
-
+
-
+ ) } diff --git a/ee/tabby-ui/app/(dashboard)/layout.tsx b/ee/tabby-ui/app/(dashboard)/layout.tsx index 1d8fdedb86b4..391b2af67fc0 100644 --- a/ee/tabby-ui/app/(dashboard)/layout.tsx +++ b/ee/tabby-ui/app/(dashboard)/layout.tsx @@ -1,8 +1,6 @@ import { Metadata } from 'next' -import { ScrollArea } from '@/components/ui/scroll-area' -import { Header } from '@/components/header' - +import MainContent from './components/main-content' import Sidebar from './components/sidebar' export const metadata: Metadata = { @@ -12,20 +10,15 @@ export const metadata: Metadata = { } } -interface DashboardLayoutProps { +export default function RootLayout({ + children +}: { children: React.ReactNode -} - -export default function RootLayout({ children }: DashboardLayoutProps) { +}) { return (
- - {/* Wraps right hand side into ScrollArea, making scroll bar consistent across all browsers */} - -
-
{children}
- + {children}
) } diff --git a/ee/tabby-ui/app/layout.tsx b/ee/tabby-ui/app/layout.tsx index 87e4fba747d7..c933da358d35 100644 --- a/ee/tabby-ui/app/layout.tsx +++ b/ee/tabby-ui/app/layout.tsx @@ -6,6 +6,7 @@ import '@/app/globals.css' import { fontMono, fontSans } from '@/lib/fonts' import { cn } from '@/lib/utils' +import { DemoBanner } from '@/components/demo-banner' import { Providers } from '@/components/providers' import { TailwindIndicator } from '@/components/tailwind-indicator' @@ -37,7 +38,10 @@ export default function RootLayout({ children }: RootLayoutProps) { )} > -
{children}
+
+ + {children} +
diff --git a/ee/tabby-ui/components/demo-banner.tsx b/ee/tabby-ui/components/demo-banner.tsx new file mode 100644 index 000000000000..0fd51ec4c85b --- /dev/null +++ b/ee/tabby-ui/components/demo-banner.tsx @@ -0,0 +1,96 @@ +'use client' + +import { useEffect, useState } from 'react' +import { isNil } from 'lodash-es' +import { useTheme } from 'next-themes' +import useSWRImmutable from 'swr/immutable' + +import { useIsDemoMode } from '@/lib/hooks/use-server-info' +import { cn } from '@/lib/utils' +import { + IconClose, + IconGitFork, + IconGithub, + IconStar +} from '@/components/ui/icons' + +export const BANNER_HEIGHT = '3.5rem' + +export function useShowDemoBanner(): [boolean, (isShow: boolean) => void] { + const isDemoMode = useIsDemoMode() + const [isShow, setIsShow] = useState(false) + + useEffect(() => { + if (!isNil(isDemoMode)) { + setIsShow(isDemoMode) + } + }, [isDemoMode]) + + return [isShow, setIsShow] +} + +export function DemoBanner() { + const [isShow, setIsShow] = useShowDemoBanner() + const [slackIconFill, setSlackIconFill] = useState('') + const { theme } = useTheme() + const { data } = useSWRImmutable( + 'https://api.github.com/repos/TabbyML/tabby', + (url: string) => fetch(url).then(res => res.json()) + ) + + useEffect(() => { + setSlackIconFill(theme === 'dark' ? '#171615' : '#ECECEC') + }, [isShow, theme]) + + const style = isShow ? { height: BANNER_HEIGHT } : { height: 0 } + return ( +
+ + 📆 + Book a 30-minute product demo. + + +
+ + +
+

TabbyML/tabby

+
+ + {data?.stargazers_count} + + {data?.forks_count} +
+
+
+ + setIsShow(false)} + /> +
+
+ ) +} diff --git a/ee/tabby-ui/components/ui/icons.tsx b/ee/tabby-ui/components/ui/icons.tsx index 815189c08547..28dfb7da1336 100644 --- a/ee/tabby-ui/components/ui/icons.tsx +++ b/ee/tabby-ui/components/ui/icons.tsx @@ -2,7 +2,7 @@ import * as React from 'react' // FIXME(wwayne): Review each icons and consider re-export from `lucide-react`. -import { BookOpenText, ChevronsDownUp, Mail } from 'lucide-react' +import { BookOpenText, ChevronsDownUp, GitFork, Mail, Star } from 'lucide-react' import { cn } from '@/lib/utils' @@ -561,44 +561,44 @@ function IconSlack({ className, ...props }: React.ComponentProps<'svg'>) { @@ -1400,6 +1400,20 @@ function IconChevronsDownUp({ ) } +const IconStar = ({ + className, + ...props +}: React.ComponentProps) => ( + +) + +const IconGitFork = ({ + className, + ...props +}: React.ComponentProps) => ( + +) + export { IconEdit, IconNextChat, @@ -1471,5 +1485,7 @@ export { IconActivity, IconBookOpenText, IconMail, - IconChevronsDownUp + IconChevronsDownUp, + IconStar, + IconGitFork }