Remove hard-coded and capped border radius overrides so shared controls and feature actions consistently follow the active theme radius. - Replace fixed radius utilities with semantic theme-aware radius tokens - Remove redundant `rounded-full` and pixel-based overrides from header, toolbar, Playground, and utility actions - Drop unused `StatusBadge` rounded prop usage - Keep existing component behavior intact while improving global theme consistency
298 lines
6.6 KiB
TypeScript
Vendored
298 lines
6.6 KiB
TypeScript
Vendored
'use client'
|
|
|
|
import {
|
|
type ComponentProps,
|
|
createContext,
|
|
useCallback,
|
|
useContext,
|
|
useEffect,
|
|
useState,
|
|
} from 'react'
|
|
import { ArrowLeftIcon, ArrowRightIcon } from 'lucide-react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { cn } from '@/lib/utils'
|
|
import { Badge } from '@/components/ui/badge'
|
|
import { Button } from '@/components/ui/button'
|
|
import {
|
|
Carousel,
|
|
type CarouselApi,
|
|
CarouselContent,
|
|
CarouselItem,
|
|
} from '@/components/ui/carousel'
|
|
import {
|
|
HoverCard,
|
|
HoverCardContent,
|
|
HoverCardTrigger,
|
|
} from '@/components/ui/hover-card'
|
|
|
|
export type InlineCitationProps = ComponentProps<'span'>
|
|
|
|
export const InlineCitation = ({
|
|
className,
|
|
...props
|
|
}: InlineCitationProps) => (
|
|
<span
|
|
className={cn('group inline items-center gap-1', className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
|
|
export type InlineCitationTextProps = ComponentProps<'span'>
|
|
|
|
export const InlineCitationText = ({
|
|
className,
|
|
...props
|
|
}: InlineCitationTextProps) => (
|
|
<span
|
|
className={cn('group-hover:bg-accent transition-colors', className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
|
|
export type InlineCitationCardProps = ComponentProps<typeof HoverCard>
|
|
|
|
export const InlineCitationCard = (props: InlineCitationCardProps) => (
|
|
<HoverCard {...props} />
|
|
)
|
|
|
|
export type InlineCitationCardTriggerProps = ComponentProps<typeof Badge> & {
|
|
sources: string[]
|
|
}
|
|
|
|
export const InlineCitationCardTrigger = ({
|
|
sources,
|
|
className,
|
|
...props
|
|
}: InlineCitationCardTriggerProps) => (
|
|
<HoverCardTrigger
|
|
delay={0}
|
|
closeDelay={0}
|
|
render={
|
|
<Badge className={cn('ml-1', className)} variant='secondary' {...props}>
|
|
{sources[0] ? (
|
|
<>
|
|
{new URL(sources[0]).hostname}{' '}
|
|
{sources.length > 1 && `+${sources.length - 1}`}
|
|
</>
|
|
) : (
|
|
'unknown'
|
|
)}
|
|
</Badge>
|
|
}
|
|
/>
|
|
)
|
|
|
|
export type InlineCitationCardBodyProps = ComponentProps<'div'>
|
|
|
|
export const InlineCitationCardBody = ({
|
|
className,
|
|
...props
|
|
}: InlineCitationCardBodyProps) => (
|
|
<HoverCardContent className={cn('relative w-80 p-0', className)} {...props} />
|
|
)
|
|
|
|
const CarouselApiContext = createContext<CarouselApi | undefined>(undefined)
|
|
|
|
const useCarouselApi = () => {
|
|
const context = useContext(CarouselApiContext)
|
|
return context
|
|
}
|
|
|
|
export type InlineCitationCarouselProps = ComponentProps<typeof Carousel>
|
|
|
|
export const InlineCitationCarousel = ({
|
|
className,
|
|
children,
|
|
...props
|
|
}: InlineCitationCarouselProps) => {
|
|
const [api, setApi] = useState<CarouselApi>()
|
|
|
|
return (
|
|
<CarouselApiContext.Provider value={api}>
|
|
<Carousel className={cn('w-full', className)} setApi={setApi} {...props}>
|
|
{children}
|
|
</Carousel>
|
|
</CarouselApiContext.Provider>
|
|
)
|
|
}
|
|
|
|
export type InlineCitationCarouselContentProps = ComponentProps<'div'>
|
|
|
|
export const InlineCitationCarouselContent = (
|
|
props: InlineCitationCarouselContentProps
|
|
) => <CarouselContent {...props} />
|
|
|
|
export type InlineCitationCarouselItemProps = ComponentProps<'div'>
|
|
|
|
export const InlineCitationCarouselItem = ({
|
|
className,
|
|
...props
|
|
}: InlineCitationCarouselItemProps) => (
|
|
<CarouselItem
|
|
className={cn('w-full space-y-2 p-4 pl-8', className)}
|
|
{...props}
|
|
/>
|
|
)
|
|
|
|
export type InlineCitationCarouselHeaderProps = ComponentProps<'div'>
|
|
|
|
export const InlineCitationCarouselHeader = ({
|
|
className,
|
|
...props
|
|
}: InlineCitationCarouselHeaderProps) => (
|
|
<div
|
|
className={cn(
|
|
'bg-secondary flex items-center justify-between gap-2 rounded-t-md p-2',
|
|
className
|
|
)}
|
|
{...props}
|
|
/>
|
|
)
|
|
|
|
export type InlineCitationCarouselIndexProps = ComponentProps<'div'>
|
|
|
|
export const InlineCitationCarouselIndex = ({
|
|
children,
|
|
className,
|
|
...props
|
|
}: InlineCitationCarouselIndexProps) => {
|
|
const api = useCarouselApi()
|
|
const [current, setCurrent] = useState(0)
|
|
const [count, setCount] = useState(0)
|
|
|
|
useEffect(() => {
|
|
if (!api) {
|
|
return
|
|
}
|
|
|
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
setCount(api.scrollSnapList().length)
|
|
|
|
setCurrent(api.selectedScrollSnap() + 1)
|
|
|
|
api.on('select', () => {
|
|
setCurrent(api.selectedScrollSnap() + 1)
|
|
})
|
|
}, [api])
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'text-muted-foreground flex flex-1 items-center justify-end px-3 py-1 text-xs',
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{children ?? `${current}/${count}`}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export type InlineCitationCarouselPrevProps = ComponentProps<'button'>
|
|
|
|
export const InlineCitationCarouselPrev = ({
|
|
className,
|
|
...props
|
|
}: InlineCitationCarouselPrevProps) => {
|
|
const { t } = useTranslation()
|
|
const api = useCarouselApi()
|
|
|
|
const handleClick = useCallback(() => {
|
|
if (api) {
|
|
api.scrollPrev()
|
|
}
|
|
}, [api])
|
|
|
|
return (
|
|
<Button
|
|
variant='ghost'
|
|
size='icon-sm'
|
|
aria-label={t('Previous')}
|
|
className={cn('shrink-0', className)}
|
|
onClick={handleClick}
|
|
type='button'
|
|
{...props}
|
|
>
|
|
<ArrowLeftIcon className='text-muted-foreground size-4' />
|
|
</Button>
|
|
)
|
|
}
|
|
|
|
export type InlineCitationCarouselNextProps = ComponentProps<'button'>
|
|
|
|
export const InlineCitationCarouselNext = ({
|
|
className,
|
|
...props
|
|
}: InlineCitationCarouselNextProps) => {
|
|
const { t } = useTranslation()
|
|
const api = useCarouselApi()
|
|
|
|
const handleClick = useCallback(() => {
|
|
if (api) {
|
|
api.scrollNext()
|
|
}
|
|
}, [api])
|
|
|
|
return (
|
|
<Button
|
|
variant='ghost'
|
|
size='icon-sm'
|
|
aria-label={t('Next')}
|
|
className={cn('shrink-0', className)}
|
|
onClick={handleClick}
|
|
type='button'
|
|
{...props}
|
|
>
|
|
<ArrowRightIcon className='text-muted-foreground size-4' />
|
|
</Button>
|
|
)
|
|
}
|
|
|
|
export type InlineCitationSourceProps = ComponentProps<'div'> & {
|
|
title?: string
|
|
url?: string
|
|
description?: string
|
|
}
|
|
|
|
export const InlineCitationSource = ({
|
|
title,
|
|
url,
|
|
description,
|
|
className,
|
|
children,
|
|
...props
|
|
}: InlineCitationSourceProps) => (
|
|
<div className={cn('space-y-1', className)} {...props}>
|
|
{title && (
|
|
<h4 className='truncate text-sm leading-tight font-medium'>{title}</h4>
|
|
)}
|
|
{url && (
|
|
<p className='text-muted-foreground truncate text-xs break-all'>{url}</p>
|
|
)}
|
|
{description && (
|
|
<p className='text-muted-foreground line-clamp-3 text-sm leading-relaxed'>
|
|
{description}
|
|
</p>
|
|
)}
|
|
{children}
|
|
</div>
|
|
)
|
|
|
|
export type InlineCitationQuoteProps = ComponentProps<'blockquote'>
|
|
|
|
export const InlineCitationQuote = ({
|
|
children,
|
|
className,
|
|
...props
|
|
}: InlineCitationQuoteProps) => (
|
|
<blockquote
|
|
className={cn(
|
|
'border-muted text-muted-foreground border-l-2 pl-3 text-sm italic',
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
{children}
|
|
</blockquote>
|
|
)
|