Skip to content
This repository was archived by the owner on Mar 25, 2026. It is now read-only.

Commit 7730990

Browse files
Embed SDK Explorer in docs (#332)
* Embed SDK Explorer in docs - Add SDKExplorerIframe component with theme sync via postMessage - Add SDK-Explorer.mdx page with full-width iframe layout - Update redirect and nav URL to /SDK-Explorer - Fade-in iframe on load, prevent reload on theme change * Fixes * Add defensive path validation * Format * Move to static route * Fix nav and page gen * Update dark mode accent * Disable Fumadocs footer on Explorer page --------- Co-authored-by: Matthew Congrove <mattcongrove@gmail.com>
1 parent 77b7070 commit 7730990

8 files changed

Lines changed: 148 additions & 18 deletions

File tree

app/(docs)/[[...slug]]/page.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,33 @@ import { notFound } from 'next/navigation';
1111
import { CLICommand } from '@/components/CLICommand';
1212
import { CodeExample } from '@/components/CodeExample';
1313
import { Mermaid } from '@/components/Mermaid';
14+
import { SDKExplorerIframe } from '@/components/SDKExplorerIframe';
1415
import { Sparkle } from '@/components/Sparkle';
1516
import { Tag } from '@/components/Tag';
1617
import { ThemeImage } from '@/components/ThemeImage';
1718
import { TypingAnimation } from '@/components/TypingAnimation';
1819
import { XButton } from '@/components/XButton';
1920
import { source } from '@/lib/source';
21+
import CodeFromFiles from '../../../components/CodeFromFiles';
2022
import { CommunityButton } from '../../../components/Community';
2123
import CopyPageDropdown from '../../../components/CopyPageDropdown';
2224
import { NavButton } from '../../../components/NavButton';
23-
import CodeFromFiles from '../../../components/CodeFromFiles';
2425
import TutorialStep from '../../../components/TutorialStep';
2526

26-
2727
export default async function Page(props: {
2828
params: Promise<{ slug?: string[] }>;
2929
}) {
3030
const params = await props.params;
31-
const page = source.getPage(params.slug);
3231

32+
if (params.slug === undefined) {
33+
return (
34+
<DocsPage full footer={{ enabled: false }}>
35+
<SDKExplorerIframe />
36+
</DocsPage>
37+
);
38+
}
39+
40+
const page = source.getPage(params.slug);
3341
if (!page) notFound();
3442

3543
const MDX = page.data.body;
@@ -107,6 +115,14 @@ export async function generateMetadata(props: {
107115
params: Promise<{ slug?: string[] }>;
108116
}) {
109117
const params = await props.params;
118+
119+
if (params.slug === undefined) {
120+
return {
121+
title: 'SDK Explorer — Agentuity Docs',
122+
description: 'Interactive examples showcasing the Agentuity SDK',
123+
};
124+
}
125+
110126
const page = source.getPage(params.slug);
111127

112128
if (!page) notFound();

app/global.css

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@
3939
}
4040

4141
.dark {
42-
--color-fd-primary: var(--color-cyan-400);
42+
--color-fd-primary: var(--color-cyan-500);
4343
--color-fd-primary-foreground: var(--color-black);
44-
--color-fd-ring: var(--color-cyan-400);
44+
--color-fd-ring: var(--color-cyan-500);
4545
}
4646

4747
/* make sure empty lines are rendered */
@@ -140,3 +140,27 @@ article > p {
140140
.dark .prose a:not([data-card]) {
141141
text-decoration-color: var(--color-cyan-500);
142142
}
143+
144+
/* SDK Explorer - iframe rendered directly inside DocsLayout */
145+
.sdk-explorer-wrapper {
146+
position: fixed;
147+
inset: 0;
148+
top: var(--fd-nav-height, 0px);
149+
left: var(--fd-sidebar-width, 0px);
150+
z-index: 10;
151+
background: var(--color-fd-background);
152+
}
153+
154+
.sdk-explorer-wrapper iframe {
155+
width: 100%;
156+
height: 100%;
157+
border: 0;
158+
}
159+
160+
/* Mobile: full width, account for nav */
161+
@media (max-width: 767px) {
162+
.sdk-explorer-wrapper {
163+
left: 0;
164+
top: var(--fd-nav-height, 56px);
165+
}
166+
}

app/layout.config.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { XButton } from '../components/XButton';
88
*/
99
export const baseOptions: BaseLayoutProps = {
1010
nav: {
11-
url: '/Get-Started/what-is-agentuity',
11+
url: '/',
1212
title: (
1313
<div className="flex items-center gap-3 font-medium ml-2">
1414
<svg

components/SDKExplorerIframe.tsx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use client';
2+
3+
import { useTheme } from 'next-themes';
4+
import { useEffect, useRef, useState } from 'react';
5+
6+
const EXPLORER_URL = 'https://explorer.agentuity.dev';
7+
8+
export function SDKExplorerIframe() {
9+
const { resolvedTheme } = useTheme();
10+
const iframeRef = useRef<HTMLIFrameElement>(null);
11+
const [mounted, setMounted] = useState(false);
12+
const [iframeLoaded, setIframeLoaded] = useState(false);
13+
// Capture initial theme for iframe src (don't change src on theme updates)
14+
const [initialTheme] = useState(() =>
15+
typeof window !== 'undefined'
16+
? document.documentElement.classList.contains('dark')
17+
? 'dark'
18+
: 'light'
19+
: 'dark'
20+
);
21+
22+
// Only render iframe after mount to avoid hydration mismatch
23+
useEffect(() => {
24+
setMounted(true);
25+
}, []);
26+
27+
// Send theme changes to iframe via postMessage
28+
useEffect(() => {
29+
if (mounted && iframeRef.current?.contentWindow) {
30+
iframeRef.current.contentWindow.postMessage(
31+
{ type: 'SET_THEME', theme: resolvedTheme },
32+
EXPLORER_URL
33+
);
34+
}
35+
}, [resolvedTheme, mounted]);
36+
37+
// Listen for navigation requests from iframe
38+
useEffect(() => {
39+
const handleMessage = (event: MessageEvent) => {
40+
// Validate origin to prevent open redirect
41+
if (event.origin !== new URL(EXPLORER_URL).origin) {
42+
return;
43+
}
44+
if (event.data?.type === 'NAVIGATE' && event.data.path) {
45+
const path = event.data.path;
46+
// Only allow relative paths to prevent open redirects
47+
if (
48+
typeof path === 'string' &&
49+
path.startsWith('/') &&
50+
!path.startsWith('//')
51+
) {
52+
window.location.href = path;
53+
}
54+
}
55+
};
56+
window.addEventListener('message', handleMessage);
57+
return () => window.removeEventListener('message', handleMessage);
58+
}, []);
59+
60+
// Show placeholder until mounted to avoid hydration mismatch
61+
if (!mounted) {
62+
return <div className="size-full bg-fd-background" />;
63+
}
64+
65+
return (
66+
<div className="sdk-explorer-wrapper">
67+
<iframe
68+
ref={iframeRef}
69+
src={`${EXPLORER_URL}?theme=${initialTheme}`}
70+
title="SDK Explorer"
71+
allow="clipboard-read; clipboard-write"
72+
onLoad={() => setIframeLoaded(true)}
73+
className={`transition-opacity duration-300 ${iframeLoaded ? 'opacity-100' : 'opacity-0'}`}
74+
/>
75+
</div>
76+
);
77+
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Introduction
2+
title: What is Agentuity?
33
description: The full-stack platform for building, deploying, and operating AI agents
44
---
55

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

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

51-
| File | Purpose |
52-
|------|---------|
53-
| `agent.ts` | Agent logic with schema validation |
51+
| File | Purpose |
52+
| -------------- | ------------------------------------ |
53+
| `agent.ts` | Agent logic with schema validation |
5454
| `api/index.ts` | HTTP endpoints that call your agents |
5555

5656
Infrastructure like cron schedules is defined in route code, not config. Rolling back a deployment restores the exact configuration from that version.

content/meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"title": "Agentuity Docs",
33
"root": true,
44
"pages": [
5-
"index",
5+
"[SDK Explorer](/)",
66
"Get-Started",
77
"Agents",
88
"APIs",

next.config.mjs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,9 @@ const config = {
88
reactStrictMode: true,
99
serverExternalPackages: ['twoslash', 'typescript'],
1010
redirects: async () => [
11-
{
12-
source: '/Get-Started/what-is-agentuity',
13-
destination: '/',
14-
permanent: false,
15-
},
1611
{
1712
source: '/Introduction',
18-
destination: '/',
13+
destination: '/Get-Started/what-is-agentuity',
1914
permanent: true,
2015
},
2116
{

package-lock.json

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)