diff --git a/app/components/AppShell.tsx b/app/components/AppShell.tsx index 1bd197d..b2c293e 100644 --- a/app/components/AppShell.tsx +++ b/app/components/AppShell.tsx @@ -10,6 +10,7 @@ import { getActiveParent, NAV_ITEMS, NavChild, NavIcon } from '../navigation'; import { BLUE, BORDER, DISABLED, INK, MUTED, SELECTED } from '../theme'; import { spectrum } from '../spectrum'; import { getChangeBySlug } from '../upgrades/data/changes'; +import { demoLabel } from '../demos/catalogue'; import { getUpgradeById } from '../upgrades/data/upgrades'; import { titleForPath } from '../navigation'; @@ -508,7 +509,7 @@ function GlobalBanner({ dismissed, onDismiss, className }: GlobalBannerProps) { New! EIP-8130: Native Account Abstraction - + Test on Vibenet @@ -630,20 +631,20 @@ export function AppShell({ children }: PropsWithChildren) { /> ); } + const demoMatch = pathname.match(/^\/demos\/(.+)$/); + if (demoMatch) { + const slug = demoMatch[1].split('/')[0]; + return ( + + ); + } if (pathname.startsWith('/vibenet') && pathname !== '/vibenet') { let childLabel = title; let middle: { label: string; href: string } | undefined; - const demosMatch = pathname.match(/^\/vibenet\/demos(?:\/(.+))?$/); - if (demosMatch) { - if (!demosMatch[1]) { - childLabel = 'Demos'; - } else { - middle = { label: 'Demos', href: '/vibenet/demos' }; - const segments = demosMatch[1].split('/'); - const first = segments[0]; - childLabel = first.charAt(0).toUpperCase() + first.slice(1); - } - } const explorerDetailMatch = pathname.match(/^\/vibenet\/explorer\/(tx|block|address)\/(.+)$/); if (explorerDetailMatch) { middle = { label: 'Explorer', href: '/vibenet/explorer' }; diff --git a/app/vibenet/demos/_components/AnimatedAmount.tsx b/app/demos/_components/AnimatedAmount.tsx similarity index 97% rename from app/vibenet/demos/_components/AnimatedAmount.tsx rename to app/demos/_components/AnimatedAmount.tsx index 5880128..0fc5905 100644 --- a/app/vibenet/demos/_components/AnimatedAmount.tsx +++ b/app/demos/_components/AnimatedAmount.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { animate, motion, useMotionValue, useTransform } from 'motion/react'; -import { cn } from '../../../components/ui/cn'; +import { cn } from '../../components/ui/cn'; type AnimatedAmountProps = { text: string; diff --git a/app/vibenet/demos/_components/DemoHeader.tsx b/app/demos/_components/DemoHeader.tsx similarity index 81% rename from app/vibenet/demos/_components/DemoHeader.tsx rename to app/demos/_components/DemoHeader.tsx index 204bd06..ed892a4 100644 --- a/app/vibenet/demos/_components/DemoHeader.tsx +++ b/app/demos/_components/DemoHeader.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react'; -import { Text } from '../../../components/ui/Text'; +import { Text } from '../../components/ui/Text'; type DemoHeaderProps = { eyebrow?: string; @@ -10,7 +10,9 @@ type DemoHeaderProps = { }; export function DemoHeader({ - eyebrow = 'Base Vibenet · Demo', + // Network-neutral default: demos run on whichever test network carries the + // feature they show. A demo names its own network by passing `eyebrow`. + eyebrow = 'Base · Demo', title, description, actions, diff --git a/app/vibenet/demos/_components/DemoTabs.tsx b/app/demos/_components/DemoTabs.tsx similarity index 97% rename from app/vibenet/demos/_components/DemoTabs.tsx rename to app/demos/_components/DemoTabs.tsx index 1e81b0a..625bfcb 100644 --- a/app/vibenet/demos/_components/DemoTabs.tsx +++ b/app/demos/_components/DemoTabs.tsx @@ -2,7 +2,7 @@ import { motion } from 'motion/react'; -import { cn } from '../../../components/ui/cn'; +import { cn } from '../../components/ui/cn'; export type DemoTabItem = { value: string; diff --git a/app/vibenet/demos/_components/Stat.tsx b/app/demos/_components/Stat.tsx similarity index 100% rename from app/vibenet/demos/_components/Stat.tsx rename to app/demos/_components/Stat.tsx diff --git a/app/vibenet/demos/account/AccountDemo.tsx b/app/demos/account/AccountDemo.tsx similarity index 99% rename from app/vibenet/demos/account/AccountDemo.tsx rename to app/demos/account/AccountDemo.tsx index 5e17105..db1ae5a 100644 --- a/app/vibenet/demos/account/AccountDemo.tsx +++ b/app/demos/account/AccountDemo.tsx @@ -58,20 +58,19 @@ import Link from 'next/link'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { AnimatePresence, motion } from 'motion/react'; -import { Button } from '../../../components/ui/Button'; -import { Card } from '../../../components/ui/Card'; -import { cn } from '../../../components/ui/cn'; -import { AnimatedArrowIcon, CloseIcon } from '../../../components/ui/icons'; -import { Modal } from '../../../components/ui/Modal'; -import { Select } from '../../../components/ui/Select'; -import { Text } from '../../../components/ui/Text'; +import { Button } from '../../components/ui/Button'; +import { Card } from '../../components/ui/Card'; +import { cn } from '../../components/ui/cn'; +import { AnimatedArrowIcon, CloseIcon } from '../../components/ui/icons'; +import { Modal } from '../../components/ui/Modal'; +import { Select } from '../../components/ui/Select'; +import { Text } from '../../components/ui/Text'; import { toast } from 'sonner'; -import { vibenetApi } from '../../library/client'; -import { ACCOUNT_RPC_URL, VIBENET_EXPLORER_PATH } from '../../library/config'; +import { vibenetApi } from '../../vibenet/library/client'; +import { ACCOUNT_RPC_URL, VIBENET_EXPLORER_PATH } from '../../vibenet/library/config'; import { AnimatedAmount } from '../_components/AnimatedAmount'; -import { DemoHeader } from '../_components/DemoHeader'; -import { Spinner } from '../../../components/ui/Spinner'; -import { Tabs } from '../../../components/ui/Tabs'; +import { Spinner } from '../../components/ui/Spinner'; +import { Tabs } from '../../components/ui/Tabs'; import { Stat } from '../_components/Stat'; import { ActivityLog } from './components/ActivityLog'; import { AppsView } from './components/AppsView'; diff --git a/app/vibenet/demos/account/components/ActivityLog.tsx b/app/demos/account/components/ActivityLog.tsx similarity index 97% rename from app/vibenet/demos/account/components/ActivityLog.tsx rename to app/demos/account/components/ActivityLog.tsx index fa1e296..d94c7d7 100644 --- a/app/vibenet/demos/account/components/ActivityLog.tsx +++ b/app/demos/account/components/ActivityLog.tsx @@ -2,10 +2,10 @@ import Link from 'next/link'; import { useEffect, useRef, useState } from 'react'; import { motion, useReducedMotion } from 'motion/react'; -import { Button } from '../../../../components/ui/Button'; -import { cn } from '../../../../components/ui/cn'; -import { Text } from '../../../../components/ui/Text'; -import { VIBENET_EXPLORER_PATH } from '../../../library/config'; +import { Button } from '../../../components/ui/Button'; +import { cn } from '../../../components/ui/cn'; +import { Text } from '../../../components/ui/Text'; +import { VIBENET_EXPLORER_PATH } from '../../../vibenet/library/config'; import { type ActivityEntry, type StoredAccount, formatTime } from '../library/model'; import { short } from '../shared'; import { AccountIdentity, Badge } from './primitives'; diff --git a/app/vibenet/demos/account/components/AppsView.tsx b/app/demos/account/components/AppsView.tsx similarity index 97% rename from app/vibenet/demos/account/components/AppsView.tsx rename to app/demos/account/components/AppsView.tsx index 64864ce..212eea2 100644 --- a/app/vibenet/demos/account/components/AppsView.tsx +++ b/app/demos/account/components/AppsView.tsx @@ -1,9 +1,9 @@ 'use client'; -import { Button } from '../../../../components/ui/Button'; -import { Card } from '../../../../components/ui/Card'; -import { cn } from '../../../../components/ui/cn'; -import { Text } from '../../../../components/ui/Text'; +import { Button } from '../../../components/ui/Button'; +import { Card } from '../../../components/ui/Card'; +import { cn } from '../../../components/ui/cn'; +import { Text } from '../../../components/ui/Text'; import type { DemoApp } from '../library/apps'; import type { DemoChain } from '../library/chains'; import { formatExpiry, type AppSessionKey, type AppSubAccount, type StoredAccount } from '../library/model'; diff --git a/app/vibenet/demos/account/components/ConfigView.tsx b/app/demos/account/components/ConfigView.tsx similarity index 98% rename from app/vibenet/demos/account/components/ConfigView.tsx rename to app/demos/account/components/ConfigView.tsx index f7d155c..82fd205 100644 --- a/app/vibenet/demos/account/components/ConfigView.tsx +++ b/app/demos/account/components/ConfigView.tsx @@ -5,14 +5,14 @@ import type { Address, Hex } from '@aa'; import Link from 'next/link'; import { AnimatePresence, motion } from 'motion/react'; -import { Button } from '../../../../components/ui/Button'; -import { Card } from '../../../../components/ui/Card'; -import { cn } from '../../../../components/ui/cn'; -import { CloseIcon } from '../../../../components/ui/icons'; -import { Spinner } from '../../../../components/ui/Spinner'; -import { Select } from '../../../../components/ui/Select'; -import { Text } from '../../../../components/ui/Text'; -import { VIBENET_EXPLORER_PATH } from '../../../library/config'; +import { Button } from '../../../components/ui/Button'; +import { Card } from '../../../components/ui/Card'; +import { cn } from '../../../components/ui/cn'; +import { CloseIcon } from '../../../components/ui/icons'; +import { Spinner } from '../../../components/ui/Spinner'; +import { Select } from '../../../components/ui/Select'; +import { Text } from '../../../components/ui/Text'; +import { VIBENET_EXPLORER_PATH } from '../../../vibenet/library/config'; import { AnimatedAmount } from '../../_components/AnimatedAmount'; import { DEMO_CHAINS, diff --git a/app/vibenet/demos/account/components/primitives.tsx b/app/demos/account/components/primitives.tsx similarity index 98% rename from app/vibenet/demos/account/components/primitives.tsx rename to app/demos/account/components/primitives.tsx index 7dbc5d9..1d5c6ad 100644 --- a/app/vibenet/demos/account/components/primitives.tsx +++ b/app/demos/account/components/primitives.tsx @@ -1,8 +1,8 @@ import type { ReactNode } from 'react'; import { AnimatePresence, motion } from 'motion/react'; -import { cn } from '../../../../components/ui/cn'; -import { Text } from '../../../../components/ui/Text'; +import { cn } from '../../../components/ui/cn'; +import { Text } from '../../../components/ui/Text'; import type { SignerKind } from '../library/model'; import { KIND_LABEL, short } from '../shared'; diff --git a/app/demos/account/layout.tsx b/app/demos/account/layout.tsx new file mode 100644 index 0000000..01b9d9c --- /dev/null +++ b/app/demos/account/layout.tsx @@ -0,0 +1,30 @@ +import type { Metadata } from 'next'; +import type { ReactNode } from 'react'; + +import { MaintenanceBanner } from '../../vibenet/components/MaintenanceBanner'; + +// The account demo page is a client component; this passthrough layout carries +// its title/description. +// +// Title convention: a section root is "{Section} · Base Chain" and a page within +// a section is "{Page} · {Section}" — e.g. "Faucet · Vibenet". Spelled out in +// full rather than composed from a parent title.template, because the llms/agents +// generator resolves a template to its `default` and would publish the bare page +// title, disagreeing with what the browser renders. +export const metadata: Metadata = { + title: 'Native Account Abstraction · Demos', + description: + 'Create native account abstraction accounts from in-browser keys, fund them from the faucet, and inspect balances on Vibenet.', +}; + +// This demo runs on Vibenet, so it carries Vibenet's maintenance notice — the +// demo is unusable while the devnet is down. Scoped here rather than to the +// /demos section, since other demos may run on other test networks. +export default function AccountDemoLayout({ children }: { children: ReactNode }) { + return ( + <> + + {children} + + ); +} diff --git a/app/vibenet/demos/account/library/apps.ts b/app/demos/account/library/apps.ts similarity index 100% rename from app/vibenet/demos/account/library/apps.ts rename to app/demos/account/library/apps.ts diff --git a/app/vibenet/demos/account/library/calls.ts b/app/demos/account/library/calls.ts similarity index 100% rename from app/vibenet/demos/account/library/calls.ts rename to app/demos/account/library/calls.ts diff --git a/app/vibenet/demos/account/library/chains.ts b/app/demos/account/library/chains.ts similarity index 99% rename from app/vibenet/demos/account/library/chains.ts rename to app/demos/account/library/chains.ts index fcb60d9..411e4f9 100644 --- a/app/vibenet/demos/account/library/chains.ts +++ b/app/demos/account/library/chains.ts @@ -5,7 +5,7 @@ import { vibenetDevnetDeployment, } from "@aa"; -import { isAddress } from "../../../library/format"; +import { isAddress } from "../../../vibenet/library/format"; /** * Last-known-good vibenet EIP-8130 system contracts — a STATIC FALLBACK only. @@ -94,7 +94,7 @@ export function deploymentFromContracts( import { ACCOUNT_BUNDLER_URL, ACCOUNT_PAYER_URL, -} from "../../../library/config"; +} from "../../../vibenet/library/config"; /** * How EIP-8130 accounts execute on a given chain: diff --git a/app/vibenet/demos/account/library/model.ts b/app/demos/account/library/model.ts similarity index 100% rename from app/vibenet/demos/account/library/model.ts rename to app/demos/account/library/model.ts diff --git a/app/vibenet/demos/account/library/policy.ts b/app/demos/account/library/policy.ts similarity index 99% rename from app/vibenet/demos/account/library/policy.ts rename to app/demos/account/library/policy.ts index 728821e..3a520ba 100644 --- a/app/vibenet/demos/account/library/policy.ts +++ b/app/demos/account/library/policy.ts @@ -12,7 +12,7 @@ import { parseUnits, } from '@aa'; -import { vibenetApi } from '../../../library/client'; +import { vibenetApi } from '../../../vibenet/library/client'; import { BASE_SEPOLIA_USDC } from './chains'; import { SCOPE, scopeChips } from './model'; import { short } from '../shared'; diff --git a/app/vibenet/demos/account/library/templates.ts b/app/demos/account/library/templates.ts similarity index 100% rename from app/vibenet/demos/account/library/templates.ts rename to app/demos/account/library/templates.ts diff --git a/app/vibenet/demos/account/loading.tsx b/app/demos/account/loading.tsx similarity index 97% rename from app/vibenet/demos/account/loading.tsx rename to app/demos/account/loading.tsx index 7ab815f..4b358b7 100644 --- a/app/vibenet/demos/account/loading.tsx +++ b/app/demos/account/loading.tsx @@ -1,4 +1,4 @@ -import { Skeleton } from '../../../components/ui/Skeleton'; +import { Skeleton } from '../../components/ui/Skeleton'; export default function Loading() { return ( diff --git a/app/vibenet/demos/account/page.tsx b/app/demos/account/page.tsx similarity index 100% rename from app/vibenet/demos/account/page.tsx rename to app/demos/account/page.tsx diff --git a/app/vibenet/demos/account/shared.ts b/app/demos/account/shared.ts similarity index 95% rename from app/vibenet/demos/account/shared.ts rename to app/demos/account/shared.ts index 879c87f..a230abb 100644 --- a/app/vibenet/demos/account/shared.ts +++ b/app/demos/account/shared.ts @@ -2,7 +2,7 @@ import type { Address, Hex } from '@aa'; -import type { AccountBalancesResponse } from '../../library/api-types'; +import type { AccountBalancesResponse } from '../../vibenet/library/api-types'; import type { ActivityEntry, SignerKind, StoredAccount } from './library/model'; /** An in-browser signer key held in the demo wallet. */ diff --git a/app/demos/catalogue.test.ts b/app/demos/catalogue.test.ts new file mode 100644 index 0000000..60674c6 --- /dev/null +++ b/app/demos/catalogue.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest'; + +import { DEMOS, demoLabel } from './catalogue'; + +describe('demoLabel', () => { + it('uses the catalogue entry so the crumb matches the demo name', () => { + expect(demoLabel('account')).toBe('Account'); + }); + + it('prefers shortTitle over title when both are set', () => { + const account = DEMOS.find((d) => d.href === '/demos/account'); + expect(account?.title).toBe('Native Account Abstraction'); + expect(demoLabel('account')).toBe(account?.shortTitle); + }); + + it('title-cases a multi-word slug that has no catalogue entry', () => { + // The regression this guards: the previous implementation only uppercased + // the first character, rendering "smart-wallet" as "Smart-wallet". + expect(demoLabel('smart-wallet')).toBe('Smart Wallet'); + expect(demoLabel('paymaster-v2')).toBe('Paymaster V2'); + }); + + it('handles a single-word unknown slug', () => { + expect(demoLabel('bridge')).toBe('Bridge'); + }); + + it('does not emit stray spaces for awkward slugs', () => { + expect(demoLabel('a--b')).toBe('A B'); + expect(demoLabel('-lead')).toBe('Lead'); + }); +}); + +describe('DEMOS', () => { + it('gives every entry a /demos/ href, so demoLabel can resolve it', () => { + for (const demo of DEMOS) { + expect(demo.href.startsWith('/demos/')).toBe(true); + } + }); + + it('has no duplicate hrefs', () => { + const hrefs = DEMOS.map((d) => d.href); + expect(new Set(hrefs).size).toBe(hrefs.length); + }); +}); diff --git a/app/demos/catalogue.ts b/app/demos/catalogue.ts new file mode 100644 index 0000000..eeed0ad --- /dev/null +++ b/app/demos/catalogue.ts @@ -0,0 +1,58 @@ +// The demo catalogue — one place to register a demo. The index page renders +// cards from it and the breadcrumb resolves its label from it, so the two can't +// drift. Plain data with no server-only imports, so client components (AppShell) +// can read it too. + +export type DemoEntry = { + /** Route for the demo, e.g. `/demos/account`. */ + href: string; + /** Short tag shown beside the title on the card, usually the spec or network. */ + eyebrow: string; + /** + * Full name of the demo. Also the page title, which by convention reads + * `{title} · Demos` — set it in the demo's own layout metadata, since this + * catalogue is data and does not drive ``. + */ + title: string; + /** Compact label for the breadcrumb; falls back to `title` when unset. */ + shortTitle?: string; + summary: string; + points: string[]; + available: boolean; +}; + +export const DEMOS: DemoEntry[] = [ + { + href: '/demos/account', + eyebrow: 'EIP-8130', + title: 'Native Account Abstraction', + shortTitle: 'Account', + summary: + 'Create portable account-abstraction accounts from in-browser keys, fund them from the faucet, and inspect balances across networks.', + points: [ + 'Smart & EOA accounts — deterministic addresses', + 'K1 / P-256 / passkey signers', + 'Live balances on Vibenet + Base Sepolia', + ], + available: true, + }, +]; + +/** `smart-wallet` -> `Smart Wallet`. Fallback for a route with no catalogue entry. */ +function prettifySlug(slug: string): string { + return slug + .split('-') + .filter(Boolean) + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +} + +/** + * Breadcrumb label for the first path segment under /demos. Prefers the + * catalogue so the crumb matches the demo's real name, and degrades to a + * title-cased slug for anything not registered. + */ +export function demoLabel(slug: string): string { + const demo = DEMOS.find((entry) => entry.href === `/demos/${slug}`); + return demo?.shortTitle ?? demo?.title ?? prettifySlug(slug); +} diff --git a/app/demos/layout.tsx b/app/demos/layout.tsx new file mode 100644 index 0000000..d805fe2 --- /dev/null +++ b/app/demos/layout.tsx @@ -0,0 +1,18 @@ +import type { ReactNode } from 'react'; + +// Metadata lives on the routes themselves: app/demos/page.tsx for the index and +// app/demos/account/layout.tsx for the demo. + +type DemosLayoutProps = { + children: ReactNode; +}; + +// Section chrome only. Demos run on whichever test network carries the feature +// they show, so nothing network-specific belongs here — a demo that depends on +// a particular network owns that concern (see app/demos/account/layout.tsx, +// which carries Vibenet's maintenance notice). +export default function DemosLayout({ children }: DemosLayoutProps) { + return ( +
{children}
+ ); +} diff --git a/app/vibenet/demos/loading.tsx b/app/demos/loading.tsx similarity index 93% rename from app/vibenet/demos/loading.tsx rename to app/demos/loading.tsx index 543310e..ba7a398 100644 --- a/app/vibenet/demos/loading.tsx +++ b/app/demos/loading.tsx @@ -1,4 +1,4 @@ -import { Skeleton } from '../../components/ui/Skeleton'; +import { Skeleton } from '../components/ui/Skeleton'; export default function DemosLoading() { return ( diff --git a/app/vibenet/demos/page.tsx b/app/demos/page.tsx similarity index 65% rename from app/vibenet/demos/page.tsx rename to app/demos/page.tsx index 6acdc0d..f65db13 100644 --- a/app/vibenet/demos/page.tsx +++ b/app/demos/page.tsx @@ -1,42 +1,16 @@ import type { Metadata } from 'next'; -import { Card, LinkCard } from '../../components/ui/Card'; -import { Text } from '../../components/ui/Text'; +import { Card, LinkCard } from '../components/ui/Card'; +import { Text } from '../components/ui/Text'; import { Badge } from './account/components/primitives'; +import { DEMOS, type DemoEntry } from './catalogue'; export const metadata: Metadata = { - title: 'Demos · Vibenet', - description: 'Interactive demos of in-flight Base features running on the vibenet developer network.', + title: 'Demos · Base Chain', + description: + 'Interactive demos of in-flight Base features. Each demo runs on the test network that carries the feature it shows.', }; -type DemoEntry = { - href: string; - eyebrow: string; - title: string; - summary: string; - points: string[]; - available: boolean; -}; - -// Demo catalogue. The account demo is the first (and, for now, only live) -// entry; further demos are listed as upcoming so the surface reflects the -// roadmap — matching how the home/features surfaces handle staged work. -const DEMOS: DemoEntry[] = [ - { - href: '/vibenet/demos/account', - eyebrow: 'EIP-8130', - title: 'Native Account Abstraction', - summary: - 'Create portable account-abstraction accounts from in-browser keys, fund them from the faucet, and inspect balances across networks.', - points: [ - 'Smart & EOA accounts — deterministic addresses', - 'K1 / P-256 / passkey signers', - 'Live balances on Vibenet + Base Sepolia', - ], - available: true, - }, -]; - export default function DemosIndexPage() { return (
diff --git a/app/navigation.ts b/app/navigation.ts index 7d69ccc..d8256b8 100644 --- a/app/navigation.ts +++ b/app/navigation.ts @@ -26,11 +26,11 @@ export const NAV_ITEMS: NavItem[] = [ enabled: true, children: [ { label: 'Overview', href: '/vibenet', icon: 'overview', exact: true }, - { label: 'Demos', href: '/vibenet/demos', icon: 'demos' }, { label: 'Faucet', href: '/vibenet/faucet', icon: 'faucet' }, { label: 'Explorer', href: '/vibenet/explorer', icon: 'explorer' }, ], }, + { label: 'Demos', href: '/demos', icon: 'demos', enabled: true }, { label: 'Upgrades', href: '/upgrades', icon: 'upgrades', enabled: true }, { label: 'Changelog', href: '/upgrades/changelog', icon: 'changelog', enabled: true }, { label: 'Snapshots', href: '/snapshots', icon: 'snapshots', enabled: true }, diff --git a/app/sitemap.ts b/app/sitemap.ts index f8a35c7..1b8017e 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -22,8 +22,8 @@ export default function sitemap(): MetadataRoute.Sitemap { { path: '/vibenet', priority: 0.8, changeFrequency: 'weekly' }, { path: '/vibenet/explorer', priority: 0.6, changeFrequency: 'daily' }, { path: '/vibenet/faucet', priority: 0.5, changeFrequency: 'monthly' }, - { path: '/vibenet/demos', priority: 0.6, changeFrequency: 'weekly' }, - { path: '/vibenet/demos/account', priority: 0.5, changeFrequency: 'weekly' }, + { path: '/demos', priority: 0.6, changeFrequency: 'weekly' }, + { path: '/demos/account', priority: 0.5, changeFrequency: 'weekly' }, ]; return routes.map(({ path, priority, changeFrequency }) => ({ diff --git a/app/upgrades/data/vibenet.ts b/app/upgrades/data/vibenet.ts index 1d2e85e..2e42bae 100644 --- a/app/upgrades/data/vibenet.ts +++ b/app/upgrades/data/vibenet.ts @@ -21,7 +21,7 @@ const vibenetAvailability = [ timestamp: '2026-07-02', featured: true, image: '/images/upgrades/layered-squares.png', - demo: '/vibenet/demos/account' + demo: '/demos/account' }, ] as const satisfies VibenetAvailability[]; diff --git a/app/upgrades/upgrade/[fork]/page.tsx b/app/upgrades/upgrade/[fork]/page.tsx index 15820ce..cea7efb 100644 --- a/app/upgrades/upgrade/[fork]/page.tsx +++ b/app/upgrades/upgrade/[fork]/page.tsx @@ -26,13 +26,13 @@ export async function generateMetadata(props: UpgradePageProps): Promise