Chat / AI
marker
$
npx shadcn@latest add @preactcn/markerMarkers
Markers
A default marker
Marker with icon
Marker with a spinner
Marker with shimmer effect
Thinking...
Marker as a linkRhea joined the chat
Olivia Rose left the chat
Marker with icon at the top
MarkerExample
function MarkerExample() {
return (
<Example title="Markers" className="gap-8">
<Marker>
<MarkerContent>A default marker</MarkerContent>
</Marker>
<Marker>
<MarkerIcon>
<FileTextIcon />
</MarkerIcon>
<MarkerContent>Marker with icon</MarkerContent>
</Marker>
<Marker role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent>Marker with a spinner</MarkerContent>
</Marker>
<Marker role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent className="shimmer">
Marker with shimmer effect
</MarkerContent>
</Marker>
<Marker role="status">
<MarkerContent className="shimmer">Thinking...</MarkerContent>
</Marker>
<Marker asChild>
<a href="#">
<MarkerIcon>
<GitBranchIcon />
</MarkerIcon>
<MarkerContent>Marker as a link</MarkerContent>
</a>
</Marker>
<Marker asChild>
<button
onClick={() => toast("You clicked the button")}
className="transition-colors hover:text-foreground"
>
<MarkerIcon>
<ClockIcon />
</MarkerIcon>
<MarkerContent className="flex-1">
<div>Marker as a button</div>
</MarkerContent>
<MarkerIcon>
<ChevronRightIcon />
</MarkerIcon>
</button>
</Marker>
<Marker>
<MarkerIcon>
<CircleUserIcon />
</MarkerIcon>
<MarkerContent>Rhea joined the chat</MarkerContent>
</Marker>
<Marker className="justify-center">
<MarkerContent>
<strong className="font-medium">Olivia Rose</strong> left the chat
</MarkerContent>
</Marker>
<Marker className="flex-col">
<MarkerIcon>
<FileTextIcon />
</MarkerIcon>
<MarkerContent>Marker with icon at the top</MarkerContent>
</Marker>
</Example>
)
}Border
Border
Switched to release-candidate
Reviewed 8 related files
Opened implementation notes
MarkerBorder
function MarkerBorder() {
return (
<Example title="Border" className="gap-3">
<Marker variant="border">
<MarkerIcon>
<GitBranchIcon />
</MarkerIcon>
<MarkerContent>Switched to release-candidate</MarkerContent>
</Marker>
<Marker variant="border">
<MarkerIcon>
<SearchIcon />
</MarkerIcon>
<MarkerContent>Reviewed 8 related files</MarkerContent>
</Marker>
<Marker variant="border">
<MarkerIcon>
<FileTextIcon />
</MarkerIcon>
<MarkerContent>Opened implementation notes</MarkerContent>
</Marker>
</Example>
)
}Separator
Separator
MarkerSeparator
function MarkerSeparator() {
return (
<Example title="Separator" className="gap-8">
<Marker variant="separator">
<MarkerContent>Worked for 42s</MarkerContent>
</Marker>
<Marker variant="separator" role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent>Compacting conversation</MarkerContent>
</Marker>
<Marker variant="separator" role="status">
<MarkerContent className="shimmer">Reading 4 files</MarkerContent>
</Marker>
<Marker variant="separator">
<MarkerIcon>
<CheckIcon />
</MarkerIcon>
<MarkerContent>Conversation compacted</MarkerContent>
</Marker>
<Marker variant="separator">
<MarkerContent>
With a <a href="#">link to learn more</a>
</MarkerContent>
</Marker>
<Marker variant="separator">
<MarkerContent>
<Button variant="outline">
<GitBranchIcon />
Button
</Button>
</MarkerContent>
</Marker>
</Example>
)
}Accordion
Accordion
MarkerAccordion
function MarkerAccordion() {
return (
<Example title="Accordion" containerClassName="min-h-72">
<Accordion type="single" collapsible>
<AccordionItem value="1">
<AccordionTrigger>
<Marker>
<MarkerIcon>
<ClockIcon />
</MarkerIcon>
<MarkerContent>Worked for 42s</MarkerContent>
</Marker>
</AccordionTrigger>
<AccordionContent>
<p className="text-sm text-muted-foreground">
The user asked for a list of all the files in the current
directory. The assistant responded with a list of all the files in
the current directory.
</p>
</AccordionContent>
</AccordionItem>
</Accordion>
</Example>
)
}Drawer
Drawer
MarkerDrawer
function MarkerDrawer() {
return (
<Example title="Drawer">
<Drawer direction="right">
<Marker variant="separator">
<DrawerTrigger asChild>
<Button variant="outline">
<SearchIcon
data-icon="inline-start"
/>
Explored 4 files
</Button>
</DrawerTrigger>
</Marker>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>File Activity</DrawerTitle>
<DrawerDescription>
Files read while preparing the response.
</DrawerDescription>
</DrawerHeader>
<div className="grid gap-3 px-4 text-sm">
<div className="flex items-center justify-between gap-4 rounded-md border px-3 py-2">
<span className="truncate">app/chat/page.tsx</span>
<span className="text-muted-foreground">read</span>
</div>
<div className="flex items-center justify-between gap-4 rounded-md border px-3 py-2">
<span className="truncate">components/message.tsx</span>
<span className="text-muted-foreground">read</span>
</div>
<div className="flex items-center justify-between gap-4 rounded-md border px-3 py-2">
<span className="truncate">lib/ai.ts</span>
<span className="text-muted-foreground">read</span>
</div>
</div>
<DrawerFooter>
<DrawerClose asChild>
<Button variant="outline">Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
</Example>
)
}Full example source
marker-example.tsx
import { toast } from "@/lib/shims/sonner"
import {
Example,
ExampleWrapper,
} from "@/components/example"
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
import { Button } from "@/components/ui/button"
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker"
import { Spinner } from "@/components/ui/spinner"
import {
CheckIcon,
ChevronRightIcon,
CircleUserIcon,
ClockIcon,
FileTextIcon,
GitBranchIcon,
SearchIcon,
} from "lucide-preact"
export default function MarkerExamples() {
return (
<ExampleWrapper>
<MarkerExample />
<MarkerBorder />
<MarkerSeparator />
<MarkerAccordion />
<MarkerDrawer />
</ExampleWrapper>
)
}
function MarkerExample() {
return (
<Example title="Markers" className="gap-8">
<Marker>
<MarkerContent>A default marker</MarkerContent>
</Marker>
<Marker>
<MarkerIcon>
<FileTextIcon />
</MarkerIcon>
<MarkerContent>Marker with icon</MarkerContent>
</Marker>
<Marker role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent>Marker with a spinner</MarkerContent>
</Marker>
<Marker role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent className="shimmer">
Marker with shimmer effect
</MarkerContent>
</Marker>
<Marker role="status">
<MarkerContent className="shimmer">Thinking...</MarkerContent>
</Marker>
<Marker asChild>
<a href="#">
<MarkerIcon>
<GitBranchIcon />
</MarkerIcon>
<MarkerContent>Marker as a link</MarkerContent>
</a>
</Marker>
<Marker asChild>
<button
onClick={() => toast("You clicked the button")}
className="transition-colors hover:text-foreground"
>
<MarkerIcon>
<ClockIcon />
</MarkerIcon>
<MarkerContent className="flex-1">
<div>Marker as a button</div>
</MarkerContent>
<MarkerIcon>
<ChevronRightIcon />
</MarkerIcon>
</button>
</Marker>
<Marker>
<MarkerIcon>
<CircleUserIcon />
</MarkerIcon>
<MarkerContent>Rhea joined the chat</MarkerContent>
</Marker>
<Marker className="justify-center">
<MarkerContent>
<strong className="font-medium">Olivia Rose</strong> left the chat
</MarkerContent>
</Marker>
<Marker className="flex-col">
<MarkerIcon>
<FileTextIcon />
</MarkerIcon>
<MarkerContent>Marker with icon at the top</MarkerContent>
</Marker>
</Example>
)
}
function MarkerBorder() {
return (
<Example title="Border" className="gap-3">
<Marker variant="border">
<MarkerIcon>
<GitBranchIcon />
</MarkerIcon>
<MarkerContent>Switched to release-candidate</MarkerContent>
</Marker>
<Marker variant="border">
<MarkerIcon>
<SearchIcon />
</MarkerIcon>
<MarkerContent>Reviewed 8 related files</MarkerContent>
</Marker>
<Marker variant="border">
<MarkerIcon>
<FileTextIcon />
</MarkerIcon>
<MarkerContent>Opened implementation notes</MarkerContent>
</Marker>
</Example>
)
}
function MarkerSeparator() {
return (
<Example title="Separator" className="gap-8">
<Marker variant="separator">
<MarkerContent>Worked for 42s</MarkerContent>
</Marker>
<Marker variant="separator" role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent>Compacting conversation</MarkerContent>
</Marker>
<Marker variant="separator" role="status">
<MarkerContent className="shimmer">Reading 4 files</MarkerContent>
</Marker>
<Marker variant="separator">
<MarkerIcon>
<CheckIcon />
</MarkerIcon>
<MarkerContent>Conversation compacted</MarkerContent>
</Marker>
<Marker variant="separator">
<MarkerContent>
With a <a href="#">link to learn more</a>
</MarkerContent>
</Marker>
<Marker variant="separator">
<MarkerContent>
<Button variant="outline">
<GitBranchIcon />
Button
</Button>
</MarkerContent>
</Marker>
</Example>
)
}
function MarkerAccordion() {
return (
<Example title="Accordion" containerClassName="min-h-72">
<Accordion type="single" collapsible>
<AccordionItem value="1">
<AccordionTrigger>
<Marker>
<MarkerIcon>
<ClockIcon />
</MarkerIcon>
<MarkerContent>Worked for 42s</MarkerContent>
</Marker>
</AccordionTrigger>
<AccordionContent>
<p className="text-sm text-muted-foreground">
The user asked for a list of all the files in the current
directory. The assistant responded with a list of all the files in
the current directory.
</p>
</AccordionContent>
</AccordionItem>
</Accordion>
</Example>
)
}
function MarkerDrawer() {
return (
<Example title="Drawer">
<Drawer direction="right">
<Marker variant="separator">
<DrawerTrigger asChild>
<Button variant="outline">
<SearchIcon
data-icon="inline-start"
/>
Explored 4 files
</Button>
</DrawerTrigger>
</Marker>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>File Activity</DrawerTitle>
<DrawerDescription>
Files read while preparing the response.
</DrawerDescription>
</DrawerHeader>
<div className="grid gap-3 px-4 text-sm">
<div className="flex items-center justify-between gap-4 rounded-md border px-3 py-2">
<span className="truncate">app/chat/page.tsx</span>
<span className="text-muted-foreground">read</span>
</div>
<div className="flex items-center justify-between gap-4 rounded-md border px-3 py-2">
<span className="truncate">components/message.tsx</span>
<span className="text-muted-foreground">read</span>
</div>
<div className="flex items-center justify-between gap-4 rounded-md border px-3 py-2">
<span className="truncate">lib/ai.ts</span>
<span className="text-muted-foreground">read</span>
</div>
</div>
<DrawerFooter>
<DrawerClose asChild>
<Button variant="outline">Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
</Example>
)
}