Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions agentex-sgp-app/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ out/
ehthumbs.db
Thumbs.db

# Claude
.claude/

# Editor directories and files
.vscode/
.idea/
Expand Down
3 changes: 3 additions & 0 deletions agentex-sgp-app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
# testing
/coverage

# claude
.claude/

# next.js
/.next/
/out/
Expand Down
5 changes: 4 additions & 1 deletion agentex-sgp-app/.prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false
"bracketSameLine": false,
"arrowParens": "avoid",

"plugins": ["prettier-plugin-tailwindcss"]
}
2 changes: 1 addition & 1 deletion agentex-sgp-app/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Complex synchronization mechanism in `lib/pending-message.ts`:
### Component Organization

- `components/agentex/*` — Domain-specific reusable components
- `components/ui/*` — Generic UI components (shadcn/ui based)
- `components/ui/*` — Generic UI components (shadcn/ui based) - only used by shadcn, do not add components to this directory unless they are shadcn
- `entrypoints/*/` — Mode-specific page components and views
- Follow pattern: **components should be presentational, controllers handle side effects**

Expand Down
61 changes: 61 additions & 0 deletions agentex-sgp-app/app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use client';

import { AlertCircle } from 'lucide-react';

import { TaskTopBar } from '@/components/agentex/task-top-bar';
import { Button } from '@/components/ui/button';

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="fixed inset-0 flex">
{/* Main Content Area */}
<div className="bg-task-background flex h-full flex-1 flex-col">
<TaskTopBar taskId={null} />

{/* Error Message - Centered */}
<div className="flex flex-1 flex-col items-center justify-center p-8">
<div className="w-full max-w-md space-y-6 text-center">
<div className="flex justify-center">
<div className="bg-destructive/10 rounded-full p-4">
<AlertCircle className="text-destructive h-12 w-12" />
</div>
</div>

<div className="space-y-2">
<h1 className="text-2xl font-semibold">Something went wrong</h1>
<p className="text-muted-foreground">
We encountered an error while loading the agent.
</p>
</div>

{error.message && (
<div className="bg-destructive/10 rounded-lg p-4 text-left text-sm">
<p className="text-destructive font-mono break-words">
{error.message}
</p>
</div>
)}

<div className="flex justify-center gap-3">
<Button onClick={reset} variant="default">
Try again
</Button>
<Button
onClick={() => (window.location.href = '/')}
variant="outline"
>
Go home
</Button>
</div>
</div>
</div>
</div>
</div>
);
}
81 changes: 1 addition & 80 deletions agentex-sgp-app/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
--destructive: #7a1331;
--destructive-foreground: #ffced5;
--border: oklch(1 0 0 / 10%);
--input: rgba(113, 77, 255, 0.15);
--input: #28292a;
--ring: #d1d5db;
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
Expand Down Expand Up @@ -154,85 +154,6 @@
body {
@apply bg-background text-foreground;
}

textarea[data-slot='textarea'] {
background-color: var(--textarea-input);
color: var(--textarea-foreground);
}

h1 {
scroll-margin-top: 5rem;
text-align: center;
font-size: 2.25rem;
line-height: 2.5rem;
font-weight: 800;
letter-spacing: -0.025em;
text-wrap: balance;
}

h2 {
scroll-margin-top: 5rem;
font-size: 1.875rem;
line-height: 2.25rem;
font-weight: 600;
letter-spacing: -0.025em;
}

h2:first-child {
margin-top: 0;
}

h3 {
scroll-margin-top: 5rem;
font-size: 1.5rem;
line-height: 2rem;
font-weight: 600;
letter-spacing: -0.025em;
}

h4 {
scroll-margin-top: 5rem;
font-size: 1.25rem;
line-height: 1.75rem;
font-weight: 600;
letter-spacing: -0.025em;
}

p {
line-height: 1.75rem;
}

blockquote {
margin-top: 1.5rem;
border-left: 2px solid;
padding-left: 1.5rem;
font-style: italic;
}

ul {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
margin-left: 1.5rem;
list-style-type: disc;
}

ul > li {
margin-top: 0.5rem;
}
}

.code-block {
background-color: var(--color-muted);
position: relative;
border-radius: var(--radius-sm);
padding-left: 0.3rem;
padding-right: 0.3rem;
padding-top: 0.2rem;
padding-bottom: 0.2rem;
font-family: var(--font-geist-mono);
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 600;
}

.hide-scrollbar {
Expand Down
24 changes: 14 additions & 10 deletions agentex-sgp-app/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ThemeProvider } from '@/components/agentex/theme-provider';
import type { Metadata } from 'next';
import { Geist, Geist_Mono } from 'next/font/google';

import { QueryProvider, ThemeProvider } from '@/components/providers';

import type { Metadata } from 'next';
import './globals.css';

const geistSans = Geist({
Expand Down Expand Up @@ -28,14 +30,16 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem={false}
disableTransitionOnChange
>
{children}
</ThemeProvider>
<QueryProvider>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem={false}
disableTransitionOnChange
>
{children}
</ThemeProvider>
</QueryProvider>
</body>
</html>
);
Expand Down
132 changes: 122 additions & 10 deletions agentex-sgp-app/app/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,128 @@
'use client';

import { Suspense } from 'react';

import { ArrowUp } from 'lucide-react';

import { IconButton } from '@/components/agentex/icon-button';
import { Skeleton } from '@/components/ui/skeleton';
import { useSafeSearchParams } from '@/hooks/use-safe-search-params';

function LoadingContent() {
const { taskID } = useSafeSearchParams();

// If there's a task ID, show the task/chat loading state
if (taskID) {
return (
<div className="fixed inset-0 flex w-full">
{/* Sidebar Skeleton */}
<div className="bg-sidebar border-sidebar-border flex h-full w-64 flex-col border-r">
{/* Header Section */}
<div className="border-sidebar-border border-b pt-4 pr-4 pb-6 pl-2">
<div className="flex items-center gap-2 pb-4 pl-2">
<Skeleton className="h-6 w-24" />
</div>
<Skeleton className="h-10 w-full" />
</div>

{/* Task List */}
<div className="flex flex-col gap-5 overflow-y-auto py-4 pr-2 pl-4">
{[...Array(8)].map((_, i) => (
<Skeleton key={i} className="h-5 w-full" />
))}
</div>
</div>

{/* Main Content Area Skeleton */}
<div className="bg-task-background flex h-full flex-1 flex-col">
{/* Top Bar */}
<div className="border-border border-b px-4 py-3">
<Skeleton className="h-8 w-48" />
</div>

{/* Messages Area */}
<div className="flex flex-1 flex-col items-center overflow-y-auto px-4 pt-4">
<div className="flex w-full max-w-[800px] flex-col gap-4">
{/* User Message Skeleton */}
<div className="flex justify-end">
<Skeleton className="h-15 w-1/4 rounded-lg" />
</div>

{/* Agent Message Skeleton */}
<div className="flex flex-col justify-start gap-3">
<Skeleton className="h-4 w-5/6" />
<Skeleton className="h-4 w-7/8" />
<Skeleton className="h-4 w-5/8" />
</div>

{/* User Message Skeleton */}
<div className="flex justify-end">
<Skeleton className="h-15 w-1/4 rounded-lg" />
</div>

{/* Agent Message Skeleton */}
<div className="flex flex-col justify-start gap-3">
<Skeleton className="h-4 w-5/6" />
<Skeleton className="h-4 w-7/8" />
<Skeleton className="h-4 w-5/8" />
</div>
</div>
</div>

{/* Input Form Skeleton */}
<div className="flex w-full justify-center px-4 py-8 sm:px-6 md:px-8">
<div className="w-full max-w-3xl">
<Skeleton className="h-[58px] w-full rounded-full" />
</div>
</div>
</div>
</div>
);
}

export default function Loading() {
return (
<>
<header className="w-full md:sticky mt-4 md:top-0 md:z-10 bg-background">
<div className="mx-auto max-w-[min(100%-var(--spacing)*4,var(--max-page-content-width))]">
<h1>Agentex: loading...</h1>
<div className="fixed inset-0 flex w-full">
<div className="flex h-full flex-1 flex-col justify-center">
<div className="flex items-center justify-center">
<div className="flex flex-col items-center justify-center gap-6 px-4">
<div className="text-2xl font-bold">Select an Agent:</div>
<div className="flex max-w-4xl flex-wrap items-center justify-center gap-2">
{[...Array(6)].map((_, i) => (
<Skeleton key={i} className="h-9.5 w-32 rounded-full" />
))}
</div>
</div>
</div>
<div className="flex w-full justify-center px-4 py-8 sm:px-6 md:px-8">
<div className="w-full max-w-3xl">
<div className="w-full opacity-50">
<div className="border-input dark:bg-input/30 disabled:bg-muted flex w-full justify-between rounded-full border bg-transparent py-2 pr-2 pl-6 disabled:cursor-not-allowed">
<input
type="text"
value=""
disabled
placeholder="Select an agent to start"
className="mr-2 flex-1 outline-none focus:ring-0 focus:outline-none"
readOnly
/>
<IconButton
className="pointer-events-auto size-10 rounded-full"
disabled
icon={ArrowUp}
/>
</div>
</div>
</div>
</div>
</header>
<main className="w-full">
<Skeleton className="mx-auto w-[min(100%-var(--spacing)*4,var(--max-page-content-width))] h-96 animate-pulse" />
</main>
</>
</div>
</div>
);
}

export default function Loading() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LoadingContent />
</Suspense>
);
}
Loading