style: 运行 prettier 格式化代码

This commit is contained in:
肖应宇 2026-03-17 14:46:40 +08:00
parent 5afe834b53
commit bc20208d0f
71 changed files with 921 additions and 738 deletions

View File

@ -14,7 +14,8 @@ type MockThreadSearchResult = Record<string, unknown> & {
}; };
export async function POST(request: Request) { export async function POST(request: Request) {
const body = ((await request.json().catch(() => ({}))) ?? {}) as ThreadSearchRequest; const body = ((await request.json().catch(() => ({}))) ??
{}) as ThreadSearchRequest;
const rawLimit = body.limit; const rawLimit = body.limit;
let limit = 50; let limit = 50;

View File

@ -82,28 +82,42 @@ export default function ChatPage() {
return ( return (
<ThreadContext.Provider value={{ thread, isMock }}> <ThreadContext.Provider value={{ thread, isMock }}>
<ChatBox threadId={threadId}> <ChatBox threadId={threadId}>
<div className="relative flex size-full min-h-0 bg-background justify-between"> <div className="bg-background relative flex size-full min-h-0 justify-between">
<header <header
className={cn( className={cn(
"absolute top-0 right-0 left-0 z-30 grid grid-cols-3 h-[58px] px-[20px] py-[15px] shrink-0 items-center rounded-t-[20px]", "absolute top-0 right-0 left-0 z-30 grid h-[58px] shrink-0 grid-cols-3 items-center rounded-t-[20px] px-[20px] py-[15px]",
isNewThread isNewThread
? "bg-background/0 backdrop-blur-none" ? "bg-background/0 backdrop-blur-none"
: "bg-background/80 shadow-xs backdrop-blur", : "bg-background/80 shadow-xs backdrop-blur",
)} )}
> >
{/* 返回查看结果左箭头 */} {/* 返回查看结果左箭头 */}
<div className="flex w-full items-center h-full text-sm font-medium"> <div className="flex h-full w-full items-center text-sm font-medium">
<button className="bg-transparent" onClick={() => setShowExitDialog(true)}> <button
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> className="bg-transparent"
<path d="M3.5 10H13.25H15.6875H16.5M3.5 10L7.5625 6M3.5 10L7.5625 14" stroke="#666666" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /> onClick={() => setShowExitDialog(true)}
>
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3.5 10H13.25H15.6875H16.5M3.5 10L7.5625 6M3.5 10L7.5625 14"
stroke="#666666"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg> </svg>
</button> </button>
</div> </div>
<div className="flex w-full justify-center items-center h-full text-sm font-medium overflow-hidden"> <div className="flex h-full w-full items-center justify-center overflow-hidden text-sm font-medium">
<ThreadTitle threadId={threadId} thread={thread} /> <ThreadTitle threadId={threadId} thread={thread} />
</div> </div>
<div className="flex overflow-hidden justify-end items-center gap-2"> <div className="flex items-center justify-end gap-2 overflow-hidden">
<DevTodoList <DevTodoList
className="bg-white" className="bg-white"
todos={thread.values.todos ?? []} todos={thread.values.todos ?? []}
@ -111,7 +125,11 @@ export default function ChatPage() {
!thread.values.todos || thread.values.todos.length === 0 !thread.values.todos || thread.values.todos.length === 0
} }
trigger={ trigger={
<Button size="sm" variant="ghost" className="text-sm font-medium py-[5px] px-[10px] h-full"> <Button
size="sm"
variant="ghost"
className="h-full px-[10px] py-[5px] text-sm font-medium"
>
<ListTodoIcon className="size-4" /> Todo <ListTodoIcon className="size-4" /> Todo
</Button> </Button>
} }
@ -129,18 +147,20 @@ export default function ChatPage() {
</div> </div>
</main> </main>
</div> </div>
<div className="fixed right-0 bottom-3 left-0 z-30 flex justify-center px-4 pointer-events-none"> <div className="pointer-events-none fixed right-0 bottom-3 left-0 z-30 flex justify-center px-4">
<div <div
className={cn( className={cn(
"relative w-full pointer-events-auto max-w-[720px]", "pointer-events-auto relative w-full max-w-[720px]",
isNewThread && "-translate-y-[calc(50vh-96px)] top-[-65px]", isNewThread && "top-[-65px] -translate-y-[calc(50vh-96px)]",
// isNewThread // isNewThread
// ? "max-w-(--container-width-sm)" // ? "max-w-(--container-width-sm)"
// : "max-w-(--container-width-md)", // : "max-w-(--container-width-md)",
)} )}
> >
<InputBox <InputBox
className={cn("bg-[#FBFAFC] w-full -translate-y-4 rounded-[20px] ")} className={cn(
"w-full -translate-y-4 rounded-[20px] bg-[#FBFAFC]",
)}
isNewThread={isNewThread} isNewThread={isNewThread}
threadId={threadId} threadId={threadId}
autoFocus={isNewThread} autoFocus={isNewThread}
@ -173,10 +193,18 @@ export default function ChatPage() {
退 退
</p> </p>
<DevDialogFooter> <DevDialogFooter>
<Button className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white" variant="ghost" onClick={() => setShowExitDialog(false)}> <Button
className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white"
variant="ghost"
onClick={() => setShowExitDialog(false)}
>
</Button> </Button>
<Button className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white" variant="ghost" onClick={() => setShowExitDialog(false)}> <Button
className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white"
variant="ghost"
onClick={() => setShowExitDialog(false)}
>
</Button> </Button>
</DevDialogFooter> </DevDialogFooter>

View File

@ -16,7 +16,7 @@ export type ArtifactProps = HTMLAttributes<HTMLDivElement>;
export const Artifact = ({ className, ...props }: ArtifactProps) => ( export const Artifact = ({ className, ...props }: ArtifactProps) => (
<div <div
className={cn( className={cn(
"bg-background flex flex-col overflow-hidden rounded-[20px] pt-[15px] px-[20px]", "bg-background flex flex-col overflow-hidden rounded-[20px] px-[20px] pt-[15px]",
className, className,
)} )}
{...props} {...props}
@ -31,7 +31,7 @@ export const ArtifactHeader = ({
}: ArtifactHeaderProps) => ( }: ArtifactHeaderProps) => (
<div <div
className={cn( className={cn(
"grid grid-cols-3 items-center mb-[20px] justify-between", "mb-[20px] grid grid-cols-3 items-center justify-between",
className, className,
)} )}
{...props} {...props}
@ -144,9 +144,6 @@ export const ArtifactContent = ({
...props ...props
}: ArtifactContentProps) => ( }: ArtifactContentProps) => (
<div className="min-h-0 flex-1 overflow-auto"> <div className="min-h-0 flex-1 overflow-auto">
<div <div className={cn("mb-[208px] p-4", className)} {...props} />
className={cn("p-4 mb-[208px]", className)}
{...props}
/>
</div> </div>
); );

View File

@ -147,7 +147,11 @@ export const ChainOfThoughtStep = memo(
{...props} {...props}
> >
<div className="relative mt-0.5"> <div className="relative mt-0.5">
{isValidElement(Icon) ? Icon : <Icon className="size-4 text-[#999999]" />} {isValidElement(Icon) ? (
Icon
) : (
<Icon className="size-4 text-[#999999]" />
)}
<div className="bg-border absolute top-7 bottom-0 left-1/2 -mx-px w-px" /> <div className="bg-border absolute top-7 bottom-0 left-1/2 -mx-px w-px" />
</div> </div>
<div className="flex-1 space-y-2 overflow-hidden"> <div className="flex-1 space-y-2 overflow-hidden">

View File

@ -19,7 +19,10 @@ export const Checkpoint = ({
...props ...props
}: CheckpointProps) => ( }: CheckpointProps) => (
<div <div
className={cn("flex items-center gap-0.5 text-muted-foreground overflow-hidden", className)} className={cn(
"text-muted-foreground flex items-center gap-0.5 overflow-hidden",
className,
)}
{...props} {...props}
> >
{children} {children}

View File

@ -115,7 +115,7 @@ export const ContextTrigger = ({ children, ...props }: ContextTriggerProps) => {
<HoverCardTrigger asChild> <HoverCardTrigger asChild>
{children ?? ( {children ?? (
<Button type="button" variant="ghost" {...props}> <Button type="button" variant="ghost" {...props}>
<span className="font-medium text-muted-foreground"> <span className="text-muted-foreground font-medium">
{renderedPercent} {renderedPercent}
</span> </span>
<ContextIcon /> <ContextIcon />
@ -163,7 +163,7 @@ export const ContextContentHeader = ({
<> <>
<div className="flex items-center justify-between gap-3 text-xs"> <div className="flex items-center justify-between gap-3 text-xs">
<p>{displayPct}</p> <p>{displayPct}</p>
<p className="font-mono text-muted-foreground"> <p className="text-muted-foreground font-mono">
{used} / {total} {used} / {total}
</p> </p>
</div> </div>
@ -213,8 +213,8 @@ export const ContextContentFooter = ({
return ( return (
<div <div
className={cn( className={cn(
"flex w-full items-center justify-between gap-3 bg-secondary p-3 text-xs", "bg-secondary flex w-full items-center justify-between gap-3 p-3 text-xs",
className className,
)} )}
{...props} {...props}
> >
@ -402,7 +402,7 @@ const TokensWithCost = ({
notation: "compact", notation: "compact",
}).format(tokens)} }).format(tokens)}
{costText ? ( {costText ? (
<span className="ml-2 text-muted-foreground"> {costText}</span> <span className="text-muted-foreground ml-2"> {costText}</span>
) : null} ) : null}
</span> </span>
); );

View File

@ -9,9 +9,9 @@ export type ControlsProps = ComponentProps<typeof ControlsPrimitive>;
export const Controls = ({ className, ...props }: ControlsProps) => ( export const Controls = ({ className, ...props }: ControlsProps) => (
<ControlsPrimitive <ControlsPrimitive
className={cn( className={cn(
"gap-px overflow-hidden rounded-md border bg-card p-1 shadow-none!", "bg-card gap-px overflow-hidden rounded-md border p-1 shadow-none!",
"[&>button]:rounded-md [&>button]:border-none! [&>button]:bg-transparent! [&>button]:hover:bg-secondary!", "[&>button]:hover:bg-secondary! [&>button]:rounded-md [&>button]:border-none! [&>button]:bg-transparent!",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -29,7 +29,7 @@ const Temporary = ({
return ( return (
<BaseEdge <BaseEdge
className="stroke-1 stroke-ring" className="stroke-ring stroke-1"
id={id} id={id}
path={edgePath} path={edgePath}
style={{ style={{
@ -41,13 +41,13 @@ const Temporary = ({
const getHandleCoordsByPosition = ( const getHandleCoordsByPosition = (
node: InternalNode<Node>, node: InternalNode<Node>,
handlePosition: Position handlePosition: Position,
) => { ) => {
// Choose the handle type based on position - Left is for target, Right is for source // Choose the handle type based on position - Left is for target, Right is for source
const handleType = handlePosition === Position.Left ? "target" : "source"; const handleType = handlePosition === Position.Left ? "target" : "source";
const handle = node.internals.handleBounds?.[handleType]?.find( const handle = node.internals.handleBounds?.[handleType]?.find(
(h) => h.position === handlePosition (h) => h.position === handlePosition,
); );
if (!handle) { if (!handle) {
@ -85,7 +85,7 @@ const getHandleCoordsByPosition = (
const getEdgeParams = ( const getEdgeParams = (
source: InternalNode<Node>, source: InternalNode<Node>,
target: InternalNode<Node> target: InternalNode<Node>,
) => { ) => {
const sourcePos = Position.Right; const sourcePos = Position.Right;
const [sx, sy] = getHandleCoordsByPosition(source, sourcePos); const [sx, sy] = getHandleCoordsByPosition(source, sourcePos);
@ -112,7 +112,7 @@ const Animated = ({ id, source, target, markerEnd, style }: EdgeProps) => {
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams( const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(
sourceNode, sourceNode,
targetNode targetNode,
); );
const [edgePath] = getBezierPath({ const [edgePath] = getBezierPath({

View File

@ -17,7 +17,7 @@ export const Image = ({
alt={props.alt} alt={props.alt}
className={cn( className={cn(
"h-auto max-w-full overflow-hidden rounded-md", "h-auto max-w-full overflow-hidden rounded-md",
props.className props.className,
)} )}
src={`data:${mediaType};base64,${base64}`} src={`data:${mediaType};base64,${base64}`}
/> />

View File

@ -87,7 +87,7 @@ export const Loader = ({ className, size = 16, ...props }: LoaderProps) => (
<div <div
className={cn( className={cn(
"inline-flex animate-spin items-center justify-center", "inline-flex animate-spin items-center justify-center",
className className,
)} )}
{...props} {...props}
> >

View File

@ -28,7 +28,9 @@ export const Message = ({ className, from, ...props }: MessageProps) => (
<div <div
className={cn( className={cn(
"group flex w-full flex-col gap-2 rounded-[10px] p-[20px]", "group flex w-full flex-col gap-2 rounded-[10px] p-[20px]",
from === "user" ? "is-user ml-auto justify-end" : "is-assistant bg-[#ffffff]", from === "user"
? "is-user ml-auto justify-end"
: "is-assistant bg-[#ffffff]",
className, className,
)} )}
{...props} {...props}

View File

@ -22,7 +22,7 @@ export const Node = ({ handles, className, ...props }: NodeProps) => (
<Card <Card
className={cn( className={cn(
"node-container relative size-full h-auto w-sm gap-0 rounded-md p-0", "node-container relative size-full h-auto w-sm gap-0 rounded-md p-0",
className className,
)} )}
{...props} {...props}
> >
@ -36,7 +36,7 @@ export type NodeHeaderProps = ComponentProps<typeof CardHeader>;
export const NodeHeader = ({ className, ...props }: NodeHeaderProps) => ( export const NodeHeader = ({ className, ...props }: NodeHeaderProps) => (
<CardHeader <CardHeader
className={cn("gap-0.5 rounded-t-md border-b bg-secondary p-3!", className)} className={cn("bg-secondary gap-0.5 rounded-t-md border-b p-3!", className)}
{...props} {...props}
/> />
); );
@ -65,7 +65,7 @@ export type NodeFooterProps = ComponentProps<typeof CardFooter>;
export const NodeFooter = ({ className, ...props }: NodeFooterProps) => ( export const NodeFooter = ({ className, ...props }: NodeFooterProps) => (
<CardFooter <CardFooter
className={cn("rounded-b-md border-t bg-secondary p-3!", className)} className={cn("bg-secondary rounded-b-md border-t p-3!", className)}
{...props} {...props}
/> />
); );

View File

@ -7,8 +7,8 @@ type PanelProps = ComponentProps<typeof PanelPrimitive>;
export const Panel = ({ className, ...props }: PanelProps) => ( export const Panel = ({ className, ...props }: PanelProps) => (
<PanelPrimitive <PanelPrimitive
className={cn( className={cn(
"m-4 overflow-hidden rounded-md border bg-card p-1", "bg-card m-4 overflow-hidden rounded-md border p-1",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -321,13 +321,28 @@ export function PromptInputAttachment({
src={data.url} src={data.url}
/> />
{/* 悬浮遮罩层 */} {/* 悬浮遮罩层 */}
<div className="absolute inset-0 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100" <div
style={{ borderRadius: '10px', background: 'rgba(0, 0, 0, 0.60)' }} className="absolute inset-0 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100"
style={{ borderRadius: "10px", background: "rgba(0, 0, 0, 0.60)" }}
> >
{/* 眼睛图标 - 居中 */} {/* 眼睛图标 - 居中 */}
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"> <svg
<path d="M10 4.75C13.3315 4.75 16.4669 6.61444 18.9805 9.88281C19.0335 9.95183 19.0335 10.0482 18.9805 10.1172C16.4669 13.3856 13.3315 15.25 10 15.25C6.66835 15.2499 3.53309 13.3857 1.01953 10.1172C0.966466 10.0482 0.966465 9.95182 1.01953 9.88281C3.53309 6.61435 6.66835 4.75014 10 4.75Z" stroke="white" strokeWidth="1.5"/> xmlns="http://www.w3.org/2000/svg"
<path d="M10 7.75C11.2426 7.75 12.25 8.75736 12.25 10C12.25 11.2426 11.2426 12.25 10 12.25C8.75736 12.25 7.75 11.2426 7.75 10C7.75 8.75736 8.75736 7.75 10 7.75Z" stroke="white" strokeWidth="1.5"/> width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
>
<path
d="M10 4.75C13.3315 4.75 16.4669 6.61444 18.9805 9.88281C19.0335 9.95183 19.0335 10.0482 18.9805 10.1172C16.4669 13.3856 13.3315 15.25 10 15.25C6.66835 15.2499 3.53309 13.3857 1.01953 10.1172C0.966466 10.0482 0.966465 9.95182 1.01953 9.88281C3.53309 6.61435 6.66835 4.75014 10 4.75Z"
stroke="white"
strokeWidth="1.5"
/>
<path
d="M10 7.75C11.2426 7.75 12.25 8.75736 12.25 10C12.25 11.2426 11.2426 12.25 10 12.25C8.75736 12.25 7.75 11.2426 7.75 10C7.75 8.75736 8.75736 7.75 10 7.75Z"
stroke="white"
strokeWidth="1.5"
/>
</svg> </svg>
{/* 删除按钮 - 右上角 */} {/* 删除按钮 - 右上角 */}
<button <button
@ -339,9 +354,25 @@ export function PromptInputAttachment({
}} }}
type="button" type="button"
> >
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8" fill="none"> <svg
<path d="M0.75 0.75L6.74995 6.74995" stroke="white" strokeWidth="1.5" strokeLinecap="round"/> xmlns="http://www.w3.org/2000/svg"
<path d="M6.75 0.75L0.750025 6.74992" stroke="white" strokeWidth="1.5" strokeLinecap="round"/> width="8"
height="8"
viewBox="0 0 8 8"
fill="none"
>
<path
d="M0.75 0.75L6.74995 6.74995"
stroke="white"
strokeWidth="1.5"
strokeLinecap="round"
/>
<path
d="M6.75 0.75L0.750025 6.74992"
stroke="white"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg> </svg>
</button> </button>
</div> </div>
@ -1041,7 +1072,8 @@ export const PromptInputSubmit = ({
// 判断是否有内容可发送 // 判断是否有内容可发送
const hasContent = controller const hasContent = controller
? controller.textInput.value.trim().length > 0 || controller.attachments.files.length > 0 ? controller.textInput.value.trim().length > 0 ||
controller.attachments.files.length > 0
: false; : false;
// 正在 streaming 时不允许发送 // 正在 streaming 时不允许发送
@ -1064,11 +1096,11 @@ export const PromptInputSubmit = ({
aria-label="Submit" aria-label="Submit"
// 被button{bgc:#fff}覆盖了,只能加"!" // 被button{bgc:#fff}覆盖了,只能加"!"
className={cn( className={cn(
'rounded-[10px] w-[140px] h-[40px] font-bold border-0 transition-all', "h-[40px] w-[140px] rounded-[10px] border-0 font-bold transition-all",
isDisabled isDisabled
? 'text-gray-400 !bg-gray-200 cursor-not-allowed' ? "cursor-not-allowed !bg-gray-200 text-gray-400"
: 'text-[#8E47F0] !bg-[#F0E8FB] hover:!bg-[#8E47F0] hover:text-[#FFFFFF]', : "!bg-[#F0E8FB] text-[#8E47F0] hover:!bg-[#8E47F0] hover:text-[#FFFFFF]",
className className,
)} )}
size={size} size={size}
type="submit" type="submit"

View File

@ -36,8 +36,8 @@ export type QueueItemProps = ComponentProps<"li">;
export const QueueItem = ({ className, ...props }: QueueItemProps) => ( export const QueueItem = ({ className, ...props }: QueueItemProps) => (
<li <li
className={cn( className={cn(
"group flex flex-col gap-1 rounded-md px-3 py-1 text-sm transition-colors hover:bg-muted", "group hover:bg-muted flex flex-col gap-1 rounded-md px-3 py-1 text-sm transition-colors",
className className,
)} )}
{...props} {...props}
/> />
@ -58,7 +58,7 @@ export const QueueItemIndicator = ({
completed completed
? "border-muted-foreground/20 bg-muted-foreground/10" ? "border-muted-foreground/20 bg-muted-foreground/10"
: "border-muted-foreground/50", : "border-muted-foreground/50",
className className,
)} )}
{...props} {...props}
/> />
@ -79,7 +79,7 @@ export const QueueItemContent = ({
completed completed
? "text-muted-foreground/50 line-through" ? "text-muted-foreground/50 line-through"
: "text-muted-foreground", : "text-muted-foreground",
className className,
)} )}
{...props} {...props}
/> />
@ -100,7 +100,7 @@ export const QueueItemDescription = ({
completed completed
? "text-muted-foreground/40 line-through" ? "text-muted-foreground/40 line-through"
: "text-muted-foreground", : "text-muted-foreground",
className className,
)} )}
{...props} {...props}
/> />
@ -126,8 +126,8 @@ export const QueueItemAction = ({
}: QueueItemActionProps) => ( }: QueueItemActionProps) => (
<Button <Button
className={cn( className={cn(
"size-auto rounded p-1 text-muted-foreground opacity-0 transition-opacity hover:bg-muted-foreground/10 hover:text-foreground group-hover:opacity-100", "text-muted-foreground hover:bg-muted-foreground/10 hover:text-foreground size-auto rounded p-1 opacity-0 transition-opacity group-hover:opacity-100",
className className,
)} )}
size="icon" size="icon"
type="button" type="button"
@ -169,8 +169,8 @@ export const QueueItemFile = ({
}: QueueItemFileProps) => ( }: QueueItemFileProps) => (
<span <span
className={cn( className={cn(
"flex items-center gap-1 rounded border bg-muted px-2 py-1 text-xs", "bg-muted flex items-center gap-1 rounded border px-2 py-1 text-xs",
className className,
)} )}
{...props} {...props}
> >
@ -215,8 +215,8 @@ export const QueueSectionTrigger = ({
<CollapsibleTrigger asChild> <CollapsibleTrigger asChild>
<button <button
className={cn( className={cn(
"group flex w-full items-center justify-between rounded-md bg-muted/40 px-3 py-2 text-left font-medium text-muted-foreground text-sm transition-colors hover:bg-muted", "group bg-muted/40 text-muted-foreground hover:bg-muted flex w-full items-center justify-between rounded-md px-3 py-2 text-left text-sm font-medium transition-colors",
className className,
)} )}
type="button" type="button"
{...props} {...props}
@ -241,7 +241,7 @@ export const QueueSectionLabel = ({
...props ...props
}: QueueSectionLabelProps) => ( }: QueueSectionLabelProps) => (
<span className={cn("flex items-center gap-2", className)} {...props}> <span className={cn("flex items-center gap-2", className)} {...props}>
<ChevronDownIcon className="group-data-[state=closed]:-rotate-90 size-4 transition-transform" /> <ChevronDownIcon className="size-4 transition-transform group-data-[state=closed]:-rotate-90" />
{icon} {icon}
<span> <span>
{count} {label} {count} {label}
@ -266,8 +266,8 @@ export type QueueProps = ComponentProps<"div">;
export const Queue = ({ className, ...props }: QueueProps) => ( export const Queue = ({ className, ...props }: QueueProps) => (
<div <div
className={cn( className={cn(
"flex flex-col gap-2 rounded-xl border border-border bg-background px-3 pt-2 pb-2 shadow-xs", "border-border bg-background flex flex-col gap-2 rounded-xl border px-3 pt-2 pb-2 shadow-xs",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -108,10 +108,12 @@ export const Reasoning = memo(
</Collapsible> </Collapsible>
</ReasoningContext.Provider> </ReasoningContext.Provider>
); );
} },
); );
export type ReasoningTriggerProps = ComponentProps<typeof CollapsibleTrigger> & { export type ReasoningTriggerProps = ComponentProps<
typeof CollapsibleTrigger
> & {
getThinkingMessage?: (isStreaming: boolean, duration?: number) => ReactNode; getThinkingMessage?: (isStreaming: boolean, duration?: number) => ReactNode;
}; };
@ -126,14 +128,19 @@ const defaultGetThinkingMessage = (isStreaming: boolean, duration?: number) => {
}; };
export const ReasoningTrigger = memo( export const ReasoningTrigger = memo(
({ className, children, getThinkingMessage = defaultGetThinkingMessage, ...props }: ReasoningTriggerProps) => { ({
className,
children,
getThinkingMessage = defaultGetThinkingMessage,
...props
}: ReasoningTriggerProps) => {
const { isStreaming, isOpen, duration } = useReasoning(); const { isStreaming, isOpen, duration } = useReasoning();
return ( return (
<CollapsibleTrigger <CollapsibleTrigger
className={cn( className={cn(
"flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground", "text-muted-foreground hover:text-foreground flex w-full items-center gap-2 text-sm transition-colors",
className className,
)} )}
{...props} {...props}
> >
@ -144,14 +151,14 @@ export const ReasoningTrigger = memo(
<ChevronDownIcon <ChevronDownIcon
className={cn( className={cn(
"size-4 transition-transform", "size-4 transition-transform",
isOpen ? "rotate-180" : "rotate-0" isOpen ? "rotate-180" : "rotate-0",
)} )}
/> />
</> </>
)} )}
</CollapsibleTrigger> </CollapsibleTrigger>
); );
} },
); );
export type ReasoningContentProps = ComponentProps< export type ReasoningContentProps = ComponentProps<
@ -165,14 +172,14 @@ export const ReasoningContent = memo(
<CollapsibleContent <CollapsibleContent
className={cn( className={cn(
"mt-4 text-sm", "mt-4 text-sm",
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-muted-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in", "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-muted-foreground data-[state=closed]:animate-out data-[state=open]:animate-in outline-none",
className className,
)} )}
{...props} {...props}
> >
<Streamdown {...props}>{children}</Streamdown> <Streamdown {...props}>{children}</Streamdown>
</CollapsibleContent> </CollapsibleContent>
) ),
); );
Reasoning.displayName = "Reasoning"; Reasoning.displayName = "Reasoning";

View File

@ -26,12 +26,12 @@ const ShimmerComponent = ({
spread = 2, spread = 2,
}: TextShimmerProps) => { }: TextShimmerProps) => {
const MotionComponent = motion.create( const MotionComponent = motion.create(
Component as keyof JSX.IntrinsicElements Component as keyof JSX.IntrinsicElements,
); );
const dynamicSpread = useMemo( const dynamicSpread = useMemo(
() => (children?.length ?? 0) * spread, () => (children?.length ?? 0) * spread,
[children, spread] [children, spread],
); );
return ( return (
@ -39,8 +39,8 @@ const ShimmerComponent = ({
animate={{ backgroundPosition: "0% center" }} animate={{ backgroundPosition: "0% center" }}
className={cn( className={cn(
"relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent", "relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent",
"[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]", "[background-repeat:no-repeat,padding-box] [--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))]",
className className,
)} )}
initial={{ backgroundPosition: "100% center" }} initial={{ backgroundPosition: "100% center" }}
style={ style={

View File

@ -13,7 +13,7 @@ export type SourcesProps = ComponentProps<"div">;
export const Sources = ({ className, ...props }: SourcesProps) => ( export const Sources = ({ className, ...props }: SourcesProps) => (
<Collapsible <Collapsible
className={cn("not-prose mb-4 text-primary text-xs", className)} className={cn("not-prose text-primary mb-4 text-xs", className)}
{...props} {...props}
/> />
); );
@ -50,8 +50,8 @@ export const SourcesContent = ({
<CollapsibleContent <CollapsibleContent
className={cn( className={cn(
"mt-3 flex w-fit flex-col gap-2", "mt-3 flex w-fit flex-col gap-2",
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 outline-none data-[state=closed]:animate-out data-[state=open]:animate-in", "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 data-[state=closed]:animate-out data-[state=open]:animate-in outline-none",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -18,8 +18,8 @@ export const TaskItemFile = ({
}: TaskItemFileProps) => ( }: TaskItemFileProps) => (
<div <div
className={cn( className={cn(
"inline-flex items-center gap-1 rounded-md border bg-secondary px-1.5 py-0.5 text-foreground text-xs", "bg-secondary text-foreground inline-flex items-center gap-1 rounded-md border px-1.5 py-0.5 text-xs",
className className,
)} )}
{...props} {...props}
> >
@ -57,7 +57,7 @@ export const TaskTrigger = ({
}: TaskTriggerProps) => ( }: TaskTriggerProps) => (
<CollapsibleTrigger asChild className={cn("group", className)} {...props}> <CollapsibleTrigger asChild className={cn("group", className)} {...props}>
{children ?? ( {children ?? (
<div className="flex w-full cursor-pointer items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground"> <div className="text-muted-foreground hover:text-foreground flex w-full cursor-pointer items-center gap-2 text-sm transition-colors">
<SearchIcon className="size-4" /> <SearchIcon className="size-4" />
<p className="text-sm">{title}</p> <p className="text-sm">{title}</p>
<ChevronDownIcon className="size-4 transition-transform group-data-[state=open]:rotate-180" /> <ChevronDownIcon className="size-4 transition-transform group-data-[state=open]:rotate-180" />
@ -75,12 +75,12 @@ export const TaskContent = ({
}: TaskContentProps) => ( }: TaskContentProps) => (
<CollapsibleContent <CollapsibleContent
className={cn( className={cn(
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in", "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground data-[state=closed]:animate-out data-[state=open]:animate-in outline-none",
className className,
)} )}
{...props} {...props}
> >
<div className="mt-4 space-y-2 border-muted border-l-2 pl-4"> <div className="border-muted mt-4 space-y-2 border-l-2 pl-4">
{children} {children}
</div> </div>
</CollapsibleContent> </CollapsibleContent>

View File

@ -7,8 +7,8 @@ type ToolbarProps = ComponentProps<typeof NodeToolbar>;
export const Toolbar = ({ className, ...props }: ToolbarProps) => ( export const Toolbar = ({ className, ...props }: ToolbarProps) => (
<NodeToolbar <NodeToolbar
className={cn( className={cn(
"flex items-center gap-1 rounded-sm border bg-background p-1.5", "bg-background flex items-center gap-1 rounded-sm border p-1.5",
className className,
)} )}
position={Position.Bottom} position={Position.Bottom}
{...props} {...props}

View File

@ -66,8 +66,8 @@ export const WebPreview = ({
<WebPreviewContext.Provider value={contextValue}> <WebPreviewContext.Provider value={contextValue}>
<div <div
className={cn( className={cn(
"flex size-full flex-col rounded-lg border bg-card", "bg-card flex size-full flex-col rounded-lg border",
className className,
)} )}
{...props} {...props}
> >
@ -107,7 +107,7 @@ export const WebPreviewNavigationButton = ({
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
className="h-8 w-8 p-0 hover:text-foreground" className="hover:text-foreground h-8 w-8 p-0"
disabled={disabled} disabled={disabled}
onClick={onClick} onClick={onClick}
size="sm" size="sm"
@ -209,21 +209,21 @@ export const WebPreviewConsole = ({
return ( return (
<Collapsible <Collapsible
className={cn("border-t bg-muted/50 font-mono text-sm", className)} className={cn("bg-muted/50 border-t font-mono text-sm", className)}
onOpenChange={setConsoleOpen} onOpenChange={setConsoleOpen}
open={consoleOpen} open={consoleOpen}
{...props} {...props}
> >
<CollapsibleTrigger asChild> <CollapsibleTrigger asChild>
<Button <Button
className="flex w-full items-center justify-between p-4 text-left font-medium hover:bg-muted/50" className="hover:bg-muted/50 flex w-full items-center justify-between p-4 text-left font-medium"
variant="ghost" variant="ghost"
> >
Console Console
<ChevronDownIcon <ChevronDownIcon
className={cn( className={cn(
"h-4 w-4 transition-transform duration-200", "h-4 w-4 transition-transform duration-200",
consoleOpen && "rotate-180" consoleOpen && "rotate-180",
)} )}
/> />
</Button> </Button>
@ -231,7 +231,7 @@ export const WebPreviewConsole = ({
<CollapsibleContent <CollapsibleContent
className={cn( className={cn(
"px-4 pb-4", "px-4 pb-4",
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 outline-none data-[state=closed]:animate-out data-[state=open]:animate-in" "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=open]:animate-in outline-none",
)} )}
> >
<div className="max-h-48 space-y-1 overflow-y-auto"> <div className="max-h-48 space-y-1 overflow-y-auto">
@ -244,7 +244,7 @@ export const WebPreviewConsole = ({
"text-xs", "text-xs",
log.level === "error" && "text-destructive", log.level === "error" && "text-destructive",
log.level === "warn" && "text-yellow-600", log.level === "warn" && "text-yellow-600",
log.level === "log" && "text-foreground" log.level === "log" && "text-foreground",
)} )}
key={`${log.timestamp.getTime()}-${index}`} key={`${log.timestamp.getTime()}-${index}`}
> >

View File

@ -1,7 +1,7 @@
import * as React from "react" import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const alertVariants = cva( const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
@ -16,8 +16,8 @@ const alertVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
) );
function Alert({ function Alert({
className, className,
@ -31,7 +31,7 @@ function Alert({
className={cn(alertVariants({ variant }), className)} className={cn(alertVariants({ variant }), className)}
{...props} {...props}
/> />
) );
} }
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
@ -40,11 +40,11 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
data-slot="alert-title" data-slot="alert-title"
className={cn( className={cn(
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function AlertDescription({ function AlertDescription({
@ -56,11 +56,11 @@ function AlertDescription({
data-slot="alert-description" data-slot="alert-description"
className={cn( className={cn(
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed", "text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
export { Alert, AlertTitle, AlertDescription } export { Alert, AlertTitle, AlertDescription };

View File

@ -1,12 +1,12 @@
"use client" "use client";
import React, { memo } from "react" import React, { memo } from "react";
interface AuroraTextProps { interface AuroraTextProps {
children: React.ReactNode children: React.ReactNode;
className?: string className?: string;
colors?: string[] colors?: string[];
speed?: number speed?: number;
} }
export const AuroraText = memo( export const AuroraText = memo(
@ -23,7 +23,7 @@ export const AuroraText = memo(
WebkitBackgroundClip: "text", WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent", WebkitTextFillColor: "transparent",
animationDuration: `${10 / speed}s`, animationDuration: `${10 / speed}s`,
} };
return ( return (
<span className={`relative inline-block ${className}`}> <span className={`relative inline-block ${className}`}>
@ -36,8 +36,8 @@ export const AuroraText = memo(
{children} {children}
</span> </span>
</span> </span>
) );
} },
) );
AuroraText.displayName = "AuroraText" AuroraText.displayName = "AuroraText";

View File

@ -1,9 +1,9 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar" import * as AvatarPrimitive from "@radix-ui/react-avatar";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Avatar({ function Avatar({
className, className,
@ -14,11 +14,11 @@ function Avatar({
data-slot="avatar" data-slot="avatar"
className={cn( className={cn(
"relative flex size-8 shrink-0 overflow-hidden rounded-full", "relative flex size-8 shrink-0 overflow-hidden rounded-full",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function AvatarImage({ function AvatarImage({
@ -31,7 +31,7 @@ function AvatarImage({
className={cn("aspect-square size-full", className)} className={cn("aspect-square size-full", className)}
{...props} {...props}
/> />
) );
} }
function AvatarFallback({ function AvatarFallback({
@ -43,11 +43,11 @@ function AvatarFallback({
data-slot="avatar-fallback" data-slot="avatar-fallback"
className={cn( className={cn(
"bg-muted flex size-full items-center justify-center rounded-full", "bg-muted flex size-full items-center justify-center rounded-full",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
export { Avatar, AvatarImage, AvatarFallback } export { Avatar, AvatarImage, AvatarFallback };

View File

@ -1,8 +1,8 @@
import * as React from "react" import * as React from "react";
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const badgeVariants = cva( const badgeVariants = cva(
"inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", "inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
@ -22,8 +22,8 @@ const badgeVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
) );
function Badge({ function Badge({
className, className,
@ -32,7 +32,7 @@ function Badge({
...props ...props
}: React.ComponentProps<"span"> & }: React.ComponentProps<"span"> &
VariantProps<typeof badgeVariants> & { asChild?: boolean }) { VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
const Comp = asChild ? Slot : "span" const Comp = asChild ? Slot : "span";
return ( return (
<Comp <Comp
@ -40,7 +40,7 @@ function Badge({
className={cn(badgeVariants({ variant }), className)} className={cn(badgeVariants({ variant }), className)}
{...props} {...props}
/> />
) );
} }
export { Badge, badgeVariants } export { Badge, badgeVariants };

View File

@ -1,11 +1,11 @@
import * as React from "react" import * as React from "react";
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot";
import { ChevronRight, MoreHorizontal } from "lucide-react" import { ChevronRight, MoreHorizontal } from "lucide-react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} /> return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />;
} }
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) { function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
@ -14,11 +14,11 @@ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
data-slot="breadcrumb-list" data-slot="breadcrumb-list"
className={cn( className={cn(
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5", "text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) { function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
@ -28,7 +28,7 @@ function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
className={cn("inline-flex items-center gap-1.5", className)} className={cn("inline-flex items-center gap-1.5", className)}
{...props} {...props}
/> />
) );
} }
function BreadcrumbLink({ function BreadcrumbLink({
@ -36,9 +36,9 @@ function BreadcrumbLink({
className, className,
...props ...props
}: React.ComponentProps<"a"> & { }: React.ComponentProps<"a"> & {
asChild?: boolean asChild?: boolean;
}) { }) {
const Comp = asChild ? Slot : "a" const Comp = asChild ? Slot : "a";
return ( return (
<Comp <Comp
@ -46,7 +46,7 @@ function BreadcrumbLink({
className={cn("hover:text-foreground transition-colors", className)} className={cn("hover:text-foreground transition-colors", className)}
{...props} {...props}
/> />
) );
} }
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) { function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
@ -59,7 +59,7 @@ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
className={cn("text-foreground font-normal", className)} className={cn("text-foreground font-normal", className)}
{...props} {...props}
/> />
) );
} }
function BreadcrumbSeparator({ function BreadcrumbSeparator({
@ -77,7 +77,7 @@ function BreadcrumbSeparator({
> >
{children ?? <ChevronRight />} {children ?? <ChevronRight />}
</li> </li>
) );
} }
function BreadcrumbEllipsis({ function BreadcrumbEllipsis({
@ -95,7 +95,7 @@ function BreadcrumbEllipsis({
<MoreHorizontal className="size-4" /> <MoreHorizontal className="size-4" />
<span className="sr-only">More</span> <span className="sr-only">More</span>
</span> </span>
) );
} }
export { export {
@ -106,4 +106,4 @@ export {
BreadcrumbPage, BreadcrumbPage,
BreadcrumbSeparator, BreadcrumbSeparator,
BreadcrumbEllipsis, BreadcrumbEllipsis,
} };

View File

@ -1,8 +1,8 @@
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator" import { Separator } from "@/components/ui/separator";
const buttonGroupVariants = cva( const buttonGroupVariants = cva(
"flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2", "flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2",
@ -18,8 +18,8 @@ const buttonGroupVariants = cva(
defaultVariants: { defaultVariants: {
orientation: "horizontal", orientation: "horizontal",
}, },
} },
) );
function ButtonGroup({ function ButtonGroup({
className, className,
@ -34,7 +34,7 @@ function ButtonGroup({
className={cn(buttonGroupVariants({ orientation }), className)} className={cn(buttonGroupVariants({ orientation }), className)}
{...props} {...props}
/> />
) );
} }
function ButtonGroupText({ function ButtonGroupText({
@ -42,19 +42,19 @@ function ButtonGroupText({
asChild = false, asChild = false,
...props ...props
}: React.ComponentProps<"div"> & { }: React.ComponentProps<"div"> & {
asChild?: boolean asChild?: boolean;
}) { }) {
const Comp = asChild ? Slot : "div" const Comp = asChild ? Slot : "div";
return ( return (
<Comp <Comp
className={cn( className={cn(
"bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4", "bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function ButtonGroupSeparator({ function ButtonGroupSeparator({
@ -68,11 +68,11 @@ function ButtonGroupSeparator({
orientation={orientation} orientation={orientation}
className={cn( className={cn(
"bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto", "bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
export { export {
@ -80,4 +80,4 @@ export {
ButtonGroupSeparator, ButtonGroupSeparator,
ButtonGroupText, ButtonGroupText,
buttonGroupVariants, buttonGroupVariants,
} };

View File

@ -1,6 +1,6 @@
import * as React from "react" import * as React from "react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Card({ className, ...props }: React.ComponentProps<"div">) { function Card({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
@ -8,11 +8,11 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card" data-slot="card"
className={cn( className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-[20px] not-first:px-[20px] not-first:py-[15px]", "bg-card text-card-foreground flex flex-col gap-6 rounded-[20px] not-first:px-[20px] not-first:py-[15px]",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function CardHeader({ className, ...props }: React.ComponentProps<"div">) { function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
@ -21,11 +21,11 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-header" data-slot="card-header"
className={cn( className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function CardTitle({ className, ...props }: React.ComponentProps<"div">) { function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
@ -35,7 +35,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
className={cn("leading-none font-semibold", className)} className={cn("leading-none font-semibold", className)}
{...props} {...props}
/> />
) );
} }
function CardDescription({ className, ...props }: React.ComponentProps<"div">) { function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
@ -45,7 +45,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
className={cn("text-muted-foreground text-sm", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
) );
} }
function CardAction({ className, ...props }: React.ComponentProps<"div">) { function CardAction({ className, ...props }: React.ComponentProps<"div">) {
@ -54,11 +54,11 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-action" data-slot="card-action"
className={cn( className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end", "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function CardContent({ className, ...props }: React.ComponentProps<"div">) { function CardContent({ className, ...props }: React.ComponentProps<"div">) {
@ -68,7 +68,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
className={cn("px-6", className)} className={cn("px-6", className)}
{...props} {...props}
/> />
) );
} }
function CardFooter({ className, ...props }: React.ComponentProps<"div">) { function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
@ -78,7 +78,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
className={cn("flex items-center px-6 [.border-t]:pt-6", className)} className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
{...props} {...props}
/> />
) );
} }
export { export {
@ -89,4 +89,4 @@ export {
CardAction, CardAction,
CardDescription, CardDescription,
CardContent, CardContent,
} };

View File

@ -1,45 +1,45 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import useEmblaCarousel, { import useEmblaCarousel, {
type UseEmblaCarouselType, type UseEmblaCarouselType,
} from "embla-carousel-react" } from "embla-carousel-react";
import { ArrowLeft, ArrowRight } from "lucide-react" import { ArrowLeft, ArrowRight } from "lucide-react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button";
type CarouselApi = UseEmblaCarouselType[1] type CarouselApi = UseEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel> type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
type CarouselOptions = UseCarouselParameters[0] type CarouselOptions = UseCarouselParameters[0];
type CarouselPlugin = UseCarouselParameters[1] type CarouselPlugin = UseCarouselParameters[1];
type CarouselProps = { type CarouselProps = {
opts?: CarouselOptions opts?: CarouselOptions;
plugins?: CarouselPlugin plugins?: CarouselPlugin;
orientation?: "horizontal" | "vertical" orientation?: "horizontal" | "vertical";
setApi?: (api: CarouselApi) => void setApi?: (api: CarouselApi) => void;
} };
type CarouselContextProps = { type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0] carouselRef: ReturnType<typeof useEmblaCarousel>[0];
api: ReturnType<typeof useEmblaCarousel>[1] api: ReturnType<typeof useEmblaCarousel>[1];
scrollPrev: () => void scrollPrev: () => void;
scrollNext: () => void scrollNext: () => void;
canScrollPrev: boolean canScrollPrev: boolean;
canScrollNext: boolean canScrollNext: boolean;
} & CarouselProps } & CarouselProps;
const CarouselContext = React.createContext<CarouselContextProps | null>(null) const CarouselContext = React.createContext<CarouselContextProps | null>(null);
function useCarousel() { function useCarousel() {
const context = React.useContext(CarouselContext) const context = React.useContext(CarouselContext);
if (!context) { if (!context) {
throw new Error("useCarousel must be used within a <Carousel />") throw new Error("useCarousel must be used within a <Carousel />");
} }
return context return context;
} }
function Carousel({ function Carousel({
@ -56,53 +56,53 @@ function Carousel({
...opts, ...opts,
axis: orientation === "horizontal" ? "x" : "y", axis: orientation === "horizontal" ? "x" : "y",
}, },
plugins plugins,
) );
const [canScrollPrev, setCanScrollPrev] = React.useState(false) const [canScrollPrev, setCanScrollPrev] = React.useState(false);
const [canScrollNext, setCanScrollNext] = React.useState(false) const [canScrollNext, setCanScrollNext] = React.useState(false);
const onSelect = React.useCallback((api: CarouselApi) => { const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) return if (!api) return;
setCanScrollPrev(api.canScrollPrev()) setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext()) setCanScrollNext(api.canScrollNext());
}, []) }, []);
const scrollPrev = React.useCallback(() => { const scrollPrev = React.useCallback(() => {
api?.scrollPrev() api?.scrollPrev();
}, [api]) }, [api]);
const scrollNext = React.useCallback(() => { const scrollNext = React.useCallback(() => {
api?.scrollNext() api?.scrollNext();
}, [api]) }, [api]);
const handleKeyDown = React.useCallback( const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => { (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") { if (event.key === "ArrowLeft") {
event.preventDefault() event.preventDefault();
scrollPrev() scrollPrev();
} else if (event.key === "ArrowRight") { } else if (event.key === "ArrowRight") {
event.preventDefault() event.preventDefault();
scrollNext() scrollNext();
} }
}, },
[scrollPrev, scrollNext] [scrollPrev, scrollNext],
) );
React.useEffect(() => { React.useEffect(() => {
if (!api || !setApi) return if (!api || !setApi) return;
setApi(api) setApi(api);
}, [api, setApi]) }, [api, setApi]);
React.useEffect(() => { React.useEffect(() => {
if (!api) return if (!api) return;
onSelect(api) onSelect(api);
api.on("reInit", onSelect) api.on("reInit", onSelect);
api.on("select", onSelect) api.on("select", onSelect);
return () => { return () => {
api?.off("select", onSelect) api?.off("select", onSelect);
} };
}, [api, onSelect]) }, [api, onSelect]);
return ( return (
<CarouselContext.Provider <CarouselContext.Provider
@ -129,11 +129,11 @@ function Carousel({
{children} {children}
</div> </div>
</CarouselContext.Provider> </CarouselContext.Provider>
) );
} }
function CarouselContent({ className, ...props }: React.ComponentProps<"div">) { function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
const { carouselRef, orientation } = useCarousel() const { carouselRef, orientation } = useCarousel();
return ( return (
<div <div
@ -145,16 +145,16 @@ function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
className={cn( className={cn(
"flex", "flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className className,
)} )}
{...props} {...props}
/> />
</div> </div>
) );
} }
function CarouselItem({ className, ...props }: React.ComponentProps<"div">) { function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
const { orientation } = useCarousel() const { orientation } = useCarousel();
return ( return (
<div <div
@ -164,11 +164,11 @@ function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
className={cn( className={cn(
"min-w-0 shrink-0 grow-0 basis-full", "min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4", orientation === "horizontal" ? "pl-4" : "pt-4",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function CarouselPrevious({ function CarouselPrevious({
@ -177,7 +177,7 @@ function CarouselPrevious({
size = "icon", size = "icon",
...props ...props
}: React.ComponentProps<typeof Button>) { }: React.ComponentProps<typeof Button>) {
const { orientation, scrollPrev, canScrollPrev } = useCarousel() const { orientation, scrollPrev, canScrollPrev } = useCarousel();
return ( return (
<Button <Button
@ -189,7 +189,7 @@ function CarouselPrevious({
orientation === "horizontal" orientation === "horizontal"
? "top-1/2 -left-12 -translate-y-1/2" ? "top-1/2 -left-12 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90", : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
className className,
)} )}
disabled={!canScrollPrev} disabled={!canScrollPrev}
onClick={scrollPrev} onClick={scrollPrev}
@ -198,7 +198,7 @@ function CarouselPrevious({
<ArrowLeft /> <ArrowLeft />
<span className="sr-only">Previous slide</span> <span className="sr-only">Previous slide</span>
</Button> </Button>
) );
} }
function CarouselNext({ function CarouselNext({
@ -207,7 +207,7 @@ function CarouselNext({
size = "icon", size = "icon",
...props ...props
}: React.ComponentProps<typeof Button>) { }: React.ComponentProps<typeof Button>) {
const { orientation, scrollNext, canScrollNext } = useCarousel() const { orientation, scrollNext, canScrollNext } = useCarousel();
return ( return (
<Button <Button
@ -219,7 +219,7 @@ function CarouselNext({
orientation === "horizontal" orientation === "horizontal"
? "top-1/2 -right-12 -translate-y-1/2" ? "top-1/2 -right-12 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
className className,
)} )}
disabled={!canScrollNext} disabled={!canScrollNext}
onClick={scrollNext} onClick={scrollNext}
@ -228,7 +228,7 @@ function CarouselNext({
<ArrowRight /> <ArrowRight />
<span className="sr-only">Next slide</span> <span className="sr-only">Next slide</span>
</Button> </Button>
) );
} }
export { export {
@ -238,4 +238,4 @@ export {
CarouselItem, CarouselItem,
CarouselPrevious, CarouselPrevious,
CarouselNext, CarouselNext,
} };

View File

@ -1,17 +1,17 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { Command as CommandPrimitive } from "cmdk" import { Command as CommandPrimitive } from "cmdk";
import { SearchIcon } from "lucide-react" import { SearchIcon } from "lucide-react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "@/components/ui/dialog" } from "@/components/ui/dialog";
function Command({ function Command({
className, className,
@ -22,11 +22,11 @@ function Command({
data-slot="command" data-slot="command"
className={cn( className={cn(
"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md", "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function CommandDialog({ function CommandDialog({
@ -37,10 +37,10 @@ function CommandDialog({
showCloseButton = true, showCloseButton = true,
...props ...props
}: React.ComponentProps<typeof Dialog> & { }: React.ComponentProps<typeof Dialog> & {
title?: string title?: string;
description?: string description?: string;
className?: string className?: string;
showCloseButton?: boolean showCloseButton?: boolean;
}) { }) {
return ( return (
<Dialog {...props}> <Dialog {...props}>
@ -57,7 +57,7 @@ function CommandDialog({
</Command> </Command>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
) );
} }
function CommandInput({ function CommandInput({
@ -74,12 +74,12 @@ function CommandInput({
data-slot="command-input" data-slot="command-input"
className={cn( className={cn(
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50", "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
{...props} {...props}
/> />
</div> </div>
) );
} }
function CommandList({ function CommandList({
@ -91,11 +91,11 @@ function CommandList({
data-slot="command-list" data-slot="command-list"
className={cn( className={cn(
"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function CommandEmpty({ function CommandEmpty({
@ -107,7 +107,7 @@ function CommandEmpty({
className="py-6 text-center text-sm" className="py-6 text-center text-sm"
{...props} {...props}
/> />
) );
} }
function CommandGroup({ function CommandGroup({
@ -119,11 +119,11 @@ function CommandGroup({
data-slot="command-group" data-slot="command-group"
className={cn( className={cn(
"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium", "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function CommandSeparator({ function CommandSeparator({
@ -136,7 +136,7 @@ function CommandSeparator({
className={cn("bg-border -mx-1 h-px", className)} className={cn("bg-border -mx-1 h-px", className)}
{...props} {...props}
/> />
) );
} }
function CommandItem({ function CommandItem({
@ -148,11 +148,11 @@ function CommandItem({
data-slot="command-item" data-slot="command-item"
className={cn( className={cn(
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function CommandShortcut({ function CommandShortcut({
@ -164,11 +164,11 @@ function CommandShortcut({
data-slot="command-shortcut" data-slot="command-shortcut"
className={cn( className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest", "text-muted-foreground ml-auto text-xs tracking-widest",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
export { export {
@ -181,4 +181,4 @@ export {
CommandItem, CommandItem,
CommandShortcut, CommandShortcut,
CommandSeparator, CommandSeparator,
} };

View File

@ -1,33 +1,33 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog" import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react" import { XIcon } from "lucide-react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function DevDialog({ function DevDialog({
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) { }: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dev-dialog" {...props} /> return <DialogPrimitive.Root data-slot="dev-dialog" {...props} />;
} }
function DevDialogTrigger({ function DevDialogTrigger({
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) { }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dev-dialog-trigger" {...props} /> return <DialogPrimitive.Trigger data-slot="dev-dialog-trigger" {...props} />;
} }
function DevDialogPortal({ function DevDialogPortal({
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) { }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dev-dialog-portal" {...props} /> return <DialogPrimitive.Portal data-slot="dev-dialog-portal" {...props} />;
} }
function DevDialogClose({ function DevDialogClose({
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) { }: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dev-dialog-close" {...props} /> return <DialogPrimitive.Close data-slot="dev-dialog-close" {...props} />;
} }
function DevDialogOverlay({ function DevDialogOverlay({
@ -39,11 +39,11 @@ function DevDialogOverlay({
data-slot="dev-dialog-overlay" data-slot="dev-dialog-overlay"
className={cn( className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function DevDialogContent({ function DevDialogContent({
@ -52,7 +52,7 @@ function DevDialogContent({
showCloseButton = true, showCloseButton = true,
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & { }: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean showCloseButton?: boolean;
}) { }) {
return ( return (
<DevDialogPortal data-slot="dev-dialog-portal"> <DevDialogPortal data-slot="dev-dialog-portal">
@ -60,8 +60,8 @@ function DevDialogContent({
<DialogPrimitive.Content <DialogPrimitive.Content
data-slot="dev-dialog-content" data-slot="dev-dialog-content"
className={cn( className={cn(
"bg-[#ffffff] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-[400px] max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-[40px] shadow-lg duration-200 outline-none sm:max-w-lg", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-[400px] max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-[#ffffff] p-[40px] shadow-lg duration-200 outline-none sm:max-w-lg",
className className,
)} )}
{...props} {...props}
> >
@ -77,7 +77,7 @@ function DevDialogContent({
)} )}
</DialogPrimitive.Content> </DialogPrimitive.Content>
</DevDialogPortal> </DevDialogPortal>
) );
} }
function DevDialogHeader({ className, ...props }: React.ComponentProps<"div">) { function DevDialogHeader({ className, ...props }: React.ComponentProps<"div">) {
@ -87,7 +87,7 @@ function DevDialogHeader({ className, ...props }: React.ComponentProps<"div">) {
className={cn("flex flex-col gap-2 text-center sm:text-left", className)} className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props} {...props}
/> />
) );
} }
function DevDialogFooter({ className, ...props }: React.ComponentProps<"div">) { function DevDialogFooter({ className, ...props }: React.ComponentProps<"div">) {
@ -96,12 +96,12 @@ function DevDialogFooter({ className, ...props }: React.ComponentProps<"div">) {
data-slot="dev-dialog-footer" data-slot="dev-dialog-footer"
className={cn( className={cn(
// sm:justify-end // sm:justify-end
"grid grid-cols-2 w-full gap-[30px] justify-between sm:flex-row ", "grid w-full grid-cols-2 justify-between gap-[30px] sm:flex-row",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function DevDialogTitle({ function DevDialogTitle({
@ -114,7 +114,7 @@ function DevDialogTitle({
className={cn("text-lg leading-none font-semibold", className)} className={cn("text-lg leading-none font-semibold", className)}
{...props} {...props}
/> />
) );
} }
function DevDialogDescription({ function DevDialogDescription({
@ -127,7 +127,7 @@ function DevDialogDescription({
className={cn("text-muted-foreground text-sm", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
) );
} }
export { export {
@ -141,4 +141,4 @@ export {
DevDialogPortal, DevDialogPortal,
DevDialogTitle, DevDialogTitle,
DevDialogTrigger, DevDialogTrigger,
} };

View File

@ -1,33 +1,33 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog" import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react" import { XIcon } from "lucide-react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Dialog({ function Dialog({
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) { }: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} /> return <DialogPrimitive.Root data-slot="dialog" {...props} />;
} }
function DialogTrigger({ function DialogTrigger({
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) { }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} /> return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
} }
function DialogPortal({ function DialogPortal({
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) { }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} /> return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
} }
function DialogClose({ function DialogClose({
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) { }: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} /> return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
} }
function DialogOverlay({ function DialogOverlay({
@ -39,11 +39,11 @@ function DialogOverlay({
data-slot="dialog-overlay" data-slot="dialog-overlay"
className={cn( className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function DialogContent({ function DialogContent({
@ -52,7 +52,7 @@ function DialogContent({
showCloseButton = true, showCloseButton = true,
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & { }: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean showCloseButton?: boolean;
}) { }) {
return ( return (
<DialogPortal data-slot="dialog-portal"> <DialogPortal data-slot="dialog-portal">
@ -61,7 +61,7 @@ function DialogContent({
data-slot="dialog-content" data-slot="dialog-content"
className={cn( className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg", "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
className className,
)} )}
{...props} {...props}
> >
@ -77,7 +77,7 @@ function DialogContent({
)} )}
</DialogPrimitive.Content> </DialogPrimitive.Content>
</DialogPortal> </DialogPortal>
) );
} }
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
@ -87,7 +87,7 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
className={cn("flex flex-col gap-2 text-center sm:text-left", className)} className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props} {...props}
/> />
) );
} }
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
@ -96,11 +96,11 @@ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
data-slot="dialog-footer" data-slot="dialog-footer"
className={cn( className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function DialogTitle({ function DialogTitle({
@ -113,7 +113,7 @@ function DialogTitle({
className={cn("text-lg leading-none font-semibold", className)} className={cn("text-lg leading-none font-semibold", className)}
{...props} {...props}
/> />
) );
} }
function DialogDescription({ function DialogDescription({
@ -126,7 +126,7 @@ function DialogDescription({
className={cn("text-muted-foreground text-sm", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
) );
} }
export { export {
@ -140,4 +140,4 @@ export {
DialogPortal, DialogPortal,
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} };

View File

@ -1,15 +1,15 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function DropdownMenu({ function DropdownMenu({
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} /> return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
} }
function DropdownMenuPortal({ function DropdownMenuPortal({
@ -17,7 +17,7 @@ function DropdownMenuPortal({
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return ( return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} /> <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
) );
} }
function DropdownMenuTrigger({ function DropdownMenuTrigger({
@ -30,7 +30,7 @@ function DropdownMenuTrigger({
className={cn(className)} className={cn(className)}
{...props} {...props}
/> />
) );
} }
function DropdownMenuContent({ function DropdownMenuContent({
@ -45,12 +45,12 @@ function DropdownMenuContent({
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-[20px] border p-[20px] shadow-md", "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-[20px] border p-[20px] shadow-md",
className className,
)} )}
{...props} {...props}
/> />
</DropdownMenuPrimitive.Portal> </DropdownMenuPrimitive.Portal>
) );
} }
function DropdownMenuGroup({ function DropdownMenuGroup({
@ -58,7 +58,7 @@ function DropdownMenuGroup({
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return ( return (
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} /> <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
) );
} }
function DropdownMenuItem({ function DropdownMenuItem({
@ -67,8 +67,8 @@ function DropdownMenuItem({
variant = "default", variant = "default",
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & { }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean inset?: boolean;
variant?: "default" | "destructive" variant?: "default" | "destructive";
}) { }) {
return ( return (
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
@ -77,11 +77,11 @@ function DropdownMenuItem({
data-variant={variant} data-variant={variant}
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function DropdownMenuCheckboxItem({ function DropdownMenuCheckboxItem({
@ -95,7 +95,7 @@ function DropdownMenuCheckboxItem({
data-slot="dropdown-menu-checkbox-item" data-slot="dropdown-menu-checkbox-item"
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className,
)} )}
checked={checked} checked={checked}
{...props} {...props}
@ -107,7 +107,7 @@ function DropdownMenuCheckboxItem({
</span> </span>
{children} {children}
</DropdownMenuPrimitive.CheckboxItem> </DropdownMenuPrimitive.CheckboxItem>
) );
} }
function DropdownMenuRadioGroup({ function DropdownMenuRadioGroup({
@ -118,7 +118,7 @@ function DropdownMenuRadioGroup({
data-slot="dropdown-menu-radio-group" data-slot="dropdown-menu-radio-group"
{...props} {...props}
/> />
) );
} }
function DropdownMenuRadioItem({ function DropdownMenuRadioItem({
@ -131,7 +131,7 @@ function DropdownMenuRadioItem({
data-slot="dropdown-menu-radio-item" data-slot="dropdown-menu-radio-item"
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className,
)} )}
{...props} {...props}
> >
@ -142,7 +142,7 @@ function DropdownMenuRadioItem({
</span> </span>
{children} {children}
</DropdownMenuPrimitive.RadioItem> </DropdownMenuPrimitive.RadioItem>
) );
} }
function DropdownMenuLabel({ function DropdownMenuLabel({
@ -150,7 +150,7 @@ function DropdownMenuLabel({
inset, inset,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & { }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean inset?: boolean;
}) { }) {
return ( return (
<DropdownMenuPrimitive.Label <DropdownMenuPrimitive.Label
@ -158,11 +158,11 @@ function DropdownMenuLabel({
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function DropdownMenuSeparator({ function DropdownMenuSeparator({
@ -175,7 +175,7 @@ function DropdownMenuSeparator({
className={cn("bg-border -mx-1 my-1 h-px", className)} className={cn("bg-border -mx-1 my-1 h-px", className)}
{...props} {...props}
/> />
) );
} }
function DropdownMenuShortcut({ function DropdownMenuShortcut({
@ -187,17 +187,17 @@ function DropdownMenuShortcut({
data-slot="dropdown-menu-shortcut" data-slot="dropdown-menu-shortcut"
className={cn( className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest", "text-muted-foreground ml-auto text-xs tracking-widest",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function DropdownMenuSub({ function DropdownMenuSub({
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} /> return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
} }
function DropdownMenuSubTrigger({ function DropdownMenuSubTrigger({
@ -206,7 +206,7 @@ function DropdownMenuSubTrigger({
children, children,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean inset?: boolean;
}) { }) {
return ( return (
<DropdownMenuPrimitive.SubTrigger <DropdownMenuPrimitive.SubTrigger
@ -214,14 +214,14 @@ function DropdownMenuSubTrigger({
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
<ChevronRightIcon className="ml-auto size-4" /> <ChevronRightIcon className="ml-auto size-4" />
</DropdownMenuPrimitive.SubTrigger> </DropdownMenuPrimitive.SubTrigger>
) );
} }
function DropdownMenuSubContent({ function DropdownMenuSubContent({
@ -233,11 +233,11 @@ function DropdownMenuSubContent({
data-slot="dropdown-menu-sub-content" data-slot="dropdown-menu-sub-content"
className={cn( className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-[20px] border p-1 shadow-lg", "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-[20px] border p-1 shadow-lg",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
export { export {
@ -256,4 +256,4 @@ export {
DropdownMenuSub, DropdownMenuSub,
DropdownMenuSubTrigger, DropdownMenuSubTrigger,
DropdownMenuSubContent, DropdownMenuSubContent,
} };

View File

@ -31,12 +31,18 @@ export function DropdownSelector<T extends string>({
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger <DropdownMenuTrigger
className={triggerClassName ?? "border-none bg-transparent shadow-none select-none focus:outline-none"} className={
triggerClassName ??
"border-none bg-transparent shadow-none select-none focus:outline-none"
}
> >
{selectedOption?.label ?? value} {selectedOption?.label ?? value}
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent className={contentClassName}> <DropdownMenuContent className={contentClassName}>
<DropdownMenuRadioGroup value={value} onValueChange={(v) => onChange(v as T)}> <DropdownMenuRadioGroup
value={value}
onValueChange={(v) => onChange(v as T)}
>
{options.map((option) => ( {options.map((option) => (
<DropdownMenuRadioItem key={option.value} value={option.value}> <DropdownMenuRadioItem key={option.value} value={option.value}>
{option.label} {option.label}

View File

@ -1,6 +1,6 @@
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Empty({ className, ...props }: React.ComponentProps<"div">) { function Empty({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
@ -8,11 +8,11 @@ function Empty({ className, ...props }: React.ComponentProps<"div">) {
data-slot="empty" data-slot="empty"
className={cn( className={cn(
"flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12", "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) { function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
@ -21,11 +21,11 @@ function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
data-slot="empty-header" data-slot="empty-header"
className={cn( className={cn(
"flex max-w-sm flex-col items-center gap-2 text-center", "flex max-w-sm flex-col items-center gap-2 text-center",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
const emptyMediaVariants = cva( const emptyMediaVariants = cva(
@ -40,8 +40,8 @@ const emptyMediaVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
) );
function EmptyMedia({ function EmptyMedia({
className, className,
@ -55,7 +55,7 @@ function EmptyMedia({
className={cn(emptyMediaVariants({ variant, className }))} className={cn(emptyMediaVariants({ variant, className }))}
{...props} {...props}
/> />
) );
} }
function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) { function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
@ -65,7 +65,7 @@ function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
className={cn("text-lg font-medium tracking-tight", className)} className={cn("text-lg font-medium tracking-tight", className)}
{...props} {...props}
/> />
) );
} }
function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) { function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
@ -74,11 +74,11 @@ function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
data-slot="empty-description" data-slot="empty-description"
className={cn( className={cn(
"text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4", "text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function EmptyContent({ className, ...props }: React.ComponentProps<"div">) { function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
@ -87,11 +87,11 @@ function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
data-slot="empty-content" data-slot="empty-content"
className={cn( className={cn(
"flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance", "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
export { export {
@ -101,4 +101,4 @@ export {
EmptyDescription, EmptyDescription,
EmptyContent, EmptyContent,
EmptyMedia, EmptyMedia,
} };

View File

@ -1,14 +1,14 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as HoverCardPrimitive from "@radix-ui/react-hover-card" import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function HoverCard({ function HoverCard({
...props ...props
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) { }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} /> return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
} }
function HoverCardTrigger({ function HoverCardTrigger({
@ -16,7 +16,7 @@ function HoverCardTrigger({
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) { }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
return ( return (
<HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} /> <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
) );
} }
function HoverCardContent({ function HoverCardContent({
@ -33,12 +33,12 @@ function HoverCardContent({
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden", "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
className className,
)} )}
{...props} {...props}
/> />
</HoverCardPrimitive.Portal> </HoverCardPrimitive.Portal>
) );
} }
export { HoverCard, HoverCardTrigger, HoverCardContent } export { HoverCard, HoverCardTrigger, HoverCardContent };

View File

@ -14,7 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
data-slot="input-group" data-slot="input-group"
role="group" role="group"
className={cn( className={cn(
"group/input-group overflow-hidden border-input/50 dark:bg-background/80 relative flex w-full items-center rounded-md border transition-[color,box-shadow] outline-none", "group/input-group border-input/50 dark:bg-background/80 relative flex w-full items-center overflow-hidden rounded-md border transition-[color,box-shadow] outline-none",
"h-9 min-w-0 has-[>textarea]:h-auto", "h-9 min-w-0 has-[>textarea]:h-auto",
// Variants based on alignment. // Variants based on alignment.

View File

@ -1,9 +1,9 @@
import * as React from "react" import * as React from "react";
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator" import { Separator } from "@/components/ui/separator";
function ItemGroup({ className, ...props }: React.ComponentProps<"div">) { function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
@ -13,7 +13,7 @@ function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
className={cn("group/item-group flex flex-col", className)} className={cn("group/item-group flex flex-col", className)}
{...props} {...props}
/> />
) );
} }
function ItemSeparator({ function ItemSeparator({
@ -27,7 +27,7 @@ function ItemSeparator({
className={cn("my-0", className)} className={cn("my-0", className)}
{...props} {...props}
/> />
) );
} }
const itemVariants = cva( const itemVariants = cva(
@ -48,8 +48,8 @@ const itemVariants = cva(
variant: "default", variant: "default",
size: "default", size: "default",
}, },
} },
) );
function Item({ function Item({
className, className,
@ -59,7 +59,7 @@ function Item({
...props ...props
}: React.ComponentProps<"div"> & }: React.ComponentProps<"div"> &
VariantProps<typeof itemVariants> & { asChild?: boolean }) { VariantProps<typeof itemVariants> & { asChild?: boolean }) {
const Comp = asChild ? Slot : "div" const Comp = asChild ? Slot : "div";
return ( return (
<Comp <Comp
data-slot="item" data-slot="item"
@ -68,7 +68,7 @@ function Item({
className={cn(itemVariants({ variant, size, className }))} className={cn(itemVariants({ variant, size, className }))}
{...props} {...props}
/> />
) );
} }
const itemMediaVariants = cva( const itemMediaVariants = cva(
@ -85,8 +85,8 @@ const itemMediaVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
) );
function ItemMedia({ function ItemMedia({
className, className,
@ -100,7 +100,7 @@ function ItemMedia({
className={cn(itemMediaVariants({ variant, className }))} className={cn(itemMediaVariants({ variant, className }))}
{...props} {...props}
/> />
) );
} }
function ItemContent({ className, ...props }: React.ComponentProps<"div">) { function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
@ -109,11 +109,11 @@ function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
data-slot="item-content" data-slot="item-content"
className={cn( className={cn(
"flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none", "flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function ItemTitle({ className, ...props }: React.ComponentProps<"div">) { function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
@ -122,11 +122,11 @@ function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
data-slot="item-title" data-slot="item-title"
className={cn( className={cn(
"flex w-fit items-center gap-2 text-sm leading-snug font-medium", "flex w-fit items-center gap-2 text-sm leading-snug font-medium",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function ItemDescription({ className, ...props }: React.ComponentProps<"p">) { function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
@ -136,11 +136,11 @@ function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
className={cn( className={cn(
"text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance", "text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance",
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function ItemActions({ className, ...props }: React.ComponentProps<"div">) { function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
@ -150,7 +150,7 @@ function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
className={cn("flex items-center gap-2", className)} className={cn("flex items-center gap-2", className)}
{...props} {...props}
/> />
) );
} }
function ItemHeader({ className, ...props }: React.ComponentProps<"div">) { function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
@ -159,11 +159,11 @@ function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
data-slot="item-header" data-slot="item-header"
className={cn( className={cn(
"flex basis-full items-center justify-between gap-2", "flex basis-full items-center justify-between gap-2",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function ItemFooter({ className, ...props }: React.ComponentProps<"div">) { function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
@ -172,11 +172,11 @@ function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
data-slot="item-footer" data-slot="item-footer"
className={cn( className={cn(
"flex basis-full items-center justify-between gap-2", "flex basis-full items-center justify-between gap-2",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
export { export {
@ -190,4 +190,4 @@ export {
ItemDescription, ItemDescription,
ItemHeader, ItemHeader,
ItemFooter, ItemFooter,
} };

View File

@ -145,7 +145,7 @@
/* Border glow effect */ /* Border glow effect */
.magic-bento-card--border-glow::after { .magic-bento-card--border-glow::after {
content: ''; content: "";
position: absolute; position: absolute;
inset: 0; inset: 0;
padding: 6px; padding: 6px;
@ -186,7 +186,7 @@
} }
.particle::before { .particle::before {
content: ''; content: "";
position: absolute; position: absolute;
top: -2px; top: -2px;
left: -2px; left: -2px;

View File

@ -1,9 +1,9 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as ProgressPrimitive from "@radix-ui/react-progress" import * as ProgressPrimitive from "@radix-ui/react-progress";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Progress({ function Progress({
className, className,
@ -15,7 +15,7 @@ function Progress({
data-slot="progress" data-slot="progress"
className={cn( className={cn(
"bg-primary/20 relative h-2 w-full overflow-hidden rounded-full", "bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
className className,
)} )}
{...props} {...props}
> >
@ -25,7 +25,7 @@ function Progress({
style={{ transform: `translateX(-${100 - (value || 0)}%)` }} style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/> />
</ProgressPrimitive.Root> </ProgressPrimitive.Root>
) );
} }
export { Progress } export { Progress };

View File

@ -1,9 +1,9 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function ScrollArea({ function ScrollArea({
className, className,
@ -25,7 +25,7 @@ function ScrollArea({
<ScrollBar /> <ScrollBar />
<ScrollAreaPrimitive.Corner /> <ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root> </ScrollAreaPrimitive.Root>
) );
} }
function ScrollBar({ function ScrollBar({
@ -43,7 +43,7 @@ function ScrollBar({
"h-full w-2.5 border-l border-l-transparent", "h-full w-2.5 border-l border-l-transparent",
orientation === "horizontal" && orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent", "h-2.5 flex-col border-t border-t-transparent",
className className,
)} )}
{...props} {...props}
> >
@ -52,7 +52,7 @@ function ScrollBar({
className="bg-border relative flex-1 rounded-full" className="bg-border relative flex-1 rounded-full"
/> />
</ScrollAreaPrimitive.ScrollAreaScrollbar> </ScrollAreaPrimitive.ScrollAreaScrollbar>
) );
} }
export { ScrollArea, ScrollBar } export { ScrollArea, ScrollBar };

View File

@ -1,9 +1,9 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as SeparatorPrimitive from "@radix-ui/react-separator" import * as SeparatorPrimitive from "@radix-ui/react-separator";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Separator({ function Separator({
className, className,
@ -18,11 +18,11 @@ function Separator({
orientation={orientation} orientation={orientation}
className={cn( className={cn(
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px", "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
export { Separator } export { Separator };

View File

@ -1,31 +1,31 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as SheetPrimitive from "@radix-ui/react-dialog" import * as SheetPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react" import { XIcon } from "lucide-react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) { function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
return <SheetPrimitive.Root data-slot="sheet" {...props} /> return <SheetPrimitive.Root data-slot="sheet" {...props} />;
} }
function SheetTrigger({ function SheetTrigger({
...props ...props
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) { }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} /> return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
} }
function SheetClose({ function SheetClose({
...props ...props
}: React.ComponentProps<typeof SheetPrimitive.Close>) { }: React.ComponentProps<typeof SheetPrimitive.Close>) {
return <SheetPrimitive.Close data-slot="sheet-close" {...props} /> return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
} }
function SheetPortal({ function SheetPortal({
...props ...props
}: React.ComponentProps<typeof SheetPrimitive.Portal>) { }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} /> return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
} }
function SheetOverlay({ function SheetOverlay({
@ -37,11 +37,11 @@ function SheetOverlay({
data-slot="sheet-overlay" data-slot="sheet-overlay"
className={cn( className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function SheetContent({ function SheetContent({
@ -50,7 +50,7 @@ function SheetContent({
side = "right", side = "right",
...props ...props
}: React.ComponentProps<typeof SheetPrimitive.Content> & { }: React.ComponentProps<typeof SheetPrimitive.Content> & {
side?: "top" | "right" | "bottom" | "left" side?: "top" | "right" | "bottom" | "left";
}) { }) {
return ( return (
<SheetPortal> <SheetPortal>
@ -67,7 +67,7 @@ function SheetContent({
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b", "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
side === "bottom" && side === "bottom" &&
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t", "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
className className,
)} )}
{...props} {...props}
> >
@ -78,7 +78,7 @@ function SheetContent({
</SheetPrimitive.Close> </SheetPrimitive.Close>
</SheetPrimitive.Content> </SheetPrimitive.Content>
</SheetPortal> </SheetPortal>
) );
} }
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
@ -88,7 +88,7 @@ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
className={cn("flex flex-col gap-1.5 p-4", className)} className={cn("flex flex-col gap-1.5 p-4", className)}
{...props} {...props}
/> />
) );
} }
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) { function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
@ -98,7 +98,7 @@ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
className={cn("mt-auto flex flex-col gap-2 p-4", className)} className={cn("mt-auto flex flex-col gap-2 p-4", className)}
{...props} {...props}
/> />
) );
} }
function SheetTitle({ function SheetTitle({
@ -111,7 +111,7 @@ function SheetTitle({
className={cn("text-foreground font-semibold", className)} className={cn("text-foreground font-semibold", className)}
{...props} {...props}
/> />
) );
} }
function SheetDescription({ function SheetDescription({
@ -124,7 +124,7 @@ function SheetDescription({
className={cn("text-muted-foreground text-sm", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
) );
} }
export { export {
@ -136,4 +136,4 @@ export {
SheetFooter, SheetFooter,
SheetTitle, SheetTitle,
SheetDescription, SheetDescription,
} };

View File

@ -1,25 +1,25 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
interface ShineBorderProps extends React.HTMLAttributes<HTMLDivElement> { interface ShineBorderProps extends React.HTMLAttributes<HTMLDivElement> {
/** /**
* Width of the border in pixels * Width of the border in pixels
* @default 1 * @default 1
*/ */
borderWidth?: number borderWidth?: number;
/** /**
* Duration of the animation in seconds * Duration of the animation in seconds
* @default 14 * @default 14
*/ */
duration?: number duration?: number;
/** /**
* Color of the border, can be a single color or an array of colors * Color of the border, can be a single color or an array of colors
* @default "#000000" * @default "#000000"
*/ */
shineColor?: string | string[] shineColor?: string | string[];
} }
/** /**
@ -55,9 +55,9 @@ export function ShineBorder({
} }
className={cn( className={cn(
"motion-safe:animate-shine pointer-events-none absolute inset-0 size-full rounded-[inherit] will-change-[background-position]", "motion-safe:animate-shine pointer-events-none absolute inset-0 size-full rounded-[inherit] will-change-[background-position]",
className className,
)} )}
{...props} {...props}
/> />
) );
} }

View File

@ -139,7 +139,7 @@ function SidebarProvider({
} as React.CSSProperties } as React.CSSProperties
} }
className={cn( className={cn(
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full m-auto rounded-t-[20px] overflow-hidden", "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar m-auto flex min-h-svh w-full overflow-hidden rounded-t-[20px]",
className, className,
)} )}
{...props} {...props}

View File

@ -1,4 +1,4 @@
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Skeleton({ className, ...props }: React.ComponentProps<"div">) { function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
@ -7,7 +7,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
className={cn("bg-accent animate-pulse rounded-md", className)} className={cn("bg-accent animate-pulse rounded-md", className)}
{...props} {...props}
/> />
) );
} }
export { Skeleton } export { Skeleton };

View File

@ -1,4 +1,4 @@
"use client" "use client";
import { import {
CircleCheckIcon, CircleCheckIcon,
@ -6,12 +6,12 @@ import {
Loader2Icon, Loader2Icon,
OctagonXIcon, OctagonXIcon,
TriangleAlertIcon, TriangleAlertIcon,
} from "lucide-react" } from "lucide-react";
import { useTheme } from "next-themes" import { useTheme } from "next-themes";
import { Toaster as Sonner, type ToasterProps } from "sonner" import { Toaster as Sonner, type ToasterProps } from "sonner";
const Toaster = ({ ...props }: ToasterProps) => { const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme() const { theme = "system" } = useTheme();
return ( return (
<Sonner <Sonner
@ -34,7 +34,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
} }
{...props} {...props}
/> />
) );
} };
export { Toaster } export { Toaster };

View File

@ -11,13 +11,17 @@
} }
.card-spotlight::before { .card-spotlight::before {
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background: radial-gradient(circle at var(--mouse-x) var(--mouse-y), var(--spotlight-color), transparent 80%); background: radial-gradient(
circle at var(--mouse-x) var(--mouse-y),
var(--spotlight-color),
transparent 80%
);
opacity: 0; opacity: 0;
transition: opacity 0.5s ease; transition: opacity 0.5s ease;
pointer-events: none; pointer-events: none;

View File

@ -1,9 +1,9 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as SwitchPrimitive from "@radix-ui/react-switch" import * as SwitchPrimitive from "@radix-ui/react-switch";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Switch({ function Switch({
className, className,
@ -14,18 +14,18 @@ function Switch({
data-slot="switch" data-slot="switch"
className={cn( className={cn(
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", "peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
{...props} {...props}
> >
<SwitchPrimitive.Thumb <SwitchPrimitive.Thumb
data-slot="switch-thumb" data-slot="switch-thumb"
className={cn( className={cn(
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0" "bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0",
)} )}
/> />
</SwitchPrimitive.Root> </SwitchPrimitive.Root>
) );
} }
export { Switch } export { Switch };

View File

@ -1,10 +1,10 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as TabsPrimitive from "@radix-ui/react-tabs" import * as TabsPrimitive from "@radix-ui/react-tabs";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function Tabs({ function Tabs({
className, className,
@ -18,11 +18,11 @@ function Tabs({
orientation={orientation} orientation={orientation}
className={cn( className={cn(
"group/tabs flex gap-2 data-[orientation=horizontal]:flex-col", "group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
const tabsListVariants = cva( const tabsListVariants = cva(
@ -37,8 +37,8 @@ const tabsListVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
) );
function TabsList({ function TabsList({
className, className,
@ -53,7 +53,7 @@ function TabsList({
className={cn(tabsListVariants({ variant }), className)} className={cn(tabsListVariants({ variant }), className)}
{...props} {...props}
/> />
) );
} }
function TabsTrigger({ function TabsTrigger({
@ -68,11 +68,11 @@ function TabsTrigger({
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent", "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 data-[state=active]:text-foreground", "data-[state=active]:bg-background dark:data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 data-[state=active]:text-foreground",
"after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100", "after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
className className,
)} )}
{...props} {...props}
/> />
) );
} }
function TabsContent({ function TabsContent({
@ -85,7 +85,7 @@ function TabsContent({
className={cn("flex-1 outline-none", className)} className={cn("flex-1 outline-none", className)}
{...props} {...props}
/> />
) );
} }
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };

View File

@ -1,10 +1,10 @@
"use client" "use client";
import * as React from "react" import * as React from "react";
import * as TogglePrimitive from "@radix-ui/react-toggle" import * as TogglePrimitive from "@radix-ui/react-toggle";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const toggleVariants = cva( const toggleVariants = cva(
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap", "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
@ -25,8 +25,8 @@ const toggleVariants = cva(
variant: "default", variant: "default",
size: "default", size: "default",
}, },
} },
) );
function Toggle({ function Toggle({
className, className,
@ -41,7 +41,7 @@ function Toggle({
className={cn(toggleVariants({ variant, size, className }))} className={cn(toggleVariants({ variant, size, className }))}
{...props} {...props}
/> />
) );
} }
export { Toggle, toggleVariants } export { Toggle, toggleVariants };

View File

@ -127,7 +127,7 @@ export function ArtifactFileDetail({
return ( return (
<Artifact className={cn(className)}> <Artifact className={cn(className)}>
<ArtifactHeader> <ArtifactHeader>
<div className="flex items-center gap-2 justify-start"> <div className="flex items-center justify-start gap-2">
{isSupportPreview && ( {isSupportPreview && (
<ToggleGroup <ToggleGroup
type="single" type="single"
@ -162,7 +162,7 @@ export function ArtifactFileDetail({
)} )}
</ArtifactTitle> </ArtifactTitle>
</div> </div>
<div className="flex justify-end items-center gap-2"> <div className="flex items-center justify-end gap-2">
<ArtifactActions> <ArtifactActions>
{!isWriteFile && filepath.endsWith(".skill") && ( {!isWriteFile && filepath.endsWith(".skill") && (
<Tooltip content={t.toolCalls.skillInstallTooltip}> <Tooltip content={t.toolCalls.skillInstallTooltip}>
@ -226,7 +226,7 @@ export function ArtifactFileDetail({
</div> </div>
</ArtifactHeader> </ArtifactHeader>
<ArtifactContent className="p-0 rounded-[10px] bg-white"> <ArtifactContent className="rounded-[10px] bg-white p-0">
{isSupportPreview && {isSupportPreview &&
viewMode === "preview" && viewMode === "preview" &&
(language === "markdown" || language === "html") && ( (language === "markdown" || language === "html") && (
@ -269,7 +269,6 @@ export function ArtifactFilePreview({
components={{ a: CitationLink }} components={{ a: CitationLink }}
> >
{content ?? ""} {content ?? ""}
</Streamdown> </Streamdown>
</div> </div>
); );

View File

@ -76,7 +76,7 @@ export function ArtifactFileList({
{files.map((file) => ( {files.map((file) => (
<Card <Card
key={file} key={file}
className="relative cursor-pointer py-[15px] px-[20px]" className="relative cursor-pointer px-[20px] py-[15px]"
onClick={() => handleClick(file)} onClick={() => handleClick(file)}
> >
<CardHeader className="pr-2 pl-1"> <CardHeader className="pr-2 pl-1">

View File

@ -16,7 +16,7 @@ export const ArtifactTrigger = () => {
return ( return (
<Tooltip content="点击可查看生成的文件结果"> <Tooltip content="点击可查看生成的文件结果">
<Button <Button
className="text-sm font-medium py-[5px] px-[10px] h-full" className="h-full px-[10px] py-[5px] text-sm font-medium"
variant="ghost" variant="ghost"
onClick={() => { onClick={() => {
setArtifactsOpen(true); setArtifactsOpen(true);

View File

@ -102,7 +102,11 @@ const ChatBox: React.FC<{ children: React.ReactNode; threadId: string }> = ({
defaultLayout={{ chat: 100, artifacts: 0 }} defaultLayout={{ chat: 100, artifacts: 0 }}
groupRef={layoutRef} groupRef={layoutRef}
> >
<ResizablePanel className="relative overflow-hidden rounded-t-[20px] " defaultSize={100} id="chat"> <ResizablePanel
className="relative overflow-hidden rounded-t-[20px]"
defaultSize={100}
id="chat"
>
{children} {children}
</ResizablePanel> </ResizablePanel>
<ResizableHandle <ResizableHandle
@ -113,14 +117,14 @@ const ChatBox: React.FC<{ children: React.ReactNode; threadId: string }> = ({
/> />
<ResizablePanel <ResizablePanel
className={cn( className={cn(
"transition-all duration-300 ease-in-out ml-[20px]", "ml-[20px] transition-all duration-300 ease-in-out",
!artifactsOpen && "opacity-0", !artifactsOpen && "opacity-0",
)} )}
id="artifacts" id="artifacts"
> >
<div <div
className={cn( className={cn(
"h-full transition-transform duration-300 ease-in-out bg-background rounded-t-[20px]", "bg-background h-full rounded-t-[20px] transition-transform duration-300 ease-in-out",
artifactPanelOpen ? "translate-x-0" : "translate-x-full", artifactPanelOpen ? "translate-x-0" : "translate-x-full",
)} )}
> >
@ -151,11 +155,14 @@ const ChatBox: React.FC<{ children: React.ReactNode; threadId: string }> = ({
/> />
) : ( ) : (
<div className="flex size-full max-w-(--container-width-sm) flex-col justify-center p-4 pt-8"> <div className="flex size-full max-w-(--container-width-sm) flex-col justify-center p-4 pt-8">
<header className="shrink-0 flex justify-center"> <header className="flex shrink-0 justify-center">
{/* 遍历thread.values.artifacts选择器*/} {/* 遍历thread.values.artifacts选择器*/}
{thread.values.artifacts && thread.values.artifacts.length > 0 && ( {thread.values.artifacts &&
thread.values.artifacts.length > 0 && (
<DropdownSelector <DropdownSelector
value={selectedArtifact ?? thread.values.artifacts[0]!} value={
selectedArtifact ?? thread.values.artifacts[0]!
}
options={thread.values.artifacts.map((artifact) => ({ options={thread.values.artifacts.map((artifact) => ({
value: artifact, value: artifact,
label: getFileName(artifact), label: getFileName(artifact),

View File

@ -48,12 +48,12 @@ export function CitationLink({
<div className="p-3"> <div className="p-3">
<div className="space-y-1"> <div className="space-y-1">
{displayText && ( {displayText && (
<h4 className="truncate font-medium text-sm leading-tight"> <h4 className="truncate text-sm leading-tight font-medium">
{displayText} {displayText}
</h4> </h4>
)} )}
{href && ( {href && (
<p className="truncate break-all text-muted-foreground text-xs"> <p className="text-muted-foreground truncate text-xs break-all">
{href} {href}
</p> </p>
)} )}

View File

@ -1,6 +1,5 @@
"use client"; "use client";
import type { Todo } from "@/core/todos"; import type { Todo } from "@/core/todos";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -34,7 +33,11 @@ export function DevTodoList({
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger> <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
<DropdownMenuContent className={cn("bg-white z-[100]", className)} align="start" side="top"> <DropdownMenuContent
className={cn("z-[100] bg-white", className)}
align="start"
side="top"
>
<QueueList className="w-64"> <QueueList className="w-64">
{todos.map((todo, i) => ( {todos.map((todo, i) => (
<QueueItem key={i + (todo.content ?? "")}> <QueueItem key={i + (todo.content ?? "")}>

View File

@ -19,7 +19,7 @@ export function FlipDisplay({
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
className="overflow-ellipsis overflow-hidden whitespace-nowrap" className="overflow-hidden overflow-ellipsis whitespace-nowrap"
// transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }} // transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }}
> >
{children} {children}

View File

@ -175,7 +175,10 @@ export function InputBox({
if (!isFocused) return; if (!isFocused) return;
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(event.target as Node)) { if (
containerRef.current &&
!containerRef.current.contains(event.target as Node)
) {
setIsFocused(false); setIsFocused(false);
onFocusChange?.(false); onFocusChange?.(false);
} }
@ -245,7 +248,14 @@ export function InputBox({
onContextChange?.({ onContextChange?.({
...context, ...context,
mode: getResolvedMode(mode, supportThinking), mode: getResolvedMode(mode, supportThinking),
reasoning_effort: mode === "ultra" ? "high" : mode === "pro" ? "medium" : mode === "thinking" ? "low" : "minimal", reasoning_effort:
mode === "ultra"
? "high"
: mode === "pro"
? "medium"
: mode === "thinking"
? "low"
: "minimal",
}); });
}, },
[onContextChange, context, supportThinking], [onContextChange, context, supportThinking],
@ -319,7 +329,9 @@ export function InputBox({
return; return;
} }
const current = (textInput.value ?? "").trim(); const current = (textInput.value ?? "").trim();
const next = current ? `${current}\n${pendingSuggestion}` : pendingSuggestion; const next = current
? `${current}\n${pendingSuggestion}`
: pendingSuggestion;
textInput.setInput(next); textInput.setInput(next);
setFollowupsHidden(true); setFollowupsHidden(true);
setConfirmOpen(false); setConfirmOpen(false);
@ -399,7 +411,13 @@ export function InputBox({
}, [context.model_name, disabled, isMock, status, thread.messages, threadId]); }, [context.model_name, disabled, isMock, status, thread.messages, threadId]);
return ( return (
<div ref={(el) => { promptRootRef.current = el; containerRef.current = el; }} className="relative"> <div
ref={(el) => {
promptRootRef.current = el;
containerRef.current = el;
}}
className="relative"
>
{/* 附件预览区域 - 在输入框上方 */} {/* 附件预览区域 - 在输入框上方 */}
<AttachmentPreviewBar /> <AttachmentPreviewBar />
@ -410,10 +428,7 @@ export function InputBox({
)} )}
{/* 输入框主容器 */} {/* 输入框主容器 */}
<PromptInput <PromptInput
className={cn( className={cn("w-full", className)}
"w-full",
className,
)}
inputGroupClassName={cn( inputGroupClassName={cn(
"border-0 backdrop-blur-sm w-[720px] rounded-[20px]", "border-0 backdrop-blur-sm w-[720px] rounded-[20px]",
"transition-[height] duration-300 ease-out", "transition-[height] duration-300 ease-out",
@ -426,15 +441,17 @@ export function InputBox({
onSubmit={handleSubmit} onSubmit={handleSubmit}
{...props} {...props}
> >
<PromptInputBody className={cn( <PromptInputBody
className={cn(
"transition-[opacity,transform] duration-300 ease-out", "transition-[opacity,transform] duration-300 ease-out",
!effectiveIsFocused && "opacity-100" !effectiveIsFocused && "opacity-100",
)}> )}
>
<PromptInputTextarea <PromptInputTextarea
ref={textareaRef} ref={textareaRef}
className={cn( className={cn(
"size-full", "size-full",
!effectiveIsFocused && "h-[80px] py-0 leading-20" !effectiveIsFocused && "h-[80px] py-0 leading-20",
)} )}
disabled={disabled} disabled={disabled}
placeholder={t.inputBox.placeholder} placeholder={t.inputBox.placeholder}
@ -455,11 +472,14 @@ export function InputBox({
}} }}
/> />
)} )}
<PromptInputFooter className={cn( <PromptInputFooter
className={cn(
"flex transition-all duration-300 ease-out", "flex transition-all duration-300 ease-out",
// height和padding都为0来隐藏 // height和padding都为0来隐藏
!effectiveIsFocused && "invisible h-[0px] p-[0px] opacity-0 translate-y-2 pointer-events-none" !effectiveIsFocused &&
)}> "pointer-events-none invisible h-[0px] translate-y-2 p-[0px] opacity-0",
)}
>
{/* ========== 左侧工具栏 ========== */} {/* ========== 左侧工具栏 ========== */}
<PromptInputTools> <PromptInputTools>
{/* 附件上传按钮 */} {/* 附件上传按钮 */}
@ -649,10 +669,14 @@ export function InputBox({
<PromptInputActionMenuTrigger className="gap-1! px-2!"> <PromptInputActionMenuTrigger className="gap-1! px-2!">
<div className="text-xs font-normal"> <div className="text-xs font-normal">
{t.inputBox.reasoningEffort}: {t.inputBox.reasoningEffort}:
{context.reasoning_effort === "minimal" && " " + t.inputBox.reasoningEffortMinimal} {context.reasoning_effort === "minimal" &&
{context.reasoning_effort === "low" && " " + t.inputBox.reasoningEffortLow} " " + t.inputBox.reasoningEffortMinimal}
{context.reasoning_effort === "medium" && " " + t.inputBox.reasoningEffortMedium} {context.reasoning_effort === "low" &&
{context.reasoning_effort === "high" && " " + t.inputBox.reasoningEffortHigh} " " + t.inputBox.reasoningEffortLow}
{context.reasoning_effort === "medium" &&
" " + t.inputBox.reasoningEffortMedium}
{context.reasoning_effort === "high" &&
" " + t.inputBox.reasoningEffortHigh}
</div> </div>
</PromptInputActionMenuTrigger> </PromptInputActionMenuTrigger>
<PromptInputActionMenuContent className="w-70"> <PromptInputActionMenuContent className="w-70">
@ -707,7 +731,8 @@ export function InputBox({
</PromptInputActionMenuItem> </PromptInputActionMenuItem>
<PromptInputActionMenuItem <PromptInputActionMenuItem
className={cn( className={cn(
context.reasoning_effort === "medium" || !context.reasoning_effort context.reasoning_effort === "medium" ||
!context.reasoning_effort
? "text-accent-foreground" ? "text-accent-foreground"
: "text-muted-foreground/65", : "text-muted-foreground/65",
)} )}
@ -721,7 +746,8 @@ export function InputBox({
{t.inputBox.reasoningEffortMediumDescription} {t.inputBox.reasoningEffortMediumDescription}
</div> </div>
</div> </div>
{context.reasoning_effort === "medium" || !context.reasoning_effort ? ( {context.reasoning_effort === "medium" ||
!context.reasoning_effort ? (
<CheckIcon className="ml-auto size-4" /> <CheckIcon className="ml-auto size-4" />
) : ( ) : (
<div className="ml-auto size-4" /> <div className="ml-auto size-4" />
@ -807,14 +833,16 @@ export function InputBox({
</PromptInput> </PromptInput>
{/* 小惊喜等 */} {/* 小惊喜等 */}
{isNewThread && searchParams.get("mode") !== "skill" && ( {isNewThread && searchParams.get("mode") !== "skill" && (
<SuggestionListContainer sendSelectSkill={iframeSkill.sendSelectSkill} /> <SuggestionListContainer
sendSelectSkill={iframeSkill.sendSelectSkill}
/>
)} )}
{!disabled && {!disabled &&
!isNewThread && !isNewThread &&
!followupsHidden && !followupsHidden &&
(followupsLoading || followups.length > 0) && ( (followupsLoading || followups.length > 0) && (
<div className="absolute right-0 -top-20 left-0 z-20 flex items-center justify-center"> <div className="absolute -top-20 right-0 left-0 z-20 flex items-center justify-center">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{followupsLoading ? ( {followupsLoading ? (
<div className="text-muted-foreground bg-background/80 rounded-full border px-4 py-2 text-xs backdrop-blur-sm"> <div className="text-muted-foreground bg-background/80 rounded-full border px-4 py-2 text-xs backdrop-blur-sm">
@ -870,16 +898,24 @@ export function InputBox({
); );
} }
// SuggestionList 容器 // SuggestionList 容器
function SuggestionListContainer({ sendSelectSkill }: { sendSelectSkill: (skill_id: string) => void }) { function SuggestionListContainer({
sendSelectSkill,
}: {
sendSelectSkill: (skill_id: string) => void;
}) {
return ( return (
<div className="absolute right-0 bottom-0 left-0 z-0 flex items-center justify-center pt-4 translate-y-full"> <div className="absolute right-0 bottom-0 left-0 z-0 flex translate-y-full items-center justify-center pt-4">
<SuggestionList sendSelectSkill={sendSelectSkill} /> <SuggestionList sendSelectSkill={sendSelectSkill} />
</div> </div>
); );
} }
// 快速选择skillbutton // 快速选择skillbutton
function SuggestionList({ sendSelectSkill }: { sendSelectSkill: (skill_id: string) => void }) { function SuggestionList({
sendSelectSkill,
}: {
sendSelectSkill: (skill_id: string) => void;
}) {
const { t } = useI18n(); const { t } = useI18n();
const { textInput } = usePromptInputController(); const { textInput } = usePromptInputController();
@ -964,11 +1000,23 @@ function AddAttachmentsButton({ className }: { className?: string }) {
className={cn("group px-2! hover:bg-[#EAE2F5]", className)} className={cn("group px-2! hover:bg-[#EAE2F5]", className)}
onClick={() => attachments.openFileDialog()} onClick={() => attachments.openFileDialog()}
> >
<svg width="18" height="15" viewBox="0 0 18 15" fill="none" xmlns="http://www.w3.org/2000/svg" className="transition-[stroke] duration-200 [&>path]:transition-[fill,stroke] [&>path]:duration-200 [&>path:first-child]:group-hover:fill-[#8E47F0] [&>path:last-child]:group-hover:stroke-[#8E47F0]"> <svg
<path d="M7.05042 7.65254C6.9754 7.72756 6.90039 7.80257 6.90039 7.95258C6.90039 8.02759 6.9754 8.1776 7.05042 8.25262C7.20043 8.40263 7.42545 8.40263 7.57546 8.25262L8.8506 6.97747V10.7279C8.8506 10.9529 9.00061 11.1029 9.22563 11.1029C9.30065 11.1029 9.45066 11.0279 9.52567 11.0279C9.60067 10.9529 9.67568 10.8779 9.67568 10.7279V6.97747L10.9508 8.25262C11.1008 8.40263 11.3259 8.40263 11.4759 8.25262C11.5509 8.1776 11.6259 8.10259 11.6259 7.95258C11.6259 7.87757 11.5509 7.72756 11.4759 7.65254L9.52567 5.70235C9.37564 5.55234 9.15062 5.55234 9.00061 5.70235L7.05042 7.65254Z" fill="#150033"/> width="18"
<path d="M1.12695 0.5H6.67871C6.87077 0.500077 7.01409 0.574515 7.07324 0.648438L7.09082 0.669922L8.30762 1.88672C8.6222 2.20119 9.01344 2.3681 9.44629 2.36816H16.875C17.2382 2.36842 17.5012 2.63339 17.5 2.99414V13.8848C17.5048 14.2408 17.2454 14.5056 16.8818 14.5059H1.12695C0.764649 14.5057 0.5 14.2401 0.5 13.877V1.12793C0.500049 0.810129 0.702664 0.567404 0.996094 0.511719L1.12695 0.5Z" stroke="#150033"/> height="15"
viewBox="0 0 18 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="transition-[stroke] duration-200 [&>path]:transition-[fill,stroke] [&>path]:duration-200 [&>path:first-child]:group-hover:fill-[#8E47F0] [&>path:last-child]:group-hover:stroke-[#8E47F0]"
>
<path
d="M7.05042 7.65254C6.9754 7.72756 6.90039 7.80257 6.90039 7.95258C6.90039 8.02759 6.9754 8.1776 7.05042 8.25262C7.20043 8.40263 7.42545 8.40263 7.57546 8.25262L8.8506 6.97747V10.7279C8.8506 10.9529 9.00061 11.1029 9.22563 11.1029C9.30065 11.1029 9.45066 11.0279 9.52567 11.0279C9.60067 10.9529 9.67568 10.8779 9.67568 10.7279V6.97747L10.9508 8.25262C11.1008 8.40263 11.3259 8.40263 11.4759 8.25262C11.5509 8.1776 11.6259 8.10259 11.6259 7.95258C11.6259 7.87757 11.5509 7.72756 11.4759 7.65254L9.52567 5.70235C9.37564 5.55234 9.15062 5.55234 9.00061 5.70235L7.05042 7.65254Z"
fill="#150033"
/>
<path
d="M1.12695 0.5H6.67871C6.87077 0.500077 7.01409 0.574515 7.07324 0.648438L7.09082 0.669922L8.30762 1.88672C8.6222 2.20119 9.01344 2.3681 9.44629 2.36816H16.875C17.2382 2.36842 17.5012 2.63339 17.5 2.99414V13.8848C17.5048 14.2408 17.2454 14.5056 16.8818 14.5059H1.12695C0.764649 14.5057 0.5 14.2401 0.5 13.877V1.12793C0.500049 0.810129 0.702664 0.567404 0.996094 0.511719L1.12695 0.5Z"
stroke="#150033"
/>
</svg> </svg>
</PromptInputButton> </PromptInputButton>
</Tooltip> </Tooltip>
); );
@ -994,17 +1042,28 @@ function IframeSkillDialogButton({
className={cn("group px-2! hover:bg-[#EAE2F5]", className)} className={cn("group px-2! hover:bg-[#EAE2F5]", className)}
onClick={openSkillDialog} onClick={openSkillDialog}
> >
<svg xmlns="http://www.w3.org/2000/svg" className="size-4 transition-[stroke] duration-200 [&>path]:transition-[stroke] [&>path]:duration-200 [&>path]:group-hover:stroke-[#8E47F0]" viewBox="0 0 12 16" fill="none"> <svg
<path d="M3.7998 0.5H9.19922C9.24033 0.5 9.26852 0.518136 9.28516 0.541992C9.30124 0.565318 9.30411 0.588767 9.29395 0.613281H9.29297L7.43066 5.07422L7.1416 5.76758H11.3994C11.4295 5.76765 11.4474 5.77552 11.459 5.7832C11.4724 5.79207 11.4846 5.80503 11.4922 5.82129C11.4997 5.83745 11.5013 5.85253 11.5 5.86328C11.4989 5.87156 11.4953 5.88556 11.4785 5.9043L2.87891 15.4629V15.4639C2.85396 15.4914 2.83406 15.4971 2.82031 15.499C2.80144 15.5016 2.77553 15.4981 2.74902 15.4844C2.72225 15.4705 2.70837 15.453 2.70312 15.4424C2.70056 15.4372 2.69457 15.4253 2.70312 15.3936V15.3926L4.30273 9.49512L4.47461 8.86426H0.600586C0.559682 8.86424 0.531324 8.84587 0.514648 8.82227C0.498608 8.79944 0.496551 8.777 0.505859 8.75293L3.70508 0.558594C3.71075 0.544183 3.72173 0.529788 3.73828 0.518555C3.74688 0.51277 3.75704 0.508037 3.76758 0.504883L3.7998 0.5Z" stroke="#150033"/> xmlns="http://www.w3.org/2000/svg"
className="size-4 transition-[stroke] duration-200 [&>path]:transition-[stroke] [&>path]:duration-200 [&>path]:group-hover:stroke-[#8E47F0]"
viewBox="0 0 12 16"
fill="none"
>
<path
d="M3.7998 0.5H9.19922C9.24033 0.5 9.26852 0.518136 9.28516 0.541992C9.30124 0.565318 9.30411 0.588767 9.29395 0.613281H9.29297L7.43066 5.07422L7.1416 5.76758H11.3994C11.4295 5.76765 11.4474 5.77552 11.459 5.7832C11.4724 5.79207 11.4846 5.80503 11.4922 5.82129C11.4997 5.83745 11.5013 5.85253 11.5 5.86328C11.4989 5.87156 11.4953 5.88556 11.4785 5.9043L2.87891 15.4629V15.4639C2.85396 15.4914 2.83406 15.4971 2.82031 15.499C2.80144 15.5016 2.77553 15.4981 2.74902 15.4844C2.72225 15.4705 2.70837 15.453 2.70312 15.4424C2.70056 15.4372 2.69457 15.4253 2.70312 15.3936V15.3926L4.30273 9.49512L4.47461 8.86426H0.600586C0.559682 8.86424 0.531324 8.84587 0.514648 8.82227C0.498608 8.79944 0.496551 8.777 0.505859 8.75293L3.70508 0.558594C3.71075 0.544183 3.72173 0.529788 3.73828 0.518555C3.74688 0.51277 3.75704 0.508037 3.76758 0.504883L3.7998 0.5Z"
stroke="#150033"
/>
</svg> </svg>
</PromptInputButton> </PromptInputButton>
</Tooltip> </Tooltip>
{selectedSkill && ( {selectedSkill && (
<Badge variant="secondary" className="gap-1 px-[15px] py-[4px] text-[#8E47F0] bg-[#EAE2F5]"> <Badge
variant="secondary"
className="gap-1 bg-[#EAE2F5] px-[15px] py-[4px] text-[#8E47F0]"
>
{selectedSkill.title} {selectedSkill.title}
<button <button
onClick={clearSkill} onClick={clearSkill}
className="ml-1 rounded-full hover:bg-muted-foreground/20" className="hover:bg-muted-foreground/20 ml-1 rounded-full"
> >
<XIcon className="size-3" /> <XIcon className="size-3" />
</button> </button>
@ -1023,7 +1082,7 @@ function AttachmentPreviewBar() {
} }
return ( return (
<div className="absolute bottom-full left-0 ml-1 z-20 mb-3 flex justify-start"> <div className="absolute bottom-full left-0 z-20 mb-3 ml-1 flex justify-start">
<PromptInputAttachments> <PromptInputAttachments>
{(attachment) => <PromptInputAttachment data={attachment} />} {(attachment) => <PromptInputAttachment data={attachment} />}
</PromptInputAttachments> </PromptInputAttachments>
@ -1034,16 +1093,18 @@ function AttachmentPreviewBar() {
// ExtraHeader 容器 - 有附件时上浮 // ExtraHeader 容器 - 有附件时上浮
function ExtraHeaderContainer({ function ExtraHeaderContainer({
hasAttachments, hasAttachments,
children children,
}: { }: {
hasAttachments: boolean; hasAttachments: boolean;
children: React.ReactNode; children: React.ReactNode;
}) { }) {
return ( return (
<div className={cn( <div
className={cn(
"absolute right-0 bottom-full left-0 z-30 flex items-center justify-center pb-4 transition-transform duration-300", "absolute right-0 bottom-full left-0 z-30 flex items-center justify-center pb-4 transition-transform duration-300",
hasAttachments && "-translate-y-20" hasAttachments && "-translate-y-20",
)}> )}
>
{children} {children}
</div> </div>
); );

View File

@ -151,7 +151,12 @@ export function MessageGroup({
<ChainOfThoughtStep <ChainOfThoughtStep
className="font-normal" className="font-normal"
label={t.common.thinking} label={t.common.thinking}
icon={<LightbulbIcon className="size-4" style={{ color: '#999999' }} />} icon={
<LightbulbIcon
className="size-4"
style={{ color: "#999999" }}
/>
}
></ChainOfThoughtStep> ></ChainOfThoughtStep>
<div> <div>
<ChevronUp <ChevronUp

View File

@ -29,7 +29,10 @@ function getModeDescriptionKey(
mode: AgentMode, mode: AgentMode,
): keyof Pick< ): keyof Pick<
Translations["inputBox"], Translations["inputBox"],
"flashModeDescription" | "reasoningModeDescription" | "proModeDescription" | "ultraModeDescription" | "flashModeDescription"
| "reasoningModeDescription"
| "proModeDescription"
| "ultraModeDescription"
> { > {
switch (mode) { switch (mode) {
case "flash": case "flash":

View File

@ -33,17 +33,20 @@ DeerFlow is proudly open source and distributed under the **MIT License**.
We extend our heartfelt gratitude to the open source projects and contributors who have made DeerFlow a reality. We truly stand on the shoulders of giants. We extend our heartfelt gratitude to the open source projects and contributors who have made DeerFlow a reality. We truly stand on the shoulders of giants.
### Core Frameworks ### Core Frameworks
- **[LangChain](https://github.com/langchain-ai/langchain)**: A phenomenal framework that powers our LLM interactions and chains. - **[LangChain](https://github.com/langchain-ai/langchain)**: A phenomenal framework that powers our LLM interactions and chains.
- **[LangGraph](https://github.com/langchain-ai/langgraph)**: Enabling sophisticated multi-agent orchestration. - **[LangGraph](https://github.com/langchain-ai/langgraph)**: Enabling sophisticated multi-agent orchestration.
- **[Next.js](https://nextjs.org/)**: A cutting-edge framework for building web applications. - **[Next.js](https://nextjs.org/)**: A cutting-edge framework for building web applications.
### UI Libraries ### UI Libraries
- **[Shadcn](https://ui.shadcn.com/)**: Minimalistic components that power our UI. - **[Shadcn](https://ui.shadcn.com/)**: Minimalistic components that power our UI.
- **[SToneX](https://github.com/stonexer)**: For his invaluable contribution to token-by-token visual effects. - **[SToneX](https://github.com/stonexer)**: For his invaluable contribution to token-by-token visual effects.
These outstanding projects form the backbone of DeerFlow and exemplify the transformative power of open source collaboration. These outstanding projects form the backbone of DeerFlow and exemplify the transformative power of open source collaboration.
### Special Thanks ### Special Thanks
Finally, we want to express our heartfelt gratitude to the core authors of DeerFlow 1.0 and 2.0: Finally, we want to express our heartfelt gratitude to the core authors of DeerFlow 1.0 and 2.0:
- **[Daniel Walnut](https://github.com/hetaoBackend/)** - **[Daniel Walnut](https://github.com/hetaoBackend/)**

View File

@ -44,7 +44,9 @@ export function Welcome({
{/* <div className={cn("inline-block", !waved ? "animate-wave" : "")}> {/* <div className={cn("inline-block", !waved ? "animate-wave" : "")}>
{isUltra ? "🚀" : "👋"} {isUltra ? "🚀" : "👋"}
</div> */} </div> */}
<AuroraText className="text-[#150033] text-[18px]" colors={colors}>{t.welcome.greeting}</AuroraText> <AuroraText className="text-[18px] text-[#150033]" colors={colors}>
{t.welcome.greeting}
</AuroraText>
</div> </div>
)} )}
</div> </div>

View File

@ -106,31 +106,36 @@ export const enUS: Translations = {
suggestions: [ suggestions: [
{ {
suggestion: "Paper Writing", suggestion: "Paper Writing",
prompt: "Write an academic paper about [topic], including abstract, introduction, body and references.", prompt:
"Write an academic paper about [topic], including abstract, introduction, body and references.",
icon: PenLineIcon, icon: PenLineIcon,
skill_id: "paper-writing", skill_id: "paper-writing",
}, },
{ {
suggestion: "Report Generation", suggestion: "Report Generation",
prompt: "Analyze [topic] in depth and generate a well-structured research report.", prompt:
"Analyze [topic] in depth and generate a well-structured research report.",
icon: MicroscopeIcon, icon: MicroscopeIcon,
skill_id: "report-generation", skill_id: "report-generation",
}, },
{ {
suggestion: "Copywriting", suggestion: "Copywriting",
prompt: "Create a complete planning proposal and promotional copy for [project/event].", prompt:
"Create a complete planning proposal and promotional copy for [project/event].",
icon: ShapesIcon, icon: ShapesIcon,
skill_id: "planning-copywriting", skill_id: "planning-copywriting",
}, },
{ {
suggestion: "PPT Generation", suggestion: "PPT Generation",
prompt: "Generate a PPT presentation outline and content about [topic].", prompt:
"Generate a PPT presentation outline and content about [topic].",
icon: GraduationCapIcon, icon: GraduationCapIcon,
skill_id: "ppt-generation", skill_id: "ppt-generation",
}, },
{ {
suggestion: "Document Processing", suggestion: "Document Processing",
prompt: "Process [document] with reading, summarizing, translating or format conversion.", prompt:
"Process [document] with reading, summarizing, translating or format conversion.",
icon: CompassIcon, icon: CompassIcon,
skill_id: "document-processing", skill_id: "document-processing",
}, },

View File

@ -101,7 +101,8 @@ export const zhCN: Translations = {
suggestions: [ suggestions: [
{ {
suggestion: "论文写作", suggestion: "论文写作",
prompt: "撰写一篇关于[主题]的学术论文,包含摘要、引言、正文和参考文献。", prompt:
"撰写一篇关于[主题]的学术论文,包含摘要、引言、正文和参考文献。",
icon: PenLineIcon, icon: PenLineIcon,
skill_id: "1", skill_id: "1",
}, },

View File

@ -8,14 +8,12 @@ export async function loadMCPConfig() {
} }
export async function updateMCPConfig(config: MCPConfig) { export async function updateMCPConfig(config: MCPConfig) {
const response = await fetch(`${getBackendBaseURL()}/api/mcp/config`, const response = await fetch(`${getBackendBaseURL()}/api/mcp/config`, {
{
method: "PUT", method: "PUT",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(config), body: JSON.stringify(config),
}, });
);
return response.json(); return response.json();
} }

View File

@ -379,7 +379,8 @@ export function useThreads(
// Preserve prior semantics: if a non-positive limit is explicitly provided, // Preserve prior semantics: if a non-positive limit is explicitly provided,
// delegate to a single search call with the original parameters. // delegate to a single search call with the original parameters.
if (maxResults !== undefined && maxResults <= 0) { if (maxResults !== undefined && maxResults <= 0) {
const response = await apiClient.threads.search<AgentThreadState>(params); const response =
await apiClient.threads.search<AgentThreadState>(params);
return response as AgentThread[]; return response as AgentThread[];
} }

View File

@ -1,10 +1,10 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from "react";
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from "next/navigation";
// 消息类型常量 // 消息类型常量
const MESSAGE_TYPES = { const MESSAGE_TYPES = {
SELECT_SKILL: 'selectSkill', SELECT_SKILL: "selectSkill",
OPEN_SKILL_DIALOG: 'openSkillDialog', OPEN_SKILL_DIALOG: "openSkillDialog",
} as const; } as const;
// Skill 数据类型 // Skill 数据类型
@ -23,17 +23,22 @@ interface UseIframeSkillReturn {
export function useIframeSkill(): UseIframeSkillReturn { export function useIframeSkill(): UseIframeSkillReturn {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const skillIdFromQuery = searchParams.get('skill_id'); const skillIdFromQuery = searchParams.get("skill_id");
const titleFromQuery = searchParams.get('title'); const titleFromQuery = searchParams.get("title");
const [selectedSkill, setSelectedSkill] = useState<SkillData | null>(null); const [selectedSkill, setSelectedSkill] = useState<SkillData | null>(null);
// 监听 query 参数变化 // 监听 query 参数变化
useEffect(() => { useEffect(() => {
console.log('[useIframeSkill] Query params changed - skill_id:', skillIdFromQuery, 'title:', titleFromQuery); console.log(
"[useIframeSkill] Query params changed - skill_id:",
skillIdFromQuery,
"title:",
titleFromQuery,
);
if (skillIdFromQuery && titleFromQuery) { if (skillIdFromQuery && titleFromQuery) {
const skill = { skill_id: skillIdFromQuery, title: titleFromQuery }; const skill = { skill_id: skillIdFromQuery, title: titleFromQuery };
console.log('[useIframeSkill] Setting skill from URL:', skill); console.log("[useIframeSkill] Setting skill from URL:", skill);
setSelectedSkill(skill); setSelectedSkill(skill);
} }
}, [skillIdFromQuery, titleFromQuery]); }, [skillIdFromQuery, titleFromQuery]);
@ -41,15 +46,18 @@ export function useIframeSkill(): UseIframeSkillReturn {
// 发送选择预定义 skill // 发送选择预定义 skill
const sendSelectSkill = useCallback((skill_id: string) => { const sendSelectSkill = useCallback((skill_id: string) => {
const message = { type: MESSAGE_TYPES.SELECT_SKILL, skill_id }; const message = { type: MESSAGE_TYPES.SELECT_SKILL, skill_id };
console.log('[useIframeSkill] sendSelectSkill:', message); console.log("[useIframeSkill] sendSelectSkill:", message);
window.parent.postMessage(message, '*'); window.parent.postMessage(message, "*");
}, []); }, []);
// 打开 skill 选择对话框 // 打开 skill 选择对话框
const openSkillDialog = useCallback(() => { const openSkillDialog = useCallback(() => {
const message = { type: MESSAGE_TYPES.OPEN_SKILL_DIALOG, openSkillDialog: true }; const message = {
console.log('[useIframeSkill] openSkillDialog:', message); type: MESSAGE_TYPES.OPEN_SKILL_DIALOG,
window.parent.postMessage(message, '*'); openSkillDialog: true,
};
console.log("[useIframeSkill] openSkillDialog:", message);
window.parent.postMessage(message, "*");
}, []); }, []);
// 清除选中 // 清除选中

View File

@ -1,19 +1,21 @@
import * as React from "react" import * as React from "react";
const MOBILE_BREAKPOINT = 768 const MOBILE_BREAKPOINT = 768;
export function useIsMobile() { export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined) const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
undefined,
);
React.useEffect(() => { React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
const onChange = () => { const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
} };
mql.addEventListener("change", onChange) mql.addEventListener("change", onChange);
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
return () => mql.removeEventListener("change", onChange) return () => mql.removeEventListener("change", onChange);
}, []) }, []);
return !!isMobile return !!isMobile;
} }

View File

@ -72,8 +72,9 @@
@theme { @theme {
--font-sans: --font-sans:
"Microsoft YaHei", "微软雅黑", var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif, "Microsoft YaHei", "微软雅黑", var(--font-geist-sans), ui-sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol", "Noto Color Emoji";
--animate-fade-in: fade-in 1.1s; --animate-fade-in: fade-in 1.1s;
@keyframes fade-in { @keyframes fade-in {
@ -225,7 +226,7 @@
:root { :root {
--radius: 0.625rem; --radius: 0.625rem;
--background: #F9F8FA; --background: #f9f8fa;
--foreground: oklch(0.145 0 0); --foreground: oklch(0.145 0 0);
/* --foreground: #00000066; */ /* --foreground: #00000066; */
/* --card: oklch(1 0.0098 87.47); */ /* --card: oklch(1 0.0098 87.47); */
@ -237,13 +238,13 @@
--primary: oklch(0 0 0); --primary: oklch(0 0 0);
--primary-foreground: oklch(0.985 0 0); --primary-foreground: oklch(0.985 0 0);
/* --secondary: oklch(0.9455 0.0098 87.47); */ /* --secondary: oklch(0.9455 0.0098 87.47); */
--secondary: #1500331A; --secondary: #1500331a;
--secondary-foreground: oklch(0.205 0 0); --secondary-foreground: oklch(0.205 0 0);
/* --muted: oklch(0.97 0.0098 87.47); */ /* --muted: oklch(0.97 0.0098 87.47); */
--muted: #1500331A; --muted: #1500331a;
--muted-foreground: oklch(0.556 0 0); --muted-foreground: oklch(0.556 0 0);
/* --accent: oklch(0.94 0.0098 87.47); */ /* --accent: oklch(0.94 0.0098 87.47); */
--accent: #1500331A; --accent: #1500331a;
--accent-foreground: oklch(0.205 0 0); --accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325); --destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0.0098 87.47); --border: oklch(0.922 0.0098 87.47);
@ -419,7 +420,6 @@ p{
font-size: 14px; font-size: 14px;
} }
/* 列表项 - 14px */ /* 列表项 - 14px */
[data-streamdown="list-item"] { [data-streamdown="list-item"] {
font-size: 14px; font-size: 14px;
@ -432,6 +432,7 @@ p{
font-size: 20px; font-size: 20px;
} }
/* 二三级标题 - 16px */ /* 二三级标题 - 16px */
[data-streamdown="heading-2"],[data-streamdown="heading-3"] { [data-streamdown="heading-2"],
[data-streamdown="heading-3"] {
font-size: 16px; font-size: 16px;
} }