diff --git a/app/api/latest-blogs/route.ts b/app/api/latest-blogs/route.ts
new file mode 100644
index 00000000000..ed9a4005080
--- /dev/null
+++ b/app/api/latest-blogs/route.ts
@@ -0,0 +1,33 @@
+import { NextResponse } from 'next/server';
+import { blog } from '@/lib/source';
+
+export const dynamic = 'force-static';
+export const revalidate = 3600; // Revalidate every hour
+
+export async function GET() {
+ try {
+ const blogPages = [...blog.getPages()]
+ .sort(
+ (a, b) =>
+ new Date((b.data.date as string) ?? b.url).getTime() -
+ new Date((a.data.date as string) ?? a.url).getTime()
+ )
+ .slice(0, 2);
+
+ const latestBlogs = blogPages.map((page) => ({
+ title: page.data.title || 'Untitled',
+ description: page.data.description || '',
+ url: page.url,
+ date:
+ page.data.date instanceof Date
+ ? page.data.date.toISOString()
+ : (page.data.date as string) || '',
+ }));
+
+ return NextResponse.json(latestBlogs);
+ } catch (error) {
+ console.error('Error fetching latest blogs:', error);
+ return NextResponse.json([], { status: 500 });
+ }
+}
+
diff --git a/app/docs/[...slug]/page.tsx b/app/docs/[...slug]/page.tsx
index 91697dc10f5..edd926a5a28 100644
--- a/app/docs/[...slug]/page.tsx
+++ b/app/docs/[...slug]/page.tsx
@@ -5,6 +5,7 @@ import { BackToTop } from "@/components/ui/back-to-top";
import { Feedback } from "@/components/ui/feedback";
import { SidebarActions } from "@/components/ui/sidebar-actions";
import { CChainAPIPage, DataAPIPage, MetricsAPIPage, PChainAPIPage, XChainAPIPage } from "@/components/api/api-pages";
+import AddNetworkButtonInline from "@/components/client/AddNetworkButtonInline";
import { documentation } from "@/lib/source";
import { createMetadata } from "@/utils/metadata";
import { Popup, PopupContent, PopupTrigger } from "fumadocs-twoslash/ui";
@@ -112,6 +113,7 @@ export default async function Page(props: {
Steps,
YouTube,
Mermaid,
+ AddNetworkButtonInline,
TypeTable,
AutoTypeTable,
Accordion,
diff --git a/app/docs/docs-layout-wrapper.tsx b/app/docs/docs-layout-wrapper.tsx
index 96157dd2790..acc0342bd16 100644
--- a/app/docs/docs-layout-wrapper.tsx
+++ b/app/docs/docs-layout-wrapper.tsx
@@ -8,22 +8,13 @@ import { DocsSubNav } from '@/components/navigation/docs-subnav';
import { DocsNavbarToggle } from '@/components/navigation/docs-navbar-toggle';
import { ForceMobileSidebar } from '@/components/navigation/force-mobile-sidebar';
import { NavbarDropdownInjector } from '@/components/navigation/navbar-dropdown-injector';
-import {
- BookOpen,
- Code,
- Layers,
- Cable,
- Cpu,
- Server,
- Database,
- Activity,
- Network,
- Webhook,
- CircleDollarSign,
- Terminal,
- Package,
- Milestone
-} from 'lucide-react';
+import { useIsMobile } from '@/hooks/use-mobile';
+import {
+ documentationOptions,
+ apiReferenceOptions,
+ nodesOptions,
+ toolingOptions,
+} from '@/components/navigation/docs-nav-config';
interface DocsLayoutWrapperProps {
children: ReactNode;
@@ -43,8 +34,9 @@ export function DocsLayoutWrapper({
acpsTree,
}: DocsLayoutWrapperProps) {
const pathname = usePathname();
+ const isMobile = useIsMobile();
- // Add data attribute to body for CSS targeting and hide tabs dropdown
+ // Add data attribute to body for CSS targeting
useEffect(() => {
// Set layout attribute for scoping styles
if (pathname.startsWith('/docs')) {
@@ -59,175 +51,18 @@ export function DocsLayoutWrapper({
document.body.setAttribute('data-docs-section', 'acps');
} else if (pathname.startsWith('/docs/api-reference')) {
document.body.setAttribute('data-docs-section', 'api-reference');
- } else if (pathname.startsWith('/docs/rpcs')) {
- document.body.setAttribute('data-docs-section', 'rpcs');
+ } else if (pathname.startsWith('/docs/rpcs') || pathname.startsWith('/docs/nodes')) {
+ document.body.setAttribute('data-docs-section', 'nodes');
} else if (pathname.startsWith('/docs')) {
document.body.setAttribute('data-docs-section', 'documentation');
}
- // Force hide the tabs dropdown on ACPs pages only
- const hideDropdown = () => {
- const sidebar = document.querySelector('#nd-sidebar');
- if (!sidebar) return;
-
- const buttons = sidebar.querySelectorAll('button');
- const isAcps = pathname.startsWith('/docs/acps');
-
- buttons.forEach((button) => {
- const isTabsTrigger = button.getAttribute('aria-haspopup') || button.getAttribute('role') === 'combobox';
- if (!isTabsTrigger) return;
-
- const el = button as HTMLElement;
- if (isAcps) {
- // Hide on ACPs
- el.style.display = 'none';
- } else {
- // Ensure it is visible again when leaving ACPs section (fixes persistent hidden state on client nav)
- if (el.style.display === 'none') {
- el.style.display = '';
- }
- }
- });
- };
-
- // Run immediately and after a short delay to catch dynamically rendered elements
- hideDropdown();
- const timeout = setTimeout(hideDropdown, 100);
-
return () => {
- clearTimeout(timeout);
document.body.removeAttribute('data-docs-section');
document.body.removeAttribute('data-layout');
- // On unmount, best-effort restore any hidden tabs trigger
- const sidebar = document.querySelector('#nd-sidebar');
- if (sidebar) {
- sidebar.querySelectorAll('button').forEach((button) => {
- if (button.getAttribute('aria-haspopup') || button.getAttribute('role') === 'combobox') {
- (button as HTMLElement).style.display = '';
- }
- });
- }
};
}, [pathname]);
- // Documentation hamburger menu options
- const documentationOptions = [
- {
- title: 'Quick-start',
- description: 'Get started with Avalanche',
- icon:
,
- url: '/docs/quick-start',
- },
- {
- title: 'Build Apps',
- description: 'Build dApps on Avalanche',
- icon:
,
- url: '/docs/dapps',
- },
- {
- title: 'Avalanche L1s',
- description: 'Build your own blockchain',
- icon:
,
- url: '/docs/avalanche-l1s',
- },
- {
- title: 'Interop',
- description: 'Cross-chain messaging',
- icon:
,
- url: '/docs/cross-chain',
- },
- {
- title: 'Custom VMs',
- description: 'Build custom virtual machines',
- icon:
,
- url: '/docs/virtual-machines',
- },
- {
- title: 'Nodes and Validators',
- description: 'Run network infrastructure',
- icon:
,
- url: '/docs/nodes',
- },
- ];
-
- // API Reference hamburger menu options
- const apiReferenceOptions = [
- {
- title: 'Data API',
- description: 'Access blockchain data',
- icon:
,
- url: '/docs/api-reference/data-api',
- },
- {
- title: 'Metrics API',
- description: 'Network metrics and statistics',
- icon:
,
- url: '/docs/api-reference/metrics-api',
- },
- {
- title: 'Webhook API',
- description: 'Real-time blockchain notifications',
- icon:
,
- url: '/docs/api-reference/webhook-api',
- },
- ];
-
- // RPCs hamburger menu options
- const rpcsOptions = [
- {
- title: 'C-Chain RPC',
- description: 'Contract Chain RPC methods',
- icon:
,
- url: '/docs/rpcs/c-chain',
- },
- {
- title: 'P-Chain RPC',
- description: 'Platform Chain RPC methods',
- icon:
,
- url: '/docs/rpcs/p-chain',
- },
- {
- title: 'X-Chain RPC',
- description: 'Exchange Chain RPC methods',
- icon:
,
- url: '/docs/rpcs/x-chain',
- },
- {
- title: 'Subnet-EVM RPC',
- description: 'Subnet-EVM RPC methods',
- icon:
,
- url: '/docs/rpcs/subnet-evm',
- },
- {
- title: 'Other RPCs',
- description: 'Additional RPC APIs',
- icon:
,
- url: '/docs/rpcs/other',
- },
- ];
-
- // Tooling hamburger menu options
- const toolingOptions = [
- {
- title: 'Avalanche-SDK',
- description: 'Software development kit for Avalanche',
- icon:
,
- url: '/docs/tooling/avalanche-sdk',
- },
- {
- title: 'Avalanche-CLI',
- description: 'Command-line interface for Avalanche',
- icon:
,
- url: '/docs/tooling/avalanche-cli',
- },
- {
- title: "Postman Collection",
- description: 'Postman collection for Avalanche APIs',
- icon:
,
- url: '/docs/tooling/avalanche-postman',
- },
- ];
-
// Determine which section we're in and get the appropriate tree
let pageTree;
let sidebarOptions: any = {};
@@ -237,10 +72,10 @@ export function DocsLayoutWrapper({
sidebarOptions = {
tabs: apiReferenceOptions,
};
- } else if (pathname.startsWith('/docs/rpcs')) {
+ } else if (pathname.startsWith('/docs/rpcs') || pathname.startsWith('/docs/nodes')) {
pageTree = rpcsTree;
sidebarOptions = {
- tabs: rpcsOptions,
+ tabs: nodesOptions,
};
} else if (pathname.startsWith('/docs/tooling')) {
pageTree = toolingTree;
@@ -266,9 +101,9 @@ export function DocsLayoutWrapper({
enabled: false,
},
sidebar: {
- ...sidebarOptions,
+ tabs: isMobile ? sidebarOptions.tabs : false,
side: 'left', // Open sidebar from left on mobile
- },
+ } as any,
};
return (
@@ -352,7 +187,7 @@ export function DocsLayoutWrapper({
@@ -372,8 +207,7 @@ export function DocsLayoutWrapper({
{children}
-
+
>
);
}
-
diff --git a/app/docs/styles.css b/app/docs/styles.css
index 6488060fce6..974ba5e8884 100644
--- a/app/docs/styles.css
+++ b/app/docs/styles.css
@@ -359,6 +359,17 @@ body.search-open #docs-subnav {
display: none !important;
}
+/* Fix sidebar dropdown icon centering */
+body[data-layout="docs"] #nd-sidebar button[aria-haspopup] > div:first-child {
+ display: flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+}
+
+body[data-layout="docs"] #nd-sidebar button[aria-haspopup] > div:first-child svg {
+ transform: translateY(-5px) !important;
+}
+
/* Reduce sidebar horizontal padding and push down below subnavbar - DOCS ONLY */
body[data-layout="docs"] #nd-sidebar,
body[data-layout="docs"] [data-sidebar="sidebar"] {
diff --git a/app/global.css b/app/global.css
index 141664f874c..afc3594a2d8 100644
--- a/app/global.css
+++ b/app/global.css
@@ -21,6 +21,12 @@ div.group\/accordion h3 {
margin-top: 0 !important;
}
+/* Adjust login icon position in navbar */
+nav[aria-label="Main"] a[href="/login"] svg,
+header a[href="/login"] svg {
+ transform: translateY(4px);
+}
+
/* Make granite banner fixed instead of sticky */
[data-granite-banner] {
position: fixed !important;
diff --git a/app/layout-wrapper.client.tsx b/app/layout-wrapper.client.tsx
index 169a2d13b36..ff91040c110 100644
--- a/app/layout-wrapper.client.tsx
+++ b/app/layout-wrapper.client.tsx
@@ -4,6 +4,7 @@ import { HomeLayout } from 'fumadocs-ui/layouts/home';
import type { ReactNode } from 'react';
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
import { ActiveNavHighlighter } from '@/components/navigation/active-nav-highlighter';
+import { useDynamicBlogMenu } from '@/components/navigation/dynamic-blog-menu';
interface LayoutWrapperProps {
children: ReactNode;
@@ -11,10 +12,23 @@ interface LayoutWrapperProps {
}
export function LayoutWrapper({ children, baseOptions }: LayoutWrapperProps) {
+ const dynamicBlogMenu = useDynamicBlogMenu();
+
+ // Replace the blog menu with the dynamic one
+ const updatedOptions = {
+ ...baseOptions,
+ links: baseOptions.links?.map(link => {
+ if (link && typeof link === 'object' && 'text' in link && link.text === 'Blog') {
+ return dynamicBlogMenu;
+ }
+ return link;
+ }),
+ };
+
return (
<>
-
{children}
+
{children}
>
);
}
diff --git a/app/layout.config.tsx b/app/layout.config.tsx
index fe66d6d2a4d..8115aca1045 100644
--- a/app/layout.config.tsx
+++ b/app/layout.config.tsx
@@ -4,16 +4,14 @@ import { AvalancheLogo } from '@/components/navigation/avalanche-logo';
import {
Sprout,
Logs,
- MonitorCheck,
ArrowUpRight,
SendHorizontal,
- Cable,
Bot,
+ Computer,
Cpu,
Snowflake,
BriefcaseBusiness,
MessageSquareQuote,
- Github,
Hexagon,
Waypoints,
HandCoins,
@@ -26,8 +24,12 @@ import {
Ticket,
Earth,
ArrowLeftRight,
- DraftingCompass,
+ Triangle,
GraduationCap,
+ BookOpen,
+ Code,
+ GitBranch,
+ DraftingCompass,
} from 'lucide-react';
import Image from 'next/image';
import { UserButtonWrapper } from '@/components/login/user-button/UserButtonWrapper';
@@ -39,10 +41,10 @@ export const integrationsMenu: LinkItemType = {
items: [
{
icon:
,
- text: 'Account Abstraction',
+ text: 'Wallet SDKs',
description:
- 'Explore solutions for implementing account abstraction in your dApps.',
- url: '/integrations#Account%20Abstraction',
+ 'Explore solutions for implementing wallet SDKs in your dApps.',
+ url: '/integrations#Wallet%20SDKs',
menu: {
className: 'lg:col-start-1',
},
@@ -101,9 +103,28 @@ export const integrationsMenu: LinkItemType = {
};
export const blogMenu: LinkItemType = {
- type: 'main',
+ type: 'menu',
text: 'Blog',
url: '/guides',
+ items: [
+ {
+ icon:
,
+ text: 'Latest Articles',
+ description:
+ 'Read the latest guides, tutorials, and insights from the Avalanche ecosystem.',
+ url: '/guides',
+ },
+ {
+ icon:
,
+ text: 'Browse All Posts',
+ description:
+ 'Explore our complete collection of articles, guides, and community content.',
+ url: '/guides',
+ menu: {
+ className: 'lg:col-start-2',
+ },
+ },
+ ],
};
export const stats: LinkItemType = {
@@ -171,14 +192,14 @@ export const stats: LinkItemType = {
export const docsMenu: LinkItemType = {
type: 'menu',
text: 'Documentation',
- url: '/docs/quick-start',
+ url: '/docs/primary-network',
items: [
{
menu: {
banner: (
,
- text: 'Avalanche Protocol',
- description: 'Learn about the Avalanche Protocol',
- url: '/docs/quick-start',
+ text: 'Primary Network',
+ description: 'Connect to Avalanche and start building dApps',
+ url: '/docs/primary-network',
},
{
- icon:
,
- text: 'Avalanche L1s',
+ icon:
,
+ text: 'Node RPCs',
description:
- "Build your own sovereign Layer 1 blockchain using Avalanche's battle-tested infrastructure and tooling.",
- url: '/docs/avalanche-l1s',
+ "Explore the RPC Methods for the C-Chain, P-Chain, and X-Chain.",
+ url: '/docs/rpcs/c-chain',
menu: {
className: 'lg:col-start-2',
},
},
{
- icon:
,
- text: 'Nodes & Validators',
+ icon:
,
+ text: 'Data APIs',
description:
- 'Learn about hardware requirements, staking mechanisms, rewards, and best practices for running validator infra on Avalanche.',
- url: '/docs/nodes',
+ 'Explore the Data, Metrics, and Webhook APIs for the C-Chain, P-Chain, and X-Chain.',
+ url: '/docs/api-reference/data-api',
menu: {
className: 'lg:col-start-2',
},
},
{
- icon:
,
- text: 'Interoperability',
+ icon:
,
+ text: 'ACPs',
description:
- "Explore Avalanche's native cross-chain protocols that enable seamless asset and data transfer across different Avalanche L1s.",
- url: '/docs/cross-chain',
+ "Explore Avalanche's Community Proposals (ACPs) for network improvements and best practices.",
+ url: '/docs/acps',
menu: {
className: 'lg:col-start-3 lg:row-start-1',
},
},
{
- icon:
,
- text: 'Browse All Docs',
+ icon:
,
+ text: 'Developer Tools',
description:
- 'Explore our in-depth documentation, guides, and resources to bring your ideas to life.',
- url: '/docs/quick-start',
+ 'Explore the Avalanche SDKs, CLI, and more.',
+ url: '/docs/tooling',
menu: {
- className: 'lg:col-start-3',
+ className: 'lg:col-start-3 lg:row-start-2',
},
},
+ // {
+ // icon:
,
+ // text: 'Browse All Tools',
+ // description:
+ // 'Explore all available developer tools in the Avalanche ecosystem.',
+ // url: '/docs/tooling',
+ // },
],
};
@@ -492,7 +520,7 @@ export const eventsMenu: LinkItemType = {
banner: (
,
+ text: 'Community driven events',
+ description:
+ 'Check out and join the global meetups, workshops and events organized by Avalanche Team1',
+ url: 'https://lu.ma/Team1?utm_source=builder_hub',
+ },
+ {
icon:
,
text: 'Avalanche Calendar',
description:
'Explore upcoming Avalanche events, meetups, and community gatherings. Stay connected with the latest happenings in the ecosystem.',
url: 'https://lu.ma/calendar/cal-Igl2DB6quhzn7Z4',
+ menu: {
+ className: 'lg:col-start-3 lg:row-start-1',
+ },
},
{
+ icon:
,
+ text: 'Campus Connect',
+ description:
+ 'Discover opportunities for students and educators to explore blockchain technology and join our community of builders.',
+ url: '/university',
menu: {
- banner: (
-
-
-
- ),
- className: 'md:row-span-2',
+ className: 'lg:col-start-3 lg:row-start-2',
},
- icon:
,
- text: 'Community driven events',
- description:
- 'Check out and join the global meetups, workshops and events organized by Avalanche Team1',
- url: 'https://lu.ma/Team1?utm_source=builder_hub',
},
],
};
@@ -555,14 +579,13 @@ export const baseOptions: BaseLayoutProps = {
},
links: [
academyMenu,
- docsMenu,
+ blogMenu,
consoleMenu,
+ docsMenu,
eventsMenu,
grantsMenu,
- stats,
integrationsMenu,
- userMenu,
- blogMenu,
- universityMenu
+ stats,
+ userMenu
],
};
diff --git a/components/ai/search.tsx b/components/ai/search.tsx
index c9607fb2201..04899f94bf2 100644
--- a/components/ai/search.tsx
+++ b/components/ai/search.tsx
@@ -13,7 +13,7 @@ import {
useRef,
useState,
} from 'react';
-import { Loader2, RefreshCw, Send, X, User, Bot, Sparkles, StopCircle, HelpCircle, ChevronRight, Maximize2, Minimize2 } from 'lucide-react';
+import { Loader2, RefreshCw, Send, X, User, Bot, Sparkles, StopCircle, HelpCircle, ChevronRight, Maximize2, Minimize2, ArrowRight } from 'lucide-react';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import { cn } from '../../lib/cn';
import { buttonVariants } from '../ui/button';
@@ -100,15 +100,16 @@ function SearchAIInput(props: FormHTMLAttributes
) {