feat: add skeleton

This commit is contained in:
Henry Li
2026-02-02 09:05:33 +08:00
parent 1eb4da6c75
commit 7da0a03dd0
2 changed files with 90 additions and 3 deletions

View File

@@ -1,4 +1,79 @@
export function MessageListSkeleton() {
// TODO: Add a loading state
return null;
import { Skeleton } from "@/components/ui/skeleton";
const STAGGER_MS = 60;
function SkeletonBar({
className,
style,
originRight,
}: {
className?: string;
style?: React.CSSProperties;
originRight?: boolean;
}) {
return (
<div
className={`animate-skeleton-entrance fill-mode-[forwards] overflow-hidden rounded-md ${originRight ? "origin-[right]" : "origin-[left]"} ${className ?? ""}`}
style={{ opacity: 0, ...style }}
>
<Skeleton className="h-full w-full rounded-md" />
</div>
);
}
export function MessageListSkeleton() {
let index = 0;
return (
<div className="flex w-full max-w-(--container-width-md) flex-col gap-12 p-8 pt-16">
<div
role="human-message"
className="flex w-[50%] flex-col items-end gap-2 self-end"
>
<SkeletonBar
className="h-6 w-full"
originRight
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
<SkeletonBar
className="h-6 w-[80%]"
originRight
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
</div>
<div role="assistant-message" className="flex flex-col gap-2">
<SkeletonBar
className="h-6 w-full"
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
<SkeletonBar
className="h-6 w-full"
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
<SkeletonBar
className="h-6 w-[70%]"
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
<SkeletonBar
className="h-6 w-full"
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
<SkeletonBar
className="h-6 w-full"
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
<SkeletonBar
className="h-6 w-full"
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
<SkeletonBar
className="h-6 w-[60%]"
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
<SkeletonBar
className="h-6 w-[40%]"
style={{ animationDelay: `${index++ * STAGGER_MS}ms` }}
/>
</div>
</div>
);
}

View File

@@ -92,6 +92,18 @@
transform: translateY(-8px);
}
}
--animate-skeleton-entrance: skeleton-entrance 0.35s ease-out forwards;
@keyframes skeleton-entrance {
0% {
opacity: 0;
transform: scaleX(0);
}
100% {
opacity: 1;
transform: scaleX(1);
}
}
}
@theme inline {