Chat Dock Section
Sidebar section that hosts a compact, scrollable chat thread alongside a spatial workspace — slotted into a dock or rail to keep an AI assistant in view. Install Chat Dock Section from the VLLNT UI registry with the shadcn CLI.
Sidebar section that hosts a compact, scrollable chat thread alongside a spatial workspace — slotted into a dock or rail to keep an AI assistant in view. Part of the ai family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.
Preview
Switch between light and dark to inspect the embedded Storybook preview.
Installation
Add Chat Dock Section to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/chat-dock-section.jsonSource
import { ArrowUpRight, MessageSquareText } from "lucide-react";
import type { ReactNode } from "react";
import { cn } from "../../lib/utils";
import { Button } from "../button/button";
export type ChatDockMessage = {
body: ReactNode;
id: string;
speaker: ReactNode;
};
export type ChatDockSectionProps = React.ComponentPropsWithoutRef<"section"> & {
composerPlaceholder?: string;
contextLabel?: ReactNode;
messages: ChatDockMessage[];
title?: ReactNode;
};
const ChatDockSection = ({
className,
composerPlaceholder = "Ask about runs, errors, or pending work…",
contextLabel,
messages,
ref,
title = "Assistant",
...props
}: ChatDockSectionProps & { ref?: React.Ref<HTMLElement> }) => (
<section
className={cn(
"flex flex-col gap-4 rounded-2xl border border-border/70 bg-background/75 p-4 shadow-[0_10px_35px_oklch(var(--foreground)/0.06)] backdrop-blur-xl",
className,
)}
ref={ref}
{...props}
>
<div className="flex items-start justify-between gap-3">
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm font-medium text-foreground">
<MessageSquareText className="size-4 text-primary" />
{title}
</div>
{contextLabel ? (
<div className="text-xs uppercase tracking-[0.24em] text-muted-foreground">
{contextLabel}
</div>
) : null}
</div>
<Button size="sm" type="button" variant="ghost">
Open thread
<ArrowUpRight className="size-4" />
</Button>
</div>
<div className="space-y-3">
{messages.map((message) => (
<div
className="rounded-xl border border-border/60 bg-background/85 px-3 py-2"
key={message.id}
>
<div className="text-[11px] font-medium uppercase tracking-[0.18em] text-muted-foreground">
{message.speaker}
</div>
<div className="mt-2 text-sm leading-6 text-foreground">
{message.body}
</div>
</div>
))}
</div>
<div className="rounded-xl border border-dashed border-border/80 bg-background/70 p-3 text-sm text-muted-foreground">
{composerPlaceholder}
</div>
</section>
);
ChatDockSection.displayName = "ChatDockSection";
export { ChatDockSection };
Stories
Explore every variant and state in the interactive Storybook:
Preview
Switch between light and dark to inspect the embedded Storybook preview.