mirror of https://github.com/kortix-ai/suna.git
mobile
This commit is contained in:
parent
75c85b7f4c
commit
418b721783
|
@ -10,6 +10,7 @@ import { GoogleAnalytics } from '@next/third-parties/google';
|
||||||
import { SpeedInsights } from '@vercel/speed-insights/next';
|
import { SpeedInsights } from '@vercel/speed-insights/next';
|
||||||
import Script from 'next/script';
|
import Script from 'next/script';
|
||||||
import { PostHogIdentify } from '@/components/posthog-identify';
|
import { PostHogIdentify } from '@/components/posthog-identify';
|
||||||
|
import '@/lib/polyfills'; // Load polyfills early
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: '--font-geist-sans',
|
variable: '--font-geist-sans',
|
||||||
|
|
|
@ -233,9 +233,9 @@ export function HeroSection() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="hero" className="w-full relative overflow-hidden">
|
<section id="hero" className="w-full relative overflow-hidden">
|
||||||
<div className="relative flex flex-col items-center w-full px-6">
|
<div className="relative flex flex-col items-center w-full px-4 sm:px-6">
|
||||||
{/* Left side flickering grid with gradient fades */}
|
{/* Left side flickering grid with gradient fades */}
|
||||||
<div className="absolute left-0 top-0 h-[600px] md:h-[800px] w-1/3 -z-10 overflow-hidden">
|
<div className="hidden sm:block absolute left-0 top-0 h-[500px] sm:h-[600px] md:h-[800px] w-1/4 sm:w-1/3 -z-10 overflow-hidden">
|
||||||
{/* Horizontal fade from left to right */}
|
{/* Horizontal fade from left to right */}
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-transparent to-background z-10" />
|
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-transparent to-background z-10" />
|
||||||
|
|
||||||
|
@ -245,18 +245,20 @@ export function HeroSection() {
|
||||||
{/* Vertical fade to bottom */}
|
{/* Vertical fade to bottom */}
|
||||||
<div className="absolute inset-x-0 bottom-0 h-48 bg-gradient-to-t from-background via-background/90 to-transparent z-10" />
|
<div className="absolute inset-x-0 bottom-0 h-48 bg-gradient-to-t from-background via-background/90 to-transparent z-10" />
|
||||||
|
|
||||||
<FlickeringGrid
|
{mounted && (
|
||||||
className="h-full w-full"
|
<FlickeringGrid
|
||||||
squareSize={mounted && tablet ? 2 : 2.5}
|
className="h-full w-full"
|
||||||
gridGap={mounted && tablet ? 2 : 2.5}
|
squareSize={tablet ? 2 : 2.5}
|
||||||
color="var(--secondary)"
|
gridGap={tablet ? 2 : 2.5}
|
||||||
maxOpacity={0.4}
|
color="var(--secondary)"
|
||||||
flickerChance={isScrolling ? 0.01 : 0.03} // Low flickering when not scrolling
|
maxOpacity={tablet ? 0.2 : 0.4}
|
||||||
/>
|
flickerChance={isScrolling ? 0.005 : (tablet ? 0.015 : 0.03)} // Lower performance impact on mobile
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right side flickering grid with gradient fades */}
|
{/* Right side flickering grid with gradient fades */}
|
||||||
<div className="absolute right-0 top-0 h-[600px] md:h-[800px] w-1/3 -z-10 overflow-hidden">
|
<div className="hidden sm:block absolute right-0 top-0 h-[500px] sm:h-[600px] md:h-[800px] w-1/4 sm:w-1/3 -z-10 overflow-hidden">
|
||||||
{/* Horizontal fade from right to left */}
|
{/* Horizontal fade from right to left */}
|
||||||
<div className="absolute inset-0 bg-gradient-to-l from-transparent via-transparent to-background z-10" />
|
<div className="absolute inset-0 bg-gradient-to-l from-transparent via-transparent to-background z-10" />
|
||||||
|
|
||||||
|
@ -266,20 +268,22 @@ export function HeroSection() {
|
||||||
{/* Vertical fade to bottom */}
|
{/* Vertical fade to bottom */}
|
||||||
<div className="absolute inset-x-0 bottom-0 h-48 bg-gradient-to-t from-background via-background/90 to-transparent z-10" />
|
<div className="absolute inset-x-0 bottom-0 h-48 bg-gradient-to-t from-background via-background/90 to-transparent z-10" />
|
||||||
|
|
||||||
<FlickeringGrid
|
{mounted && (
|
||||||
className="h-full w-full"
|
<FlickeringGrid
|
||||||
squareSize={mounted && tablet ? 2 : 2.5}
|
className="h-full w-full"
|
||||||
gridGap={mounted && tablet ? 2 : 2.5}
|
squareSize={tablet ? 2 : 2.5}
|
||||||
color="var(--secondary)"
|
gridGap={tablet ? 2 : 2.5}
|
||||||
maxOpacity={0.4}
|
color="var(--secondary)"
|
||||||
flickerChance={isScrolling ? 0.01 : 0.03} // Low flickering when not scrolling
|
maxOpacity={tablet ? 0.2 : 0.4}
|
||||||
/>
|
flickerChance={isScrolling ? 0.005 : (tablet ? 0.015 : 0.03)} // Lower performance impact on mobile
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Center content background with rounded bottom */}
|
{/* Center content background with rounded bottom */}
|
||||||
<div className="absolute inset-x-1/4 top-0 h-[600px] md:h-[800px] -z-20 bg-background rounded-b-xl"></div>
|
<div className="absolute inset-x-0 sm:inset-x-1/6 md:inset-x-1/4 top-0 h-[500px] sm:h-[600px] md:h-[800px] -z-20 bg-background rounded-b-xl"></div>
|
||||||
|
|
||||||
<div className="relative z-10 pt-32 mx-auto h-full w-full max-w-6xl flex flex-col items-center justify-center">
|
<div className="relative z-10 pt-16 sm:pt-24 md:pt-32 mx-auto h-full w-full max-w-6xl flex flex-col items-center justify-center">
|
||||||
{/* <p className="border border-border bg-accent rounded-full text-sm h-8 px-3 flex items-center gap-2">
|
{/* <p className="border border-border bg-accent rounded-full text-sm h-8 px-3 flex items-center gap-2">
|
||||||
{hero.badgeIcon}
|
{hero.badgeIcon}
|
||||||
{hero.badge}
|
{hero.badge}
|
||||||
|
@ -314,17 +318,17 @@ export function HeroSection() {
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</Link> */}
|
</Link> */}
|
||||||
<div className="flex flex-col items-center justify-center gap-4 pt-12 max-w-4xl mx-auto">
|
<div className="flex flex-col items-center justify-center gap-3 sm:gap-4 pt-8 sm:pt-12 max-w-4xl mx-auto">
|
||||||
<h1 className="text-3xl md:text-4xl lg:text-5xl xl:text-6xl font-medium tracking-tighter text-balance text-center">
|
<h1 className="text-3xl md:text-4xl lg:text-5xl xl:text-6xl font-medium tracking-tighter text-balance text-center px-2">
|
||||||
<span className="text-primary">Build, manage and train your </span>
|
<span className="text-primary">Build, manage and train your </span>
|
||||||
<span className="text-secondary">AI Workforce.</span>
|
<span className="text-secondary">AI Workforce.</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-base md:text-lg text-center text-muted-foreground font-medium text-balance leading-relaxed tracking-tight max-w-2xl">
|
<p className="text-base md:text-lg text-center text-muted-foreground font-medium text-balance leading-relaxed tracking-tight max-w-2xl px-2">
|
||||||
Kortix – the simplest way to migrate from human to AI.
|
Kortix – the simplest way to migrate from human to AI.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col items-center w-full max-w-3xl mx-auto gap-2 flex-wrap justify-center">
|
<div className="flex flex-col items-center w-full max-w-3xl mx-auto gap-2 flex-wrap justify-center px-2 sm:px-0">
|
||||||
<div className="w-full relative">
|
<div className="w-full relative">
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
<ChatInput
|
<ChatInput
|
||||||
|
@ -347,14 +351,14 @@ export function HeroSection() {
|
||||||
|
|
||||||
{/* Examples section - right after chat input */}
|
{/* Examples section - right after chat input */}
|
||||||
<div className="w-full pt-2">
|
<div className="w-full pt-2">
|
||||||
<Examples onSelectPrompt={setInputValue} count={4} />
|
<Examples onSelectPrompt={setInputValue} count={tablet ? 2 : 4} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-16 sm:mt-32 mx-auto"></div>
|
<div className="mb-8 sm:mb-16 sm:mt-32 mx-auto"></div>
|
||||||
|
|
||||||
{/* Auth Dialog */}
|
{/* Auth Dialog */}
|
||||||
<Dialog open={authDialogOpen} onOpenChange={setAuthDialogOpen}>
|
<Dialog open={authDialogOpen} onOpenChange={setAuthDialogOpen}>
|
||||||
|
|
|
@ -113,8 +113,8 @@ export function Navbar() {
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
className={cn(
|
className={cn(
|
||||||
'sticky z-50 mx-4 flex justify-center transition-all duration-300 md:mx-0',
|
'sticky z-50 flex justify-center transition-all duration-300',
|
||||||
hasScrolled ? 'top-6' : 'top-4 mx-0',
|
hasScrolled ? 'top-6 mx-4 md:mx-0' : 'top-4 mx-2 md:mx-0',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
@ -124,34 +124,35 @@ export function Navbar() {
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'mx-auto max-w-7xl rounded-2xl transition-all duration-300 xl:px-0',
|
'mx-auto max-w-7xl rounded-2xl transition-all duration-300 xl:px-0',
|
||||||
hasScrolled
|
hasScrolled
|
||||||
? 'px-2 border border-border backdrop-blur-lg bg-background/75'
|
? 'px-2 md:px-2 border border-border backdrop-blur-lg bg-background/75'
|
||||||
: 'shadow-none px-7',
|
: 'shadow-none px-3 md:px-7',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex h-[56px] items-center p-4">
|
<div className="flex h-[56px] items-center p-2 md:p-4">
|
||||||
{/* Left Section - Logo */}
|
{/* Left Section - Logo */}
|
||||||
<div className="flex items-center justify-start" style={{ width: '200px' }}>
|
<div className="flex items-center justify-start flex-shrink-0 w-auto md:w-[200px]">
|
||||||
<Link href="/" className="flex items-center gap-3">
|
<Link href="/" className="flex items-center gap-3">
|
||||||
<Image
|
<Image
|
||||||
src={logoSrc}
|
src={logoSrc}
|
||||||
alt="Kortix Logo"
|
alt="Kortix Logo"
|
||||||
width={100}
|
width={80}
|
||||||
height={18}
|
height={14}
|
||||||
|
className="md:w-[100px] md:h-[18px]"
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Center Section - Navigation Menu */}
|
{/* Center Section - Navigation Menu */}
|
||||||
<div className="flex items-center justify-center flex-grow">
|
<div className="hidden md:flex items-center justify-center flex-grow">
|
||||||
<NavMenu />
|
<NavMenu />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Section - Actions */}
|
{/* Right Section - Actions */}
|
||||||
<div className="flex items-center justify-end" style={{ width: '200px' }}>
|
<div className="flex items-center justify-end flex-shrink-0 w-auto md:w-[200px] ml-auto">
|
||||||
<div className="flex flex-row items-center gap-1 md:gap-3 shrink-0">
|
<div className="flex flex-row items-center gap-2 md:gap-3 shrink-0">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<Link
|
<Link
|
||||||
href="https://github.com/kortix-ai/suna"
|
href="https://github.com/kortix-ai/suna"
|
||||||
|
|
|
@ -1,18 +1,53 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Document, Page, pdfjs } from 'react-pdf';
|
import '@/lib/polyfills'; // Import polyfill for Promise.withResolvers
|
||||||
|
|
||||||
// Import styles for annotations and text layer
|
// Import styles for annotations and text layer
|
||||||
import 'react-pdf/dist/Page/AnnotationLayer.css';
|
import 'react-pdf/dist/Page/AnnotationLayer.css';
|
||||||
import 'react-pdf/dist/Page/TextLayer.css';
|
import 'react-pdf/dist/Page/TextLayer.css';
|
||||||
|
|
||||||
// Configure PDF.js worker (same as main PDF renderer)
|
// Internal component that uses react-pdf
|
||||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
interface PdfDocumentProps {
|
||||||
'pdfjs-dist/build/pdf.worker.min.mjs',
|
url: string;
|
||||||
import.meta.url,
|
containerWidth: number | null;
|
||||||
).toString();
|
}
|
||||||
|
|
||||||
|
const PdfDocument = ({ url, containerWidth }: PdfDocumentProps) => {
|
||||||
|
const { Document, Page, pdfjs } = require('react-pdf');
|
||||||
|
|
||||||
|
// Configure PDF.js worker
|
||||||
|
React.useEffect(() => {
|
||||||
|
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||||
|
'pdfjs-dist/build/pdf.worker.min.mjs',
|
||||||
|
import.meta.url,
|
||||||
|
).toString();
|
||||||
|
}, [pdfjs]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Document file={url} className="shadow-none">
|
||||||
|
<Page
|
||||||
|
pageNumber={1}
|
||||||
|
width={containerWidth ?? undefined}
|
||||||
|
renderTextLayer={true}
|
||||||
|
renderAnnotationLayer={true}
|
||||||
|
className="border border-border rounded bg-white"
|
||||||
|
/>
|
||||||
|
</Document>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dynamic import to avoid SSR issues
|
||||||
|
const DynamicPdfDocument = dynamic(() => Promise.resolve(PdfDocument), {
|
||||||
|
ssr: false,
|
||||||
|
loading: () => (
|
||||||
|
<div className="w-full h-full flex items-center justify-center bg-muted/20">
|
||||||
|
<div className="text-sm text-muted-foreground">Loading PDF...</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
interface PdfRendererProps {
|
interface PdfRendererProps {
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
|
@ -43,15 +78,7 @@ export function PdfRenderer({ url, className }: PdfRendererProps) {
|
||||||
return (
|
return (
|
||||||
<div ref={wrapperRef} className={cn('w-full h-full overflow-auto bg-background', className)}>
|
<div ref={wrapperRef} className={cn('w-full h-full overflow-auto bg-background', className)}>
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<Document file={url} className="shadow-none">
|
<DynamicPdfDocument url={url} containerWidth={containerWidth} />
|
||||||
<Page
|
|
||||||
pageNumber={1}
|
|
||||||
width={containerWidth ?? undefined}
|
|
||||||
renderTextLayer={true}
|
|
||||||
renderAnnotationLayer={true}
|
|
||||||
className="border border-border rounded bg-white"
|
|
||||||
/>
|
|
||||||
</Document>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Polyfill for Promise.withResolvers (Node.js 21+ feature)
|
||||||
|
// This is needed for react-pdf compatibility with Node.js 20
|
||||||
|
if (!Promise.withResolvers) {
|
||||||
|
Promise.withResolvers = function <T>(): {
|
||||||
|
promise: Promise<T>;
|
||||||
|
resolve: (value: T | PromiseLike<T>) => void;
|
||||||
|
reject: (reason?: any) => void;
|
||||||
|
} {
|
||||||
|
let resolve: (value: T | PromiseLike<T>) => void;
|
||||||
|
let reject: (reason?: any) => void;
|
||||||
|
|
||||||
|
const promise = new Promise<T>((res, rej) => {
|
||||||
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { promise, resolve: resolve!, reject: reject! };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "agentpress",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
Loading…
Reference in New Issue