diff --git a/.gitignore b/.gitignore index 5d274798b63..bb3dbfd248e 100644 --- a/.gitignore +++ b/.gitignore @@ -144,6 +144,9 @@ package-lock.json + + + # Remote content output paths content/docs/acps/103-dynamic-fees.mdx content/docs/acps/108-evm-event-importing.mdx @@ -186,10 +189,10 @@ content/docs/cross-chain/teleporter/cli.mdx content/docs/cross-chain/teleporter/deep-dive.mdx content/docs/cross-chain/teleporter/overview.mdx content/docs/cross-chain/teleporter/upgradeability.mdx -content/docs/nodes/chain-configs/c-chain.mdx -content/docs/nodes/chain-configs/p-chain.mdx +content/docs/nodes/chain-configs/primary-network/c-chain.mdx +content/docs/nodes/chain-configs/primary-network/p-chain.mdx +content/docs/nodes/chain-configs/primary-network/x-chain.mdx content/docs/nodes/chain-configs/subnet-evm.mdx -content/docs/nodes/chain-configs/x-chain.mdx content/docs/nodes/configure/avalanche-l1-configs.mdx content/docs/nodes/configure/configs-flags.mdx content/docs/rpcs/c-chain/index.mdx diff --git a/app/(home)/docs/page.tsx b/app/(home)/docs/page.tsx index aaaec2c5cd4..e8c3b218cc0 100644 --- a/app/(home)/docs/page.tsx +++ b/app/(home)/docs/page.tsx @@ -1,5 +1,5 @@ import { redirect } from 'next/navigation'; export default function DocsPage() { - redirect('/docs/quick-start'); + redirect('/docs/dapps'); } diff --git a/app/(home)/page.tsx b/app/(home)/page.tsx index f98e0c9e958..9cefba2afa7 100644 --- a/app/(home)/page.tsx +++ b/app/(home)/page.tsx @@ -1,23 +1,16 @@ import Hero, { HeroBackground } from '@/components/landing/hero'; import Paths from '@/components/landing/paths'; import QuickLinks from '@/components/landing/quicklinks'; -import AcademySplash from '@/components/landing/academy-splash'; -import StudentCallout from '@/components/landing/student-callout'; export default function HomePage(): React.ReactElement { return ( <> -
- {/* */} - - - - - {/* */} - {/* */} -
+
+ + {/* */} +
); } \ No newline at end of file diff --git a/app/(home)/university/page.tsx b/app/(home)/university/page.tsx index ab7a58a53eb..b93e3080b14 100644 --- a/app/(home)/university/page.tsx +++ b/app/(home)/university/page.tsx @@ -21,6 +21,7 @@ import { import Link from "next/link"; import { HeroBackground } from "@/components/landing/hero"; import UniversitySlideshow from "@/components/university/UniversitySlideshow"; +import StudentCallout from '@/components/landing/student-callout'; interface ProgramCardProps { title: string; @@ -299,6 +300,7 @@ export default function Page() { + {/* CTA Section */}
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: (
Preview, - 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: (
Preview, + 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: ( -
- Preview -
- ), - 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) {
-
+
{ setInput(e.target.value); @@ -123,20 +124,20 @@ function SearchAIInput(props: FormHTMLAttributes) {
@@ -216,16 +217,6 @@ function Input(props: TextareaHTMLAttributes & { className? let processor: Processor | undefined; const map = new Map(); -const roleName: Record = { - user: 'You', - assistant: 'AI Assistant', -}; - -const roleIcon: Record = { - user: , - assistant: , -}; - function parseFollowUpQuestions(content: string): string[] { if (!content) return []; @@ -261,30 +252,23 @@ function SuggestedFollowUps({ questions, onQuestionClick }: { if (questions.length === 0) return null; return ( -
-

- Related topics you might find helpful: +

+

+ Suggested

-
+
{questions.map((question, index) => ( ))}
@@ -310,57 +294,67 @@ function Message({ message, isLast, onFollowUpClick, isStreaming, onToolReferenc const [detectedTools, setDetectedTools] = useState([]); const [hasAutoOpened, setHasAutoOpened] = useState(false); - return ( -
-
-
- {roleIcon[message.role] ?? } -
-
-

- {roleName[message.role] ?? 'Unknown'} -

-
- + if (isUser) { + // User message - right aligned + return ( +
+
+
+

{cleanContent}

+
+
+ ); + } - {/* Show all tools referenced */} - {!isUser && detectedTools.length > 0 && !isMobile && onToolReference && ( -
-

Tools referenced:

- {detectedTools.map((toolId) => ( - - ))} + // AI message - left aligned + return ( +
+
+
+ {/* AI */} +
+ {/*

AI Assistant

*/} +
+
- )} + + {/* Show all tools referenced */} + {detectedTools.length > 0 && !isMobile && onToolReference && ( +
+

Tools referenced:

+ {detectedTools.map((toolId) => ( + + ))} +
+ )} +
-
- {/* Show follow-up suggestions only for the last assistant message and not while streaming */} - {!isUser && isLast && !isStreaming && followUpQuestions.length > 0 && ( - - )} + {/* Show follow-up suggestions only for the last assistant message and not while streaming */} + {isLast && !isStreaming && followUpQuestions.length > 0 && ( + + )} +
); } @@ -515,7 +509,7 @@ export default function AISearch(props: DialogProps & { onToolSelect?: (toolId: "transition-all duration-300" )} > -
+
setViewMode('big')} /> @@ -534,11 +528,11 @@ export default function AISearch(props: DialogProps & { onToolSelect?: (toolId: aria-describedby={undefined} className={cn( "fixed inset-4 md:inset-auto md:left-1/2 md:top-1/2 md:-translate-x-1/2 md:-translate-y-1/2 z-50", - selectedTool && !isMobile ? "md:max-w-[1400px] md:w-[95vw]" : "md:max-w-2xl md:w-[90vw]", + selectedTool && !isMobile ? "md:max-w-[1600px] md:w-[95vw]" : "md:max-w-5xl md:w-[90vw]", "md:h-[85vh] max-h-[90vh] focus-visible:outline-none data-[state=closed]:animate-fd-fade-out data-[state=open]:animate-fd-fade-in transition-all duration-300" )} > -
+
{/* Desktop view - side by side */}
void }) { return (
-
- +
+ AI AI Assistant
@@ -610,19 +608,21 @@ function SmallViewContent({ onExpand }: { onExpand: () => void }) { {messages.length === 0 ? ( -
-
-
- +
+
+ AI +

How can I help?

+

+ Ask me anything about Avalanche +

+
-

How can I help?

-

- Ask me anything about Avalanche -

-
-
) : ( -
+
{messages.map((item, index) => ( void }) { /> ))} {status === 'streaming' && messages[messages.length - 1]?.role === 'user' && ( -
-
- -
-
-
- - - +
+
+
+ AI +

AI Assistant

+
+
+
+
+ + + +
+
@@ -703,8 +712,12 @@ function Content({ onToolReference, onCollapse }: { onToolReference?: (toolId: s return (
-
- +
+ AI AI Assistant
@@ -737,43 +750,38 @@ function Content({ onToolReference, onCollapse }: { onToolReference?: (toolId: s
-
- -
+ AI

How can I help you today?

Ask me anything about the documentation or get help with your code.

-
+
{suggestedQuestions.map((question, index) => ( ))}
) : ( -
+
{messages.map((item, index) => ( ))} {isLoading && messages[messages.length - 1]?.role === 'user' && ( -
-
- -
-
-
- - - +
+
+
+ AI +

AI Assistant

+
+
+
+
+ + + +
+
diff --git a/components/client/AddNetworkButtonInline.tsx b/components/client/AddNetworkButtonInline.tsx new file mode 100644 index 00000000000..8a95c7f3a65 --- /dev/null +++ b/components/client/AddNetworkButtonInline.tsx @@ -0,0 +1,101 @@ +'use client'; + +import { useState } from 'react'; +import { Wallet, Check, AlertCircle } from 'lucide-react'; + +interface NetworkConfig { + chainId: string; + chainName: string; + nativeCurrency: { + name: string; + symbol: string; + decimals: number; + }; + rpcUrls: string[]; + blockExplorerUrls: string[]; +} + +const AVALANCHE_MAINNET: NetworkConfig = { + chainId: '0xA86A', + chainName: 'Avalanche C-Chain', + nativeCurrency: { + name: 'Avalanche', + symbol: 'AVAX', + decimals: 18, + }, + rpcUrls: ['https://api.avax.network/ext/bc/C/rpc'], + blockExplorerUrls: ['https://subnets.avax.network/c-chain'], +}; + +const AVALANCHE_FUJI: NetworkConfig = { + chainId: '0xA869', + chainName: 'Avalanche Fuji Testnet', + nativeCurrency: { + name: 'Avalanche', + symbol: 'AVAX', + decimals: 18, + }, + rpcUrls: ['https://api.avax-test.network/ext/bc/C/rpc'], + blockExplorerUrls: ['https://subnets-test.avax.network/c-chain'], +}; + +interface AddNetworkButtonInlineProps { + network: 'mainnet' | 'fuji'; +} + +export default function AddNetworkButtonInline({ network }: AddNetworkButtonInlineProps) { + const [status, setStatus] = useState<'idle' | 'adding' | 'success' | 'error'>('idle'); + + const addNetwork = async () => { + if (typeof window === 'undefined' || !window.ethereum) { + setStatus('error'); + setTimeout(() => setStatus('idle'), 3000); + return; + } + + setStatus('adding'); + + const config = network === 'mainnet' ? AVALANCHE_MAINNET : AVALANCHE_FUJI; + + try { + await window.ethereum.request({ + method: 'wallet_addEthereumChain', + params: [config], + }); + setStatus('success'); + setTimeout(() => setStatus('idle'), 3000); + } catch (err: any) { + setStatus('error'); + setTimeout(() => setStatus('idle'), 3000); + } + }; + + return ( + + ); +} + diff --git a/components/landing/academy-splash.tsx b/components/landing/academy-splash.tsx index bff9356fe71..73661f906c6 100644 --- a/components/landing/academy-splash.tsx +++ b/components/landing/academy-splash.tsx @@ -1,174 +1,163 @@ "use client"; import React from "react"; -import { GraduationCap, ChevronRight, Code2, Sparkles, TrendingUp, Trophy, Flame, ArrowRight, BookOpen } from "lucide-react"; +import { Trophy, ArrowRight, BookOpen, Award, Zap } from "lucide-react"; import { cn } from "@/utils/cn"; import Link from "next/link"; import Image from "next/image"; +const features = [ + { + id: 1, + title: "Interactive Courses", + description: "Hands-on blockchain courses", + icon: BookOpen, + href: "/academy" + }, + { + id: 2, + title: "Hackathons", + description: "Compete for prizes", + icon: Trophy, + href: "/hackathons" + }, + { + id: 3, + title: "Bounties", + description: "Earn by contributing", + icon: Zap, + href: "/grants" + }, + { + id: 4, + title: "Certificates", + description: "Showcase your skills", + icon: Award, + href: "/login" + } +]; + export default function AcademySplash() { return ( -
-
-
- {/* Left side - Academy Info */} -
-
-
- - - Learn & Build - -
- -

- - Avalanche - - - Academy - -

- -

- Join thousands of developers learning blockchain through hands-on courses, - hackathons, and real bounties. Track your progress and earn rewards. -

-
- -
+
+
+

+ Avalanche Academy +

+
+ +
+
+ {/* Left - Description */} +
+

+ Learn. Build. Earn. +

+

+ Join thousands of developers learning blockchain through hands-on courses, + hackathons, and real bounties. Track your progress and earn rewards. +

+
Start Learning - + - - Join Hackathons + Create Account
- - {/* Right side - Wolfie Profile Card */} -
- {/* Main Profile Card */} + + {/* Right - Stats */} +
+
+
12+
+
Courses
+
+
+
5K+
+
Students
+
- {/* Header with Wolfie */} -
-
-
- Intern Wolfie -
-
- - 42 -
+
$50K+
+
In Prizes
+
+
+
100%
+
Free
+
+
+
+ + {/* Features Grid */} +
+ {features.map((feature) => ( + +
+
+
-

- Wolfie Hacks +

+ {feature.title}

-

- Level 15 • Master Builder +

+ {feature.description}

-
- - {/* Progress */} -
-
-
- - Academy Progress - - 78% -
-
-
-
+
+
- - {/* Stats */} -
-
-
12
-
Certificates
-
-
-
7
-
Bounties
-
-
-
3
-
Hackathons
-
-
- - {/* Current Focus */} - -
-
- -
-
-

- Currently Learning -

-

- Chainlink VRF with Avalanche ICM -

-
- -
- - - {/* CTA */} - - Create Account - - -
-
+ + ))}
diff --git a/components/landing/development.tsx b/components/landing/development.tsx index 9388716073f..a12d230c0a6 100644 --- a/components/landing/development.tsx +++ b/components/landing/development.tsx @@ -13,7 +13,7 @@ const development = [ description: "Find references for all Avalanche SDKs, APIs, and tools.", icon: Book, - href: "/docs/quick-start", + href: "/docs/dapps", }, { id: 5, diff --git a/components/landing/globe.tsx b/components/landing/globe.tsx index 157dd7c8b20..04f213b95fb 100644 --- a/components/landing/globe.tsx +++ b/components/landing/globe.tsx @@ -17,7 +17,7 @@ export const Sponsors = () => { { name: "Avalanche", image: "https://images.ctfassets.net/gcj8jwzm6086/5VHupNKwnDYJvqMENeV7iJ/3e4b8ff10b69bfa31e70080a4b142cd0/avalanche-avax-logo.svg", - link: "https://www.avax.network/", + link: "/docs/primary-network", type: "Primary" }, // Primary networks diff --git a/components/landing/hero.tsx b/components/landing/hero.tsx index 70a99f60cf8..f8ac53fcd9d 100644 --- a/components/landing/hero.tsx +++ b/components/landing/hero.tsx @@ -200,12 +200,11 @@ function RotatingText() { // Extract Background Component export function HeroBackground() { return ( -
+
{/* Premium Background */} -
+
{/* Subtle grid overlay */}
-
); @@ -232,38 +231,37 @@ export default function Hero() { -

+ {/*

Everything you need to go from idea to impact. -

+

*/}
{/* CTA Buttons */} -
+
Start Learning Build -
- - {/* AI Assistant */} -
- + +
+ +
diff --git a/components/landing/paths.tsx b/components/landing/paths.tsx index 683c463f6a9..fcd0005b872 100644 --- a/components/landing/paths.tsx +++ b/components/landing/paths.tsx @@ -53,49 +53,46 @@ const paths = [ export default function Paths() { return (
-
-

+
+

Choose your path

-
+
{paths.map((path, index) => ( -
+
{/* Icon */} -
-
- -
+
+
{/* Content */}
-

+

{path.title}

-

+

{path.description}

{/* Arrow */} -
- +
+
diff --git a/components/landing/quicklinks.tsx b/components/landing/quicklinks.tsx index 405f4459627..1c061843796 100644 --- a/components/landing/quicklinks.tsx +++ b/components/landing/quicklinks.tsx @@ -1,13 +1,17 @@ "use client"; -import React from "react"; import { Droplet, Wrench, - Search, BookOpen, ArrowRight, - ArrowLeftRight + Computer, + ArrowLeftRight, + GitBranch, + ActivityIcon, + PackageIcon, + CodeIcon, + Triangle } from "lucide-react"; import { cn } from "@/utils/cn"; import Link from "next/link"; @@ -22,80 +26,112 @@ const quickLinks = [ }, { id: 2, - title: "Bridge", - description: "Bridge assets to and from the C-Chain", - icon: ArrowLeftRight, - href: "https://core.app/en/bridge/" - }, - { - id: 3, title: "Create New L1", - description: "Use our Builder Console to create a new L1", + description: "Create a blockchain with the Builder Console", icon: Wrench, href: "/console/layer-1/create" }, + { + id: 3, + title: "Setup a Node", + description: "Run a node on your own hardware or cloud provider.", + icon: Computer, + href: "/docs/nodes/run-a-node/using-docker" + }, { id: 4, - title: "Explorer", - description: "Learn from zero to hero", - icon: Search, - href: "https://subnets.avax.network" + title: "RPC References", + description: "Explore the RPC Methods for the C-Chain, P-Chain, and X-Chain.", + icon: ArrowLeftRight, + href: "/docs/rpcs/c-chain" }, { id: 5, title: "API References", - description: "Avalanche APIs", + description: "Avalanche Data, Metrics, and Webhook APIs", icon: BookOpen, href: "/docs/api-reference" - } + }, + { + id: 6, + title: "Avalanche Fundamentals", + description: "Learn about the basics of Avalanche.", + icon: Triangle, + href: "/academy/avalanche-fundamentals" + }, + { + id: 7, + title: "Network Stats", + description: "View the latest metrics for the Avalanche Network.", + icon: ActivityIcon, + href: "/stats/overview" + }, + { + id: 8, + title: "ACPs", + description: "Explore Avalanche's Community Proposals (ACPs) for network improvements and best practices.", + icon: GitBranch, + href: "/docs/acps" + }, + { + id: 9, + title: "Integrations", + description: "Explore the integrations with Avalanche.", + icon: PackageIcon, + href: "/integrations" + }, + { + id: 10, + title: "Developer Tools", + description: "Explore the developer tools for Avalanche.", + icon: CodeIcon, + href: "/docs/tooling" + }, ]; export default function QuickLinks() { return (
-
-

+
+

Quick Links

-
+
{quickLinks.map((link, index) => ( -
+
{/* Icon */} -
-
- -
+
+
{/* Content */}
-

+

{link.title}

-

+

{link.description}

{/* Arrow */} -
- +
+
diff --git a/components/navigation/active-nav-highlighter.tsx b/components/navigation/active-nav-highlighter.tsx index 7e57b69a1da..2bbb10cad12 100644 --- a/components/navigation/active-nav-highlighter.tsx +++ b/components/navigation/active-nav-highlighter.tsx @@ -45,8 +45,8 @@ export function ActiveNavHighlighter() { if (href) { // Check if this link's href matches or starts with the active section if (href === activeSection || href.startsWith(activeSection + '/')) { - // Special handling for docs (which might have url '/docs/quick-start') - if (activeSection === '/docs' && (href === '/docs/quick-start' || href.startsWith('/docs/'))) { + // Special handling for docs (which might have url '/docs/dapps') + if (activeSection === '/docs' && (href === '/docs/dapps' || href.startsWith('/docs/'))) { link.setAttribute('data-active', 'true'); link.setAttribute('aria-current', 'page'); } diff --git a/components/navigation/docs-nav-config.tsx b/components/navigation/docs-nav-config.tsx new file mode 100644 index 00000000000..c5df003dc77 --- /dev/null +++ b/components/navigation/docs-nav-config.tsx @@ -0,0 +1,146 @@ +import { + Network, + Layers, + Cable, + Server, + Database, + Activity, + Webhook, + Code, + CircleDollarSign, + Package, + Terminal, + Milestone, + Book, + Eye +} from 'lucide-react'; + +export const documentationOptions = [ + { + title: 'Primary Network', + description: 'C-Chain, P-Chain, and X-Chain', + icon: , + url: '/docs/primary-network', + }, + { + title: 'Layer 1s', + description: 'Build your own Avalanche blockchain', + icon: , + url: '/docs/avalanche-l1s', + }, + { + title: 'Interchain Messaging', + description: 'Interchain messaging protocol', + icon: , + url: '/docs/cross-chain', + }, +]; + +export const nodesOptions = [ + { + title: 'AvalancheGo Node', + description: 'Run nodes and validators', + icon: , + url: '/docs/nodes', + }, + { + title: 'C-Chain RPC', + description: 'Contract Chain RPC reference', + icon: , + url: '/docs/rpcs/c-chain', + }, + { + title: 'P-Chain RPC', + description: 'Platform Chain RPC reference', + icon: , + url: '/docs/rpcs/p-chain', + }, + { + title: 'X-Chain RPC', + description: 'Exchange Chain RPC reference', + icon: , + url: '/docs/rpcs/x-chain', + }, + { + title: 'Subnet-EVM RPC', + description: 'Subnet-EVM RPC reference', + icon: , + url: '/docs/rpcs/subnet-evm', + }, + { + title: 'Other RPCs', + description: 'Additional RPC references', + icon: , + url: '/docs/rpcs/other', + }, +]; + +export 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', + }, +]; + +export 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', + }, +]; + + +export const acpsOptions = [ + { + title: 'Streaming Asynchronous Execution', + description: 'ACP-194', + icon: , + url: '/docs/acps/194-streaming-asynchronous-execution', + }, + { + title: 'Continuous Staking', + description: 'ACP-236', + icon: , + url: '/docs/acps/236-continuous-staking', + }, + { + title: 'ValidatorManager Contract', + description: 'ACP-99', + icon: , + url: '/docs/acps/99-validatorsetmanager-contract', + }, + { + title: "View All ACPs", + description: 'View all Avalanche Community Proposals', + icon: , + url: '/docs/acps', + } +]; \ No newline at end of file diff --git a/components/navigation/docs-subnav.tsx b/components/navigation/docs-subnav.tsx index 931f47c8cfb..1780a067142 100644 --- a/components/navigation/docs-subnav.tsx +++ b/components/navigation/docs-subnav.tsx @@ -3,67 +3,140 @@ import Link from "next/link"; import { usePathname } from "next/navigation"; import { cn } from "@/lib/cn"; +import { ChevronDown } from "lucide-react"; +import { + HoverCard, + HoverCardContent, + HoverCardTrigger, +} from "@/components/ui/hover-card"; +import { useIsMobile } from "@/hooks/use-mobile"; +import { + documentationOptions, + apiReferenceOptions, + nodesOptions, + toolingOptions, + acpsOptions, +} from "./docs-nav-config"; const tabs = [ { - label: "Overview", - href: "/docs/quick-start", + label: "Network", + href: "/docs/primary-network", + items: documentationOptions, pathMatch: (path: string) => - path === "/docs/quick-start" || + path === "/docs/primary-network" || + path === "/docs" || (path.startsWith("/docs/") && !path.startsWith("/docs/api-reference") && !path.startsWith("/docs/rpcs") && + !path.startsWith("/docs/nodes") && !path.startsWith("/docs/tooling") && !path.startsWith("/docs/acps")), }, { - label: "Node RPCs", - href: "/docs/rpcs/c-chain", - pathMatch: (path: string) => path.startsWith("/docs/rpcs"), + label: "Nodes", + href: "/docs/nodes", + items: nodesOptions, + pathMatch: (path: string) => path.startsWith("/docs/rpcs") || path.startsWith("/docs/nodes"), }, { - label: "Data APIs", + label: "APIs", href: "/docs/api-reference/data-api", + items: apiReferenceOptions, pathMatch: (path: string) => path.startsWith("/docs/api-reference"), }, { - label: "Developer Tools", + label: "Tools", href: "/docs/tooling/avalanche-sdk", + items: toolingOptions, pathMatch: (path: string) => path.startsWith("/docs/tooling"), }, { label: "ACPs", href: "/docs/acps", + items: acpsOptions, pathMatch: (path: string) => path.startsWith("/docs/acps"), }, ]; export function DocsSubNav() { const pathname = usePathname(); + const isMobile = useIsMobile(); return (
-
+
{tabs.map((tab) => { const isActive = tab.pathMatch(pathname); + const hasItems = tab.items && tab.items.length > 0; - return ( + // Only show chevron if has items AND NOT mobile + const showChevron = hasItems && !isMobile; + + const LinkContent = ( + + {tab.label} + {showChevron && ( + + )} + + ); + + const LinkElement = ( - {tab.label} + {LinkContent} ); + + // If mobile, or no items, just render the link without hover card + if (hasItems && !isMobile) { + return ( + + + {LinkElement} + + +
+ {tab.items?.map((item) => ( + +
+ {item.icon} +
+
+

+ {item.title} +

+

+ {item.description} +

+
+ + ))} +
+
+
+ ); + } + + return LinkElement; })}
diff --git a/components/navigation/dynamic-blog-menu.tsx b/components/navigation/dynamic-blog-menu.tsx new file mode 100644 index 00000000000..af6aacee517 --- /dev/null +++ b/components/navigation/dynamic-blog-menu.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { useEffect, useState } from 'react'; +import { type LinkItemType } from 'fumadocs-ui/layouts/docs'; +import { BookOpen, FileText, ArrowUpRight } from 'lucide-react'; +import Image from 'next/image'; + +interface BlogPost { + title: string; + description: string; + url: string; + date: string; +} + +export function useDynamicBlogMenu(): LinkItemType { + const [latestBlogs, setLatestBlogs] = useState([]); + + useEffect(() => { + fetch('/api/latest-blogs') + .then(res => res.json()) + .then(data => setLatestBlogs(data)) + .catch(err => console.error('Failed to fetch latest blogs:', err)); + }, []); + + const blogItems: any[] = []; + + // Add dynamic blog posts + if (latestBlogs.length > 0) { + latestBlogs.forEach((post, index) => { + blogItems.push({ + icon: , + text: post.title, + description: post.description.length > 100 + ? post.description.substring(0, 100) + '...' + : post.description, + url: post.url, + } as any); + }); + } + + // Add "Browse All" link + blogItems.push({ + icon: , + text: 'Browse All Posts', + description: + 'Explore our complete collection of articles, guides, and community content.', + url: '/guides', + } as any); + + return { + type: 'menu', + text: 'Blog', + url: '/guides', + items: blogItems, + }; +} + diff --git a/components/navigation/footer.tsx b/components/navigation/footer.tsx index 0a50fe74933..12c72a5c020 100644 --- a/components/navigation/footer.tsx +++ b/components/navigation/footer.tsx @@ -39,64 +39,58 @@ export function Footer() { }; return ( -