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
22 changes: 19 additions & 3 deletions app/(docs)/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,33 @@ import { notFound } from 'next/navigation';
import { CLICommand } from '@/components/CLICommand';
import { CodeExample } from '@/components/CodeExample';
import { Mermaid } from '@/components/Mermaid';
import { SDKExplorerIframe } from '@/components/SDKExplorerIframe';
import { Sparkle } from '@/components/Sparkle';
import { Tag } from '@/components/Tag';
import { ThemeImage } from '@/components/ThemeImage';
import { TypingAnimation } from '@/components/TypingAnimation';
import { XButton } from '@/components/XButton';
import { source } from '@/lib/source';
import CodeFromFiles from '../../../components/CodeFromFiles';
import { CommunityButton } from '../../../components/Community';
import CopyPageDropdown from '../../../components/CopyPageDropdown';
import { NavButton } from '../../../components/NavButton';
import CodeFromFiles from '../../../components/CodeFromFiles';
import TutorialStep from '../../../components/TutorialStep';


export default async function Page(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);

if (params.slug === undefined) {
return (
<DocsPage full footer={{ enabled: false }}>
<SDKExplorerIframe />
</DocsPage>
);
}

const page = source.getPage(params.slug);
if (!page) notFound();

const MDX = page.data.body;
Expand Down Expand Up @@ -107,6 +115,14 @@ export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;

if (params.slug === undefined) {
return {
title: 'SDK Explorer — Agentuity Docs',
description: 'Interactive examples showcasing the Agentuity SDK',
};
}

const page = source.getPage(params.slug);

if (!page) notFound();
Expand Down
28 changes: 26 additions & 2 deletions app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
}

.dark {
--color-fd-primary: var(--color-cyan-400);
--color-fd-primary: var(--color-cyan-500);
--color-fd-primary-foreground: var(--color-black);
--color-fd-ring: var(--color-cyan-400);
--color-fd-ring: var(--color-cyan-500);
}

/* make sure empty lines are rendered */
Expand Down Expand Up @@ -140,3 +140,27 @@ article > p {
.dark .prose a:not([data-card]) {
text-decoration-color: var(--color-cyan-500);
}

/* SDK Explorer - iframe rendered directly inside DocsLayout */
.sdk-explorer-wrapper {
position: fixed;
inset: 0;
top: var(--fd-nav-height, 0px);
left: var(--fd-sidebar-width, 0px);
z-index: 10;
background: var(--color-fd-background);
}

.sdk-explorer-wrapper iframe {
width: 100%;
height: 100%;
border: 0;
}

/* Mobile: full width, account for nav */
@media (max-width: 767px) {
.sdk-explorer-wrapper {
left: 0;
top: var(--fd-nav-height, 56px);
}
}
2 changes: 1 addition & 1 deletion app/layout.config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { XButton } from '../components/XButton';
*/
export const baseOptions: BaseLayoutProps = {
nav: {
url: '/Get-Started/what-is-agentuity',
url: '/',
title: (
<div className="flex items-center gap-3 font-medium ml-2">
<svg
Expand Down
77 changes: 77 additions & 0 deletions components/SDKExplorerIframe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
'use client';

import { useTheme } from 'next-themes';
import { useEffect, useRef, useState } from 'react';

const EXPLORER_URL = 'https://explorer.agentuity.dev';

export function SDKExplorerIframe() {
const { resolvedTheme } = useTheme();
const iframeRef = useRef<HTMLIFrameElement>(null);
const [mounted, setMounted] = useState(false);
const [iframeLoaded, setIframeLoaded] = useState(false);
// Capture initial theme for iframe src (don't change src on theme updates)
const [initialTheme] = useState(() =>
typeof window !== 'undefined'
? document.documentElement.classList.contains('dark')
? 'dark'
: 'light'
: 'dark'
);

// Only render iframe after mount to avoid hydration mismatch
useEffect(() => {
setMounted(true);
}, []);

// Send theme changes to iframe via postMessage
useEffect(() => {
if (mounted && iframeRef.current?.contentWindow) {
iframeRef.current.contentWindow.postMessage(
{ type: 'SET_THEME', theme: resolvedTheme },
EXPLORER_URL
);
}
}, [resolvedTheme, mounted]);

// Listen for navigation requests from iframe
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
// Validate origin to prevent open redirect
if (event.origin !== new URL(EXPLORER_URL).origin) {
return;
}
if (event.data?.type === 'NAVIGATE' && event.data.path) {
const path = event.data.path;
// Only allow relative paths to prevent open redirects
if (
typeof path === 'string' &&
path.startsWith('/') &&
!path.startsWith('//')
) {
window.location.href = path;
}
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);

// Show placeholder until mounted to avoid hydration mismatch
if (!mounted) {
return <div className="size-full bg-fd-background" />;
}

return (
<div className="sdk-explorer-wrapper">
<iframe
ref={iframeRef}
src={`${EXPLORER_URL}?theme=${initialTheme}`}
title="SDK Explorer"
allow="clipboard-read; clipboard-write"
onLoad={() => setIframeLoaded(true)}
className={`transition-opacity duration-300 ${iframeLoaded ? 'opacity-100' : 'opacity-0'}`}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Introduction
title: What is Agentuity?
description: The full-stack platform for building, deploying, and operating AI agents
---

Expand Down Expand Up @@ -48,9 +48,9 @@ This agent uses the AI SDK to call OpenAI and respond to messages. Deploy with `

Each agent lives in its own file under `src/agent/`. Routes are defined separately in `src/api/index.ts`:

| File | Purpose |
|------|---------|
| `agent.ts` | Agent logic with schema validation |
| File | Purpose |
| -------------- | ------------------------------------ |
| `agent.ts` | Agent logic with schema validation |
| `api/index.ts` | HTTP endpoints that call your agents |

Infrastructure like cron schedules is defined in route code, not config. Rolling back a deployment restores the exact configuration from that version.
Expand Down
2 changes: 1 addition & 1 deletion content/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"title": "Agentuity Docs",
"root": true,
"pages": [
"index",
"[SDK Explorer](/)",
"Get-Started",
"Agents",
"APIs",
Expand Down
7 changes: 1 addition & 6 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@ const config = {
reactStrictMode: true,
serverExternalPackages: ['twoslash', 'typescript'],
redirects: async () => [
{
source: '/Get-Started/what-is-agentuity',
destination: '/',
permanent: false,
},
{
source: '/Introduction',
destination: '/',
destination: '/Get-Started/what-is-agentuity',
permanent: true,
},
{
Expand Down
20 changes: 19 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.