Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion registry/activity-graph/activity-graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export interface ActivityEntry {
count: number
}

export interface ActivityGraphProps {
export interface ActivityGraphProps extends Omit<React.ComponentProps<"div">, "children"> {
/** Activity data entries. */
data: ActivityEntry[]
/**
Expand Down Expand Up @@ -163,6 +163,7 @@ export function ActivityGraph({
blockRadius = 2,
weeks: weekCount = 52,
className,
...props
}: ActivityGraphProps) {
const containerRef = React.useRef<HTMLDivElement>(null)
const [autoSize, setAutoSize] = React.useState<number | null>(null)
Expand Down Expand Up @@ -205,6 +206,8 @@ export function ActivityGraph({
)}
role="img"
aria-label="Activity graph"
data-slot="activity-graph"
{...props}
>
{showGraph && (
<div
Expand Down
22 changes: 8 additions & 14 deletions registry/ai-copy-button/ai-copy-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ import { DropdownMenu } from "radix-ui"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

/* ------------------------------------------------------------------ */
/* Brand icons (not available in lucide-react) */
/* ------------------------------------------------------------------ */
// Brand icons (not available in lucide-react)

type IconProps = React.SVGProps<SVGSVGElement>

Expand Down Expand Up @@ -75,9 +73,7 @@ function MarkdownIcon({ className, ...props }: IconProps) {
)
}

/* ------------------------------------------------------------------ */
/* Targets */
/* ------------------------------------------------------------------ */
// Targets

type BuiltInTarget =
| "markdown"
Expand Down Expand Up @@ -167,9 +163,7 @@ const defaultTargets: (BuiltInTarget | AiTarget)[] = [
"gemini",
]

/* ------------------------------------------------------------------ */
/* Variants */
/* ------------------------------------------------------------------ */
// Variants

const aiCopyButtonVariants = cva(
"inline-flex items-center shrink-0 whitespace-nowrap font-medium transition-colors outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50",
Expand Down Expand Up @@ -200,12 +194,11 @@ const aiCopyButtonVariants = cva(
}
)

/* ------------------------------------------------------------------ */
/* Component */
/* ------------------------------------------------------------------ */
// Component

interface AiCopyButtonProps
extends VariantProps<typeof aiCopyButtonVariants> {
extends Omit<React.ComponentProps<"div">, "children">,
VariantProps<typeof aiCopyButtonVariants> {
/** The string content to copy or send to AI targets. */
value: string
/** Primary button label. Defaults to "Copy". */
Expand All @@ -216,7 +209,6 @@ interface AiCopyButtonProps
brandColors?: boolean
/** Callback fired after the primary copy action completes. */
onCopy?: () => void
className?: string
}

function AiCopyButton({
Expand All @@ -228,6 +220,7 @@ function AiCopyButton({
size,
onCopy,
className,
...props
}: AiCopyButtonProps) {
const [copied, setCopied] = React.useState(false)
const resolved = resolveTargets(targets)
Expand Down Expand Up @@ -283,6 +276,7 @@ function AiCopyButton({
"rounded-md p-0",
className
)}
{...props}
>
{/* Primary copy button */}
<button
Expand Down
7 changes: 3 additions & 4 deletions registry/api-ref-table/api-ref-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ interface ApiProp {
fullType?: string
}

interface ApiRefTableProps {
interface ApiRefTableProps extends React.ComponentProps<"div"> {
title: string
props: ApiProp[]
className?: string
}

function typeColor(type: string) {
Expand Down Expand Up @@ -127,9 +126,9 @@ function ApiRefRow({ prop }: { prop: ApiProp }) {
)
}

export function ApiRefTable({ title, props, className }: ApiRefTableProps) {
export function ApiRefTable({ title, props, className, ...rest }: ApiRefTableProps) {
return (
<div className={cn("overflow-hidden rounded-xl border border-border/60 bg-card shadow-sm", className)}>
<div data-slot="api-ref-table" className={cn("overflow-hidden rounded-xl border border-border/60 bg-card shadow-sm", className)} {...rest}>
<div className="border-b border-border/40 px-4 py-3">
<h3 className="text-lg font-bold tracking-tight">{title}</h3>
</div>
Expand Down
24 changes: 14 additions & 10 deletions registry/code-block-command/code-block-command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const defaultIcons: Record<PackageManager, string> = {

type IconStyle = "none" | "colored" | "muted"

interface CodeBlockCommandProps {
interface CodeBlockCommandProps extends Omit<React.ComponentProps<"div">, "children"> {
pnpm?: string
yarn?: string
npm?: string
Expand Down Expand Up @@ -97,24 +97,25 @@ export function CodeBlockCommand({
show,
colorTheme,
className,
...props
}: CodeBlockCommandProps) {
const commands: Partial<Record<PackageManager, string>> = {
const commands = React.useMemo<Partial<Record<PackageManager, string>>>(() => ({
...(pnpm && { pnpm }),
...(yarn && { yarn }),
...(npm && { npm }),
...(bun && { bun }),
...(shadcn && { shadcn }),
}
}), [pnpm, yarn, npm, bun, shadcn])

const available = (show ?? managers).filter((m) => commands[m])

const [active, setActive] = React.useState<PackageManager>(() => {
if (typeof window !== "undefined") {
const stored = localStorage.getItem(STORAGE_KEY) as PackageManager | null
if (stored && commands[stored]) return stored
}
return available[0] ?? "pnpm"
})
const [active, setActive] = React.useState<PackageManager>(available[0] ?? "pnpm")

// Sync with localStorage after hydration to avoid mismatch
React.useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY) as PackageManager | null
if (stored && commands[stored]) setActive(stored)
}, [commands])

const [copied, setCopied] = React.useState(false)

Expand Down Expand Up @@ -149,10 +150,12 @@ export function CodeBlockCommand({

return (
<div
data-slot="code-block-command"
className={cn(
"overflow-hidden rounded-xl border border-border/60 bg-card shadow-sm",
className
)}
{...props}
>
<div className="flex items-center justify-between border-b border-border/60 bg-muted/40">
<div className="flex" role="tablist" aria-label="Package manager">
Expand Down Expand Up @@ -190,6 +193,7 @@ export function CodeBlockCommand({
</button>
</div>
<div
data-slot="code-block-command"
className="overflow-x-auto px-4 py-3"
style={
colorTheme
Expand Down
13 changes: 6 additions & 7 deletions registry/code-block/code-block-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ import * as React from "react"
import { Check, ChevronDown, Copy } from "lucide-react"
import { cn } from "@/lib/utils"

interface CodeBlockCopyButtonProps {
interface CodeBlockCopyButtonProps extends Omit<React.ComponentProps<"button">, "value"> {
value: string
className?: string
}

export function CodeBlockCopyButton({ value, className }: CodeBlockCopyButtonProps) {
export function CodeBlockCopyButton({ value, className, ...props }: CodeBlockCopyButtonProps) {
const [copied, setCopied] = React.useState(false)

async function handleCopy() {
Expand All @@ -34,25 +33,25 @@ export function CodeBlockCopyButton({ value, className }: CodeBlockCopyButtonPro
<button
type="button"
onClick={handleCopy}
data-slot="code-block-copy-button"
className={cn(
"inline-flex h-8 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
className
)}
aria-label={copied ? "Copied code" : "Copy code"}
{...props}
>
{copied ? <Check className="size-3.5" /> : <Copy className="size-3.5" />}
{copied ? "Copied" : "Copy"}
</button>
)
}

/* ------------------------------------------------------------------ */
/* CodeBlockWrapper */
/* ------------------------------------------------------------------ */
// CodeBlockWrapper

type Overflow = "default" | "scrollable" | "collapsible"

interface CodeBlockWrapperProps {
interface CodeBlockWrapperProps extends Omit<React.ComponentProps<"div">, "children"> {
overflow: Overflow
maxHeight?: number
muted?: boolean
Expand Down
5 changes: 4 additions & 1 deletion registry/code-block/code-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function getLanguageIcon(language: string): string | null {
return languageIcons[title] ?? null
}

interface CodeBlockProps {
interface CodeBlockProps extends Omit<React.ComponentProps<"div">, "children"> {
code: string
language?: string
title?: string
Expand All @@ -113,6 +113,7 @@ export async function CodeBlock({
compact = false,
theme,
className,
...props
}: CodeBlockProps) {
const highlighted = await highlightCode(code, language, theme)

Expand All @@ -133,13 +134,15 @@ export async function CodeBlock({

return (
<div
data-slot="code-block"
className={cn(
"overflow-hidden rounded-xl border shadow-sm",
muted
? "border-border/40 bg-muted/30"
: "border-border/60 bg-card",
className
)}
{...props}
>
{!compact && (
<div
Expand Down
7 changes: 4 additions & 3 deletions registry/code-line/code-line-copy-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ import * as React from "react"
import { Check, Copy } from "lucide-react"
import { cn } from "@/lib/utils"

interface CodeLineCopyButtonProps {
interface CodeLineCopyButtonProps extends Omit<React.ComponentProps<"button">, "value"> {
value: string
className?: string
}

function CodeLineCopyButton({ value, className }: CodeLineCopyButtonProps) {
function CodeLineCopyButton({ value, className, ...props }: CodeLineCopyButtonProps) {
const [copied, setCopied] = React.useState(false)

async function handleCopy() {
Expand All @@ -33,11 +32,13 @@ function CodeLineCopyButton({ value, className }: CodeLineCopyButtonProps) {
<button
type="button"
onClick={handleCopy}
data-slot="code-line-copy-button"
className={cn(
"inline-flex shrink-0 items-center justify-center rounded-md p-2 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50",
className
)}
aria-label={copied ? "Copied" : "Copy to clipboard"}
{...props}
>
{copied ? (
<Check className="size-3.5 text-emerald-500" />
Expand Down
5 changes: 3 additions & 2 deletions registry/code-line/code-line.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { cn } from "@/lib/utils"
import { highlightCode } from "@/lib/highlight-code"
import { CodeLineCopyButton } from "@/registry/code-line/code-line-copy-button"

interface CodeLineProps {
interface CodeLineProps extends Omit<React.ComponentProps<"div">, "children"> {
/** The code string to display. Should be a single line. */
code: string
/** Language for syntax highlighting. */
Expand All @@ -32,7 +32,6 @@ interface CodeLineProps {
hideCopy?: boolean
/** Shiki theme name for single-theme rendering (e.g. "dracula", "nord"). */
theme?: string
className?: string
}

async function CodeLine({
Expand All @@ -42,6 +41,7 @@ async function CodeLine({
hideCopy = false,
theme,
className,
...props
}: CodeLineProps) {
const highlighted = await highlightCode(code.trim(), language, theme)

Expand All @@ -59,6 +59,7 @@ async function CodeLine({
className
)}
style={themeBg ? { backgroundColor: themeBg } : undefined}
{...props}
>
{label && (
<span className="flex shrink-0 items-center self-stretch border-r border-border/60 bg-muted/50 px-3 text-xs font-medium text-muted-foreground">
Expand Down
7 changes: 4 additions & 3 deletions registry/cron-schedule/cron-schedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ function formatNextRun(date: Date): string {
return `${day}, ${month} ${d} at ${hour}:${m} ${period}`
}

interface CronScheduleProps {
interface CronScheduleProps extends Omit<React.ComponentProps<"div">, "children" | "title"> {
/** Standard 5-field cron expression (e.g. "0 9 * * 1-5"). */
expression: string
/** Optional heading label. */
Expand All @@ -228,8 +228,6 @@ interface CronScheduleProps {
showNextRuns?: number
/** Base date for computing next runs. Defaults to now. */
referenceDate?: Date
/** Additional CSS classes on the root element. */
className?: string
}

function CronSchedule({
Expand All @@ -238,6 +236,7 @@ function CronSchedule({
showNextRuns = 0,
referenceDate,
className,
...props
}: CronScheduleProps) {
const fields = expression.trim().split(/\s+/)

Expand All @@ -249,6 +248,7 @@ function CronSchedule({
"rounded-xl border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive",
className
)}
{...props}
>
Invalid cron expression. Expected 5 fields, got {fields.length}.
</div>
Expand All @@ -268,6 +268,7 @@ function CronSchedule({
"overflow-hidden rounded-xl border border-border/60 bg-card shadow-sm",
className
)}
{...props}
>
{/* Header */}
<div className="flex items-start justify-between gap-3 border-b border-border/40 px-4 py-3">
Expand Down
9 changes: 6 additions & 3 deletions registry/diff-viewer/diff-viewer-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import * as React from "react"
import { Check, Copy } from "lucide-react"
import { cn } from "@/lib/utils"

interface DiffViewerCopyButtonProps {
interface DiffViewerCopyButtonProps extends Omit<React.ComponentProps<"button">, "value"> {
value: string
}

export function DiffViewerCopyButton({ value }: DiffViewerCopyButtonProps) {
export function DiffViewerCopyButton({ value, className, ...props }: DiffViewerCopyButtonProps) {
const [copied, setCopied] = React.useState(false)

async function handleCopy() {
Expand All @@ -20,8 +21,10 @@ export function DiffViewerCopyButton({ value }: DiffViewerCopyButtonProps) {
<button
type="button"
onClick={handleCopy}
className="inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
data-slot="diff-viewer-copy-button"
className={cn("inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50", className)}
aria-label={copied ? "Copied" : "Copy code"}
{...props}
>
{copied ? <Check className="size-3.5" /> : <Copy className="size-3.5" />}
{copied ? "Copied" : "Copy"}
Expand Down
Loading
Loading