From d681a2694348696dd841612e45ed518e511e1af8 Mon Sep 17 00:00:00 2001 From: Max Barvian Date: Tue, 25 Aug 2026 16:00:38 -0700 Subject: [PATCH 01/11] fix: layout animation when closing banner --- app/components/AppShell.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/components/AppShell.tsx b/app/components/AppShell.tsx index b88f1e7..7c260c9 100644 --- a/app/components/AppShell.tsx +++ b/app/components/AppShell.tsx @@ -312,6 +312,10 @@ function NavRow({ icon, label, href, active, enabled, hasChildren, onNavigate, l {active && ( Date: Tue, 25 Aug 2026 16:13:27 -0700 Subject: [PATCH 02/11] animate banner close --- app/components/AppShell.tsx | 64 +++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/app/components/AppShell.tsx b/app/components/AppShell.tsx index 7c260c9..0c7738c 100644 --- a/app/components/AppShell.tsx +++ b/app/components/AppShell.tsx @@ -3,7 +3,7 @@ import { CSSProperties, MouseEvent as ReactMouseEvent, PropsWithChildren, useEffect, useRef, useState } from 'react'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; -import { AnimatePresence, motion } from 'motion/react'; +import { AnimatePresence, motion, useReducedMotion } from 'motion/react'; import { Toaster } from 'sonner'; import { getActiveParent, NAV_ITEMS, NavChild, NavIcon } from '../navigation'; @@ -660,34 +660,44 @@ type GlobalBannerProps = { }; function GlobalBanner({ dismissed, onDismiss, className }: GlobalBannerProps) { - if (dismissed) return null; + const reducedMotion = useReducedMotion(); + const transition = reducedMotion ? { duration: 0 } : slideTransition; + return ( -
+ {!dismissed && ( + +
+
+
+ New! + EIP-8130: Accounts + + + Test on Vibenet + + +
+
+ +
+
)} - > -
-
- New! - EIP-8130: Accounts - - - Test on Vibenet - - -
-
- -
+ ); } From a91d3d3c415135ecaccc0962cea749969f03f002 Mon Sep 17 00:00:00 2001 From: Max Barvian Date: Tue, 25 Aug 2026 16:19:43 -0700 Subject: [PATCH 03/11] navigation fixes --- app/components/AppShell.tsx | 73 +++++++++++----------------- app/navigation.test.ts | 95 +++++++++++++++++++++++++++++++++++++ app/navigation.ts | 42 ++++++++++++---- 3 files changed, 158 insertions(+), 52 deletions(-) create mode 100644 app/navigation.test.ts diff --git a/app/components/AppShell.tsx b/app/components/AppShell.tsx index 0c7738c..274d24a 100644 --- a/app/components/AppShell.tsx +++ b/app/components/AppShell.tsx @@ -6,12 +6,11 @@ import { usePathname } from 'next/navigation'; import { AnimatePresence, motion, useReducedMotion } from 'motion/react'; import { Toaster } from 'sonner'; -import { getActiveParent, NAV_ITEMS, NavChild, NavIcon } from '../navigation'; +import { getActiveParent, isChildActive, isTopNavActive, navActiveParent, navHighlightPath, NAV_ITEMS, NavIcon, titleForPath } from '../navigation'; import { BLUE, BORDER, BRAND_BLUE, DISABLED, INK, MUTED, SELECTED } from '../theme'; import { getChangeBySlug } from '../upgrades/data/changes'; import { demoLabel } from '../vibenet/demos/catalogue'; import { getUpgradeById } from '../upgrades/data/upgrades'; -import { titleForPath } from '../navigation'; import { trackNavClick } from '../analytics/events'; import { navSlideDirection } from './nav-motion'; @@ -369,11 +368,6 @@ function opensInNewTab(event: ReactMouseEvent): boolean { return event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0; } -function isChildActive(child: NavChild, pathname: string): boolean { - if (child.exact) return pathname === child.href; - return pathname === child.href || pathname.startsWith(`${child.href}/`); -} - const slideVariants = { enter: (direction: number) => ({ x: direction > 0 ? '60%' : '-60%', opacity: 0 }), center: { x: 0, opacity: 1 }, @@ -427,8 +421,8 @@ function SidebarContent({ dark, onToggleTheme, onNavigate, hideBrand, layoutScop // sub-nav slide sitting still for the whole navigation — the tap felt dead, then // everything moved at once. const [pendingPath, setPendingPath] = useState(null); - const activePath = pendingPath ?? pathname; - const activeParent = getActiveParent(activePath); + const activePath = navHighlightPath(pendingPath, pathname); + const activeParent = navActiveParent(pendingPath, pathname); const directionRef = useRef(1); const prevParentRef = useRef(activeParent?.href ?? null); const scrollViewportRef = useRef(null); @@ -451,7 +445,9 @@ function SidebarContent({ dark, onToggleTheme, onNavigate, hideBrand, layoutScop }, [pathname]); useEffect(() => { - if (!pendingPath) return; + // '/' is the back-header view: the route will not commit, so a timeout + // would snap the submenu back. Only time out real pending navigations. + if (!pendingPath || pendingPath === '/') return; const timer = setTimeout(() => setPendingPath(null), PENDING_PATH_TIMEOUT_MS); return () => clearTimeout(timer); }, [pendingPath]); @@ -465,7 +461,9 @@ function SidebarContent({ dark, onToggleTheme, onNavigate, hideBrand, layoutScop const selectPath = (href: string) => { setPendingPath(href); - onNavigate?.(); + // Keep the drawer open when the tap is the section root (Vibenet, + // Benchmark). getActiveParent already knows which hrefs have a submenu. + if (getActiveParent(href)?.href !== href) onNavigate?.(); }; const direction = directionRef.current; @@ -499,7 +497,10 @@ function SidebarContent({ dark, onToggleTheme, onNavigate, hideBrand, layoutScop style={{ ...styles.navLink, display: 'flex', alignItems: 'center', padding: '9px 6px 9px 2px', marginBottom: 4, color: 'var(--bds-gray-50)' }} onClick={(event) => { if (opensInNewTab(event)) return; - selectPath('/'); + // Stay on the current page and keep the mobile drawer + // open. href="/" remains for modified clicks (new tab). + event.preventDefault(); + setPendingPath('/'); }} >
)} @@ -726,10 +715,6 @@ export function AppShell({ children }: PropsWithChildren) { } }; - useEffect(() => { - setMenuOpen(false); - }, [pathname]); - useEffect(() => { if (!menuOpen) return; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setMenuOpen(false); }; @@ -750,7 +735,7 @@ export function AppShell({ children }: PropsWithChildren) { setBannerDismissed(true)} - className="hidden md:flex" + className="hidden md:block" />
{/* Desktop sidebar */} @@ -762,7 +747,7 @@ export function AppShell({ children }: PropsWithChildren) {
{/* Static on touch: the morph is hover-driven, so base.org leaves its mobile mark static too. */} - + setMenuOpen(false)}>
From 159e7ae8452c0a58127f669ee2ff2a020bf2c861 Mon Sep 17 00:00:00 2001 From: Max Barvian Date: Tue, 25 Aug 2026 17:04:14 -0700 Subject: [PATCH 07/11] 14px default button font --- app/components/ui/Text.tsx | 2 +- app/vibenet/demos/b20/components/DeployModule.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/ui/Text.tsx b/app/components/ui/Text.tsx index 4008135..984e767 100644 --- a/app/components/ui/Text.tsx +++ b/app/components/ui/Text.tsx @@ -54,7 +54,7 @@ export const textVariantClasses: Record = { 'text-[13px] md:text-[14px] leading-[18px] md:leading-[20px] font-mono font-[400] tracking-[0px]', caption: 'text-[11px] md:text-[12px] leading-[14px] md:leading-[16px] font-[500] tracking-[0px] uppercase', - button: 'text-[14px] md:text-[15px] leading-[20px] font-base font-[400] tracking-[-0.01em]', + button: 'text-[14px] leading-[20px] font-base font-[400] tracking-[-0.01em]', footnote: 'text-[11px] md:text-[12px] leading-[14px] md:leading-[16px] font-[400] tracking-[0px]', }; diff --git a/app/vibenet/demos/b20/components/DeployModule.tsx b/app/vibenet/demos/b20/components/DeployModule.tsx index 0463b47..3161eee 100644 --- a/app/vibenet/demos/b20/components/DeployModule.tsx +++ b/app/vibenet/demos/b20/components/DeployModule.tsx @@ -452,7 +452,7 @@ export function DeployModule({ placeholder="Paste an existing Policy ID" inputMode="numeric" /> - From 0c05a442e843b9da35d45dbbec3a83ece9df1fb9 Mon Sep 17 00:00:00 2001 From: Max Barvian Date: Tue, 25 Aug 2026 17:06:59 -0700 Subject: [PATCH 08/11] fix upgrades responsiveness --- app/upgrades/UpgradesClient.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/upgrades/UpgradesClient.tsx b/app/upgrades/UpgradesClient.tsx index bc53b8a..8a7bc46 100644 --- a/app/upgrades/UpgradesClient.tsx +++ b/app/upgrades/UpgradesClient.tsx @@ -262,7 +262,7 @@ function UpgradeView({ nowMs }: { nowMs: number }) { {upgrade.summary}
-
+
Sepolia From ce4a20a32c33a1b79bff9d758ff6732ef3a22c60 Mon Sep 17 00:00:00 2001 From: Max Barvian Date: Tue, 25 Aug 2026 17:09:49 -0700 Subject: [PATCH 09/11] copyablevalue spending acct --- app/vibenet/demos/account/AccountDemo.tsx | 2 -- app/vibenet/demos/account/components/AppsView.tsx | 15 ++------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/app/vibenet/demos/account/AccountDemo.tsx b/app/vibenet/demos/account/AccountDemo.tsx index 9b40653..3781b57 100644 --- a/app/vibenet/demos/account/AccountDemo.tsx +++ b/app/vibenet/demos/account/AccountDemo.tsx @@ -847,8 +847,6 @@ export function AccountDemo() { appBusy={appBusy} activeSigner={activeSigner} signers={signers} - copied={copied} - copy={copy} sessionKeyFor={sessionKeyFor} subAccountFor={subAccountFor} connectSessionApp={connectSessionApp} diff --git a/app/vibenet/demos/account/components/AppsView.tsx b/app/vibenet/demos/account/components/AppsView.tsx index d433b22..7d5ad3a 100644 --- a/app/vibenet/demos/account/components/AppsView.tsx +++ b/app/vibenet/demos/account/components/AppsView.tsx @@ -1,6 +1,7 @@ 'use client'; import { Button } from '../../../../components/ui/Button'; +import { CopyableValue } from '../../../components/CopyableValue'; import type { DemoApp } from '../library/apps'; import { formatExpiry, type AppSessionKey, type AppSubAccount, type StoredAccount } from '../library/model'; import { short, type WalletSigner } from '../shared'; @@ -35,8 +36,6 @@ type AppCardProps = { appBusy: string | null; activeSigner: WalletSigner | null; signers: WalletSigner[]; - copied: string | null; - copy: (text: string, k: string) => void; sessionKeyFor: (name: string) => AppSessionKey | undefined; subAccountFor: (name: string) => AppSubAccount | undefined; connectSessionApp: (app: DemoApp) => void; @@ -112,17 +111,7 @@ export function AppCard(p: AppCardProps) { connected = true; footer = ( <> - +
diff --git a/app/globals.css b/app/globals.css index ee8b182..de41cc0 100644 --- a/app/globals.css +++ b/app/globals.css @@ -811,11 +811,6 @@ button { padding-top: 56px; } - .activity-full-width { - width: 100vw; - margin-left: calc(-1 * (100vw - 100%) / 2); - } - /* iOS Safari auto-zooms the page when a focused input's font-size is < 16px, which shrinks the visual viewport and pushes fixed modals (and their close button) off-screen. Force >=16px on mobile so focusing a field never zooms. @@ -830,9 +825,16 @@ button { } } -@media (min-width: 768px) { - .activity-full-width { - width: calc(100vw - 248px); - margin-left: calc(-1 * (100vw - 248px - 100%) / 2); - } +/* Activity drawer: span the content scrollport without 100vw. 100vw includes + the scrollbar and is wider than the nested max-w-5xl column, so it created + a horizontal scrollbar. `cqw` is the .content-scroll column's client width. */ +.content-scroll { + container-type: inline-size; + container-name: content-scroll; +} + +.activity-full-width { + width: auto; + margin-left: calc(50% - 50cqw); + margin-right: calc(50% - 50cqw); } diff --git a/app/vibenet/demos/_components/AccountDemoShell.tsx b/app/vibenet/demos/_components/AccountDemoShell.tsx index 6a43983..00eb206 100644 --- a/app/vibenet/demos/_components/AccountDemoShell.tsx +++ b/app/vibenet/demos/_components/AccountDemoShell.tsx @@ -69,7 +69,10 @@ export function AccountDemoShell({ return ( <> -
+ {/* flex-1 fills the content column so the activity drawer can sit at the + bottom on short pages (`mt-auto`). A 100vh min-height overshot the + padded scrollport and left extra scroll below the drawer. */} +
{/* Desktop: the switcher lives in the app top bar. Hidden until an account exists so the gate reads as a clean full-page empty state. */} {hasAccounts && topbarSlot ? createPortal(switcher, topbarSlot) : null} diff --git a/app/vibenet/demos/_shared/ActivityDrawer.tsx b/app/vibenet/demos/_shared/ActivityDrawer.tsx index 2f86e25..fe79bfd 100644 --- a/app/vibenet/demos/_shared/ActivityDrawer.tsx +++ b/app/vibenet/demos/_shared/ActivityDrawer.tsx @@ -34,7 +34,7 @@ export function ActivityDrawer({ // `mt-auto` drops the bar to the bottom of the (full-height) flex column so // it rests at the viewport bottom on short pages; `sticky bottom-0` pins it // once content is tall enough to scroll. -
+