From d0767cd76ea7c814a8ba58d3e4d27ed0fe3d6018 Mon Sep 17 00:00:00 2001 From: Saxon Fletcher Date: Fri, 11 Sep 2026 11:29:59 +1000 Subject: [PATCH] refine --- apps/ui-library/components/command-menu.tsx | 8 +- .../ui-library/components/copy-doc-prompt.tsx | 2 +- .../ui-library/components/install-command.tsx | 5 +- .../components/installation-panel.tsx | 5 +- apps/ui-library/components/site-header.tsx | 230 ++++++++---------- apps/ui-library/components/terminal-code.tsx | 21 ++ .../components/theme-switcher-dropdown.tsx | 2 +- apps/ui-library/hooks/use-menu-aim.ts | 59 +++++ 8 files changed, 194 insertions(+), 138 deletions(-) create mode 100644 apps/ui-library/components/terminal-code.tsx create mode 100644 apps/ui-library/hooks/use-menu-aim.ts diff --git a/apps/ui-library/components/command-menu.tsx b/apps/ui-library/components/command-menu.tsx index ce2a92d089f..89be8d41180 100644 --- a/apps/ui-library/components/command-menu.tsx +++ b/apps/ui-library/components/command-menu.tsx @@ -1,6 +1,6 @@ 'use client' -import { CircleIcon, LaptopIcon, MoonIcon, SunIcon } from 'lucide-react' +import { LaptopIcon, MoonIcon, SunIcon } from 'lucide-react' import { useTheme } from 'next-themes' import { useRouter } from 'next/navigation' import * as React from 'react' @@ -79,7 +79,7 @@ export function CommandMenu({ ...props }: DialogProps) { Find guides, blocks, starter apps, and theme settings. - + No results found. {gettingStarted.items.map((navItem) => ( @@ -88,9 +88,6 @@ export function CommandMenu({ ...props }: DialogProps) { value={navItem.title} onSelect={() => runCommand(() => router.push(navItem.href as string))} > -
- -
{navItem.title} ))} @@ -111,7 +108,6 @@ export function CommandMenu({ ...props }: DialogProps) { }) } > - {block.title} ))} diff --git a/apps/ui-library/components/copy-doc-prompt.tsx b/apps/ui-library/components/copy-doc-prompt.tsx index a09289c7a75..cb7ac9de5eb 100644 --- a/apps/ui-library/components/copy-doc-prompt.tsx +++ b/apps/ui-library/components/copy-doc-prompt.tsx @@ -46,7 +46,7 @@ export function CopyDocPrompt({ title, pagePath, command }: CopyDocPromptProps) } onClick={copyPrompt} > - {status === 'copied' ? 'Prompt copied' : 'Copy prompt'} + {status === 'copied' ? 'Prompt copied' : 'Copy agent prompt'} -
-        {command}
-      
+ ) diff --git a/apps/ui-library/components/installation-panel.tsx b/apps/ui-library/components/installation-panel.tsx index 019bae1167f..f12153b54b1 100644 --- a/apps/ui-library/components/installation-panel.tsx +++ b/apps/ui-library/components/installation-panel.tsx @@ -4,6 +4,7 @@ import { Check, Copy } from 'lucide-react' import { useEffect, useState } from 'react' import { Tabs, TabsContent, TabsList, TabsTrigger } from 'ui' +import { TerminalCode } from './terminal-code' import type { PackageManager } from '@/lib/install-command' interface InstallationPanelProps { @@ -72,9 +73,7 @@ export function InstallationPanel({ title, pagePath, commands }: InstallationPan {packageManagers.map((manager) => ( -
-              {commands[manager]}
-            
+
))} diff --git a/apps/ui-library/components/site-header.tsx b/apps/ui-library/components/site-header.tsx index 5854dbfa708..c8dd55ef0eb 100644 --- a/apps/ui-library/components/site-header.tsx +++ b/apps/ui-library/components/site-header.tsx @@ -1,10 +1,9 @@ 'use client' import supabaseLogoIcon from 'common/assets/images/supabase-logo-icon.svg' -import { ArrowUpRight, ChevronRight, Code2 } from 'lucide-react' +import { ArrowUpRight, ChevronRight, Code2, Grid2X2 } from 'lucide-react' import Image from 'next/image' import Link from 'next/link' -import { usePathname } from 'next/navigation' import { useState } from 'react' import { Button, @@ -16,7 +15,6 @@ import { NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, - navigationMenuTriggerStyle, Tabs, TabsContent, TabsList, @@ -28,21 +26,23 @@ import { Sidebar } from '@/components/sidebar' import { ThemeSwitcherDropdown } from '@/components/theme-switcher-dropdown' import { getLibraryBlockHref, libraryBlocks, libraryCategories } from '@/config/library' import { useFramework } from '@/context/framework-context' +import { useMenuAim } from '@/hooks/use-menu-aim' export function SiteHeader() { - const pathname = usePathname() const { framework } = useFramework() const [activeCategory, setActiveCategory] = useState(libraryCategories[0].name) + const menuAim = useMenuAim(activeCategory, setActiveCategory) + return ( -
+
Skip to content -
+
-
+ + + + {block.description} + + + + + ))} + + ) : ( +

+ No blocks in this category yet. +

+ )} + + ) + })} + + + + + +
@@ -197,7 +179,7 @@ export function SiteHeader() { asChild variant="text" size="tiny" - className="hidden px-2 sm:inline-flex" + className="hidden h-8 w-8 p-0 sm:inline-flex" aria-label="View source on GitHub" > @@ -208,7 +190,7 @@ export function SiteHeader() { asChild variant="text" size="small" - className="hidden px-3 text-sm sm:inline-flex" + className="hidden h-8 px-3 text-sm sm:inline-flex" iconRight={} > Docs diff --git a/apps/ui-library/components/terminal-code.tsx b/apps/ui-library/components/terminal-code.tsx new file mode 100644 index 00000000000..92f83490131 --- /dev/null +++ b/apps/ui-library/components/terminal-code.tsx @@ -0,0 +1,21 @@ +'use client' + +import dynamic from 'next/dynamic' + +const CodeBlock = dynamic(() => import('ui-patterns/CodeBlock').then((module) => module.CodeBlock)) + +export function TerminalCode({ command }: { command: string }) { + return ( + + {command} + + ) +} diff --git a/apps/ui-library/components/theme-switcher-dropdown.tsx b/apps/ui-library/components/theme-switcher-dropdown.tsx index c53583afbd1..882b4e5e765 100644 --- a/apps/ui-library/components/theme-switcher-dropdown.tsx +++ b/apps/ui-library/components/theme-switcher-dropdown.tsx @@ -58,7 +58,7 @@ const ThemeSwitcherDropdown = () => { variant="text" size="tiny" aria-label="Choose a theme" - className="px-1 group" + className="group h-8 w-8 p-0" icon={ resolvedTheme?.includes('light') ? ( diff --git a/apps/ui-library/hooks/use-menu-aim.ts b/apps/ui-library/hooks/use-menu-aim.ts new file mode 100644 index 00000000000..49259b6e26e --- /dev/null +++ b/apps/ui-library/hooks/use-menu-aim.ts @@ -0,0 +1,59 @@ +'use client' + +import { useEffect, useRef, type PointerEvent } from 'react' + +// Keep the selected category while the pointer travels toward its content panel. +export function useMenuAim(activeCategory: string, onCategoryChange: (category: string) => void) { + const previousPoint = useRef<{ x: number; y: number } | null>(null) + const timeout = useRef | null>(null) + + function cancelPending() { + if (timeout.current !== null) clearTimeout(timeout.current) + timeout.current = null + } + + function reset() { + cancelPending() + previousPoint.current = null + } + + useEffect(() => { + return () => { + if (timeout.current !== null) clearTimeout(timeout.current) + } + }, []) + + function onPointerMove(event: PointerEvent) { + if (event.pointerType !== 'mouse') return + + const previous = previousPoint.current + const point = { x: event.clientX, y: event.clientY } + previousPoint.current = point + const target = event.target instanceof Element ? event.target : null + const tab = target?.closest('[data-menu-category]') + const category = tab?.dataset.menuCategory + cancelPending() + if (!category || category === activeCategory) return + + const list = tab?.closest('[role="tablist"]')?.getBoundingClientRect() + const panel = event.currentTarget.getBoundingClientRect() + if (previous && list && previous.x < point.x && point.x < list.right) { + const progress = (point.x - previous.x) / (list.right - previous.x) + const top = previous.y + (panel.top - previous.y) * progress + const bottom = previous.y + (panel.bottom - previous.y) * progress + if (point.y >= top && point.y <= bottom) { + timeout.current = setTimeout(() => onCategoryChange(category), 300) + return + } + } + + onCategoryChange(category) + } + + function selectCategory(category: string) { + reset() + onCategoryChange(category) + } + + return { onPointerMove, onPointerLeave: reset, selectCategory } +}