mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
80595e6463
* fix: migrate to new facelift * add: max-w- for footer * update * DCO Remediation Commit for christopherkindl <53372002+christopherkindl@users.noreply.github.com> I, christopherkindl <53372002+christopherkindl@users.noreply.github.com>, hereby add my Signed-off-by to this commit:ba7353f1fcI, christopherkindl <53372002+christopherkindl@users.noreply.github.com>, hereby add my Signed-off-by to this commit:e83ca813b1I, christopherkindl <53372002+christopherkindl@users.noreply.github.com>, hereby add my Signed-off-by to this commit:8e69ff0d6bSigned-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * Fix: Mobile menu's "close on route change" useEffect has empty dependency array [], making it a no-op that never fires on actual route changes. This commit fixes the issue reported at docs/components/geistdocs/mobile-menu.tsx:80 ## Bug Analysis The `MobileMenu` component at `docs/components/geistdocs/mobile-menu.tsx` line 80 has a useEffect intended to close the menu on route changes: ```js useEffect(() => { setShow(false); }, []); ``` The empty dependency array `[]` means this effect runs exactly once — on initial mount — when `show` is already `false` (its initial state). This is a complete no-op. It never detects or responds to route changes. **When this manifests:** If a user opens the mobile menu and then navigates via browser back/forward buttons, the menu remains open and `document.body.style.overflow` stays set to `'hidden'`, completely locking page scroll. While clicking the `NavLink` items within the menu closes it via their `onClick={close}` handlers, programmatic/browser-level navigation bypasses those handlers. **Impact:** Users on mobile who use browser navigation while the menu is open will have a broken experience — the menu stays open overlaying the page and body scroll is locked. ## Fix Replaced the empty-dependency useEffect with the correct pattern already demonstrated in the sibling `sidebar.tsx` component: 1. Added `usePathname()` from `next/navigation` to track the current route. 2. Added a `useRef` to store the previous pathname. 3. Updated the useEffect to depend on `pathname`, comparing it to the previous value and closing the menu when a change is detected. This ensures the menu closes on any route change, whether triggered by clicking a link, browser back/forward, or programmatic navigation. Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: christopherkindl <christopher.kindl@gmail.com> * fix(docs): use ds-gray-900 token for muted-foreground color Replace hardcoded oklch values with var(--ds-gray-900) for both light and dark mode to stay in sync with the design system. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): update world component colors to use 900-level tokens Align icon and badge colors with design system by switching from 500/600 shades to 900-level variants across WorldDetailHero and WorldTestingPerformance components. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): update world page colors and align hero layout with TOC grid - Align hero quick links with TOC sidebar using matching grid layout - Add shrink-0 to hero icons for consistent vertical alignment - Update benchmark bar colors (green for fastest, blue for others) - Remove extra px-4 padding from hero section Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * polish: /worlds page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * feat(docs): add filterable world cards with section grouping Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): use radix checkbox with SSR placeholder to prevent layout shift Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): use badge tabs for world filters instead of checkboxes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * revert: restore .husky/pre-commit from main Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): improve benchmark history chart UI and API performance Benchmark history dialog: - Fix DialogContent accessibility warning (use DialogDescription) - Fix 2MB unstable_cache error by using in-memory cache for snapshot map - Add Geist-style underline variant to Tabs component - Add skeleton loading state matching stat cards + chart layout - Add Geist shimmer effect to Skeleton component (--ds-gray-100/200 gradient) - Add --ds-shadow-tooltip token and apply to chart tooltip - Use design tokens for all chart colors (--ds-blue-800, --ds-green-800, --ds-purple-900) - Use linear interpolation, solid gridlines (--ds-gray-400), no animations - Add 10% fill area under single-line charts, hide for multi-line - Add white ring to chart dots, equidistant x-axis labels - Stale-while-revalidate client cache for instant mode switching - Stat cards refactored to use .map() over a data array - Lock dialog height with min-h to prevent layout shift API performance: - Early exit on gh-pages pagination (cap at MAX_ITEMS * 3) - Use snapshot timestamp for releases (eliminates 30 serial API calls) - Increase fetch batch size from 10 to 30 - Remove unstable_cache, rely on in-memory + fetch-level caching Other: - Add recharts.d.ts type augmentation for React 19 compatibility Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): add missing geist tokens and utilities from homepage-visuals Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * [DO NOT MERGE][home] Adds vercel-com workflow visuals (#1674) * add: visualizations * DCO Remediation Commit for christopherkindl <53372002+christopherkindl@users.noreply.github.com> I, christopherkindl <53372002+christopherkindl@users.noreply.github.com>, hereby add my Signed-off-by to this commit:eefbc9744aSigned-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix: adjust bar border radius and add @container to feature cards Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix: use rounded-lg for bar border radius, add @container to wide card Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * revert: restore geistdocs.css from main Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> --------- Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(docs): add mobile docs bar to cookbook pages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): add mobile docs bar to cookbook pages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): increase hero section vertical spacing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * chore(docs): remove recharts React 19 type shim Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): hoist useTransform calls to top level of components Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): add optional chaining for world.features access Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): log warning when benchmark pagination cap is hit Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): make filter badges keyboard accessible Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): clean up setTimeout on unmount in globe path components Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * feat(docs): add full-width frameworks section to homepage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * feat(docs): update navbar logo and add framework logos from geist symbols - Replace LogoWorkflowSdk with Geist symbols wordmark (LogoWorkflow) - Add currentColor framework logo icons from geistcn-assets - Add full-width frameworks section to homepage with colored logos - TanStack shown as "Coming soon" with analytics tracking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * docs: add comment noting timestamp semantic change in benchmark API Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> * fix(docs): move frameworks section after effortless setup on homepage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> --------- Signed-off-by: christopherkindl <53372002+christopherkindl@users.noreply.github.com> Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
166 lines
4.4 KiB
TypeScript
166 lines
4.4 KiB
TypeScript
'use client';
|
|
|
|
import { CheckIcon, CopyIcon } from 'lucide-react';
|
|
import {
|
|
type CSSProperties,
|
|
type ReactNode,
|
|
useCallback,
|
|
useLayoutEffect,
|
|
useRef,
|
|
useState,
|
|
} from 'react';
|
|
import { toast } from 'sonner';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
type CodeBlockProps = {
|
|
children: ReactNode;
|
|
className?: string;
|
|
icon?: ReactNode;
|
|
style?: CSSProperties;
|
|
tabIndex?: number;
|
|
title?: string;
|
|
'data-line-numbers'?: string;
|
|
'data-line-highlighting'?: string;
|
|
};
|
|
|
|
export const CodeBlock = ({
|
|
children,
|
|
className,
|
|
icon,
|
|
style,
|
|
tabIndex,
|
|
title,
|
|
...rest
|
|
}: CodeBlockProps) => {
|
|
const ref = useRef<HTMLPreElement>(null);
|
|
const [isCopied, setIsCopied] = useState(false);
|
|
const { 'data-line-numbers': lineNumbers } = rest;
|
|
|
|
// Filter out lines marked with `// @setup` using useLayoutEffect
|
|
// useLayoutEffect runs synchronously after DOM mutations but before paint,
|
|
// which minimizes the flash of @setup content
|
|
useLayoutEffect(() => {
|
|
if (!ref.current) return;
|
|
|
|
const lines = Array.from(ref.current.querySelectorAll('.line'));
|
|
|
|
// First pass: hide @setup lines
|
|
for (const line of lines) {
|
|
if (line.textContent?.includes('// @setup')) {
|
|
(line as HTMLElement).style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// Second pass: hide leading empty lines (after @setup lines are hidden)
|
|
for (const line of lines) {
|
|
const htmlLine = line as HTMLElement;
|
|
// Skip already hidden lines
|
|
if (htmlLine.style.display === 'none') continue;
|
|
// If this line is empty (no text content), hide it
|
|
if (!line.textContent?.trim()) {
|
|
htmlLine.style.display = 'none';
|
|
} else {
|
|
// Stop at first non-empty visible line
|
|
break;
|
|
}
|
|
}
|
|
}, [children]);
|
|
|
|
const copyToClipboard = useCallback(async () => {
|
|
if (typeof window === 'undefined' || !navigator?.clipboard?.writeText) {
|
|
toast.error('Clipboard API not available');
|
|
return;
|
|
}
|
|
|
|
const code = ref.current?.innerText;
|
|
|
|
if (!code) {
|
|
toast.error('No code to copy');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await navigator.clipboard.writeText(code);
|
|
setIsCopied(true);
|
|
setTimeout(() => setIsCopied(false), 2000);
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
|
|
toast.error(message);
|
|
}
|
|
}, []);
|
|
|
|
const Icon = isCopied ? CheckIcon : CopyIcon;
|
|
|
|
const CodeBlockComponent = useCallback(
|
|
(props: { className?: string }) => (
|
|
<pre
|
|
className={cn(
|
|
'not-prose flex-1 overflow-x-auto rounded-sm border bg-background py-3 text-sm outline-none',
|
|
'[&>code]:grid [&>code]:min-w-max',
|
|
className,
|
|
props.className
|
|
)}
|
|
ref={ref}
|
|
style={style}
|
|
tabIndex={tabIndex}
|
|
>
|
|
{children}
|
|
</pre>
|
|
),
|
|
[children, style, tabIndex, className]
|
|
);
|
|
|
|
if (!title) {
|
|
return (
|
|
<div className="relative mb-6">
|
|
<CodeBlockComponent
|
|
className={cn(lineNumbers ? 'line-numbers' : '', className)}
|
|
/>
|
|
<Button
|
|
className="absolute top-[5px] right-[5px] bg-background/80 backdrop-blur-sm"
|
|
onClick={copyToClipboard}
|
|
size="icon"
|
|
variant="ghost"
|
|
>
|
|
<Icon size={14} />
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Card className="not-prose mb-6 gap-0 overflow-hidden rounded-sm p-0 shadow-none border">
|
|
<CardHeader className="flex items-center gap-2 border-b bg-sidebar py-1.5! pr-1.5 pl-4 text-muted-foreground">
|
|
<div
|
|
className="size-3.5 shrink-0"
|
|
// biome-ignore lint/security/noDangerouslySetInnerHtml: "Required for icon prop."
|
|
dangerouslySetInnerHTML={{ __html: icon as unknown as TrustedHTML }}
|
|
/>
|
|
<CardTitle className="flex-1 font-mono font-normal text-sm tracking-tight">
|
|
{title}
|
|
</CardTitle>
|
|
<Button
|
|
className={cn('shrink-0', className)}
|
|
onClick={copyToClipboard}
|
|
size="icon"
|
|
variant="ghost"
|
|
>
|
|
<Icon size={14} />
|
|
</Button>
|
|
</CardHeader>
|
|
<CardContent className="p-0">
|
|
<CodeBlockComponent
|
|
className={cn(
|
|
className,
|
|
'rounded-none border-none',
|
|
lineNumbers ? 'line-numbers' : ''
|
|
)}
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
};
|