mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-07-07 22:18:39 +00:00
* feat(@shadcn/react): add message-scroller package Add the @shadcn/react headless primitives package with MessageScroller scroll anchoring, streaming follow, history prepend, and jump-to-message behavior. Includes geometry helpers, use-render utility, and unit, browser, and perf tests. * feat(registry): add chat components Add MessageScroller, Message, Bubble, Attachment, and Marker registry sources for base and radix, style variants, preview-03 chat blocks, and registry index wiring. * feat(v4): integrate chat components into docs site Wire chat components into the v4 app with docs routes, example preview pages, message part renderers, markdown support, registry build updates, and supporting lib utilities. * feat(examples): add chat component demos Add base and radix example demos for MessageScroller, Message, Bubble, Attachment, Marker, scroll-fade, and shimmer. * docs: add chat component documentation Add component and utility docs for the chat component set, update docs navigation, and add the June 2026 chat components changelog entry. * chore: regenerate registry JSON output Rebuild public registry artifacts for all style variants with the new chat components. * chore(release): add @shadcn/react publish and CI pipeline Add Changesets prerelease workflow, browser test job, RELEASING docs, and monorepo wiring for publishing @shadcn/react independently from the shadcn CLI. * docs: fix display of component preview on mobile * fix * fix * docs: add message scroller docs * style: format * fix
946 lines
29 KiB
TypeScript
946 lines
29 KiB
TypeScript
import { createChat } from "@/lib/ai"
|
|
import { type MessagePartRenderProps } from "@/components/message-parts"
|
|
import {
|
|
Example,
|
|
ExampleWrapper,
|
|
} from "@/registry/bases/base/components/example"
|
|
import {
|
|
Attachment,
|
|
AttachmentAction,
|
|
AttachmentActions,
|
|
AttachmentContent,
|
|
AttachmentDescription,
|
|
AttachmentGroup,
|
|
AttachmentMedia,
|
|
AttachmentTitle,
|
|
} from "@/registry/bases/base/ui/attachment"
|
|
import {
|
|
Avatar,
|
|
AvatarFallback,
|
|
AvatarImage,
|
|
} from "@/registry/bases/base/ui/avatar"
|
|
import {
|
|
Bubble,
|
|
BubbleContent,
|
|
BubbleGroup,
|
|
BubbleReactions,
|
|
} from "@/registry/bases/base/ui/bubble"
|
|
import { Button } from "@/registry/bases/base/ui/button"
|
|
import {
|
|
Marker,
|
|
MarkerContent,
|
|
MarkerIcon,
|
|
} from "@/registry/bases/base/ui/marker"
|
|
import {
|
|
Message,
|
|
MessageAvatar,
|
|
MessageContent,
|
|
MessageFooter,
|
|
MessageGroup,
|
|
MessageHeader,
|
|
} from "@/registry/bases/base/ui/message"
|
|
import { Spinner } from "@/registry/bases/base/ui/spinner"
|
|
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"
|
|
|
|
type MessagePartsData = {
|
|
deployment: {
|
|
status: "healthy" | "degraded"
|
|
region: string
|
|
latency: number
|
|
}
|
|
}
|
|
|
|
type MessagePartsTools = {
|
|
getDeploymentHealth: {
|
|
input: {
|
|
project: string
|
|
region: string
|
|
}
|
|
output: {
|
|
status: "healthy" | "degraded"
|
|
p95: number
|
|
errors: number
|
|
recommendation: string
|
|
}
|
|
}
|
|
}
|
|
|
|
const messagePartsChat = createChat<
|
|
unknown,
|
|
MessagePartsData,
|
|
MessagePartsTools
|
|
>()
|
|
.user("Can you review this screenshot and check the deployment?", {
|
|
files: [
|
|
{
|
|
mediaType: "image/jpeg",
|
|
filename: "checkout-error.jpg",
|
|
url: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=900&auto=format&fit=crop&q=80",
|
|
},
|
|
],
|
|
})
|
|
.assistant(({ writer }) => {
|
|
writer.reasoning(
|
|
"The user included an image and asked for deployment status, so I should inspect the screenshot context and call the deployment health tool before answering."
|
|
)
|
|
|
|
const deployment = writer.tool("getDeploymentHealth", {
|
|
title: "Checking deployment health",
|
|
input: {
|
|
project: "checkout",
|
|
region: "iad1",
|
|
},
|
|
})
|
|
|
|
deployment.output({
|
|
status: "healthy",
|
|
p95: 186,
|
|
errors: 2,
|
|
recommendation: "Keep the deployment live and monitor error rate.",
|
|
})
|
|
|
|
writer.data({
|
|
type: "data-deployment",
|
|
id: "deployment-health",
|
|
data: {
|
|
status: "healthy",
|
|
region: "iad1",
|
|
latency: 186,
|
|
},
|
|
})
|
|
writer.text(
|
|
"The deployment looks healthy. P95 latency is **186ms** with **2 recent errors**, so I would keep it live and monitor error rate for the next 15 minutes."
|
|
)
|
|
writer.sourceUrl({
|
|
sourceId: "source-deployment-docs",
|
|
title: "Deployment health checks",
|
|
url: "https://vercel.com/docs/deployments",
|
|
})
|
|
writer.sourceDocument({
|
|
sourceId: "source-runbook",
|
|
title: "Checkout release runbook",
|
|
filename: "checkout-release-runbook.md",
|
|
mediaType: "text/markdown",
|
|
})
|
|
})
|
|
|
|
const messagePartsMessages = messagePartsChat.get()
|
|
|
|
type MessagePartsMessage = (typeof messagePartsMessages)[number]
|
|
type MessagePartsPart = MessagePartsMessage["parts"][number]
|
|
|
|
export default function MessageExample() {
|
|
return (
|
|
<ExampleWrapper>
|
|
<MessageDefault />
|
|
<MessageWithAvatar />
|
|
<MessageGroupExample />
|
|
<MessageGroupChat />
|
|
<MessageHeaderFooter />
|
|
<MessageActions />
|
|
<MessageAttachment />
|
|
<MessageAttachmentGroup />
|
|
</ExampleWrapper>
|
|
)
|
|
}
|
|
|
|
function MessageTextPart({
|
|
part,
|
|
message,
|
|
}: MessagePartRenderProps<MessagePartsPart, MessagePartsMessage>) {
|
|
if (part.type !== "text") {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<Bubble
|
|
align={message.role === "user" ? "end" : "start"}
|
|
variant={message.role === "user" ? "default" : "ghost"}
|
|
>
|
|
<BubbleContent>
|
|
<span className="whitespace-pre-wrap">{part.text}</span>
|
|
</BubbleContent>
|
|
</Bubble>
|
|
)
|
|
}
|
|
|
|
function MessageDefault() {
|
|
return (
|
|
<Example title="Message">
|
|
<div className="flex w-full max-w-md min-w-0 flex-col gap-10">
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Bubble>
|
|
<BubbleContent>Deploying to prod real quick.</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>It's 4:55 PM. On a Friday.</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Bubble>
|
|
<BubbleContent>It's a one-line change.</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageContent>
|
|
<BubbleGroup>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
It's always a one-line change 😭. Make sure to run the
|
|
tests this time.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>Alright, go for it.</BubbleContent>
|
|
</Bubble>
|
|
</BubbleGroup>
|
|
</MessageContent>
|
|
</Message>
|
|
</div>
|
|
</Example>
|
|
)
|
|
}
|
|
|
|
function MessageWithAvatar() {
|
|
return (
|
|
<Example title="Avatar">
|
|
<div className="flex w-full max-w-md flex-col gap-10">
|
|
<Message>
|
|
<MessageAvatar>
|
|
<Avatar>
|
|
<AvatarImage
|
|
src="https://github.com/shadcn.png"
|
|
alt="@shadcn"
|
|
className="grayscale"
|
|
/>
|
|
<AvatarFallback>CN</AvatarFallback>
|
|
</Avatar>
|
|
</MessageAvatar>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>Something went wrong. Any idea?</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageAvatar>
|
|
<Avatar>
|
|
<AvatarImage
|
|
src="https://github.com/evilrabbit.png"
|
|
alt="@evilrabbit"
|
|
/>
|
|
<AvatarFallback>ER</AvatarFallback>
|
|
</Avatar>
|
|
</MessageAvatar>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
I checked the latest deployment and found the build failed
|
|
during dependency installation.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="end">
|
|
<MessageAvatar>
|
|
<Avatar>
|
|
<AvatarImage
|
|
src="https://github.com/evilrabbit.png"
|
|
alt="@evilrabbit"
|
|
/>
|
|
<AvatarFallback>ER</AvatarFallback>
|
|
</Avatar>
|
|
</MessageAvatar>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>Something went wrong. Any idea?</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageAvatar>
|
|
<Avatar>
|
|
<AvatarImage
|
|
src="https://avatar.vercel.sh/shadcn"
|
|
alt="@shadcn"
|
|
/>
|
|
<AvatarFallback>CN</AvatarFallback>
|
|
</Avatar>
|
|
</MessageAvatar>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent className="space-y-2">
|
|
<div>
|
|
I checked the latest deployment and found the build failed
|
|
during dependency installation.
|
|
</div>
|
|
<div>Can you share the exact error message from the logs?</div>
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageAvatar>
|
|
<Avatar>
|
|
<AvatarFallback>CN</AvatarFallback>
|
|
</Avatar>
|
|
</MessageAvatar>
|
|
<MessageContent>
|
|
<Bubble variant="destructive">
|
|
<BubbleContent>Something went wrong. Any idea?</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
</div>
|
|
</Example>
|
|
)
|
|
}
|
|
|
|
function MessageGroupExample() {
|
|
return (
|
|
<Example title="Group">
|
|
<div className="flex w-full max-w-md flex-col gap-10">
|
|
<MessageGroup>
|
|
<Message>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>I checked the registry addresses.</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
The component and example JSON now live under the UI registry.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
</MessageGroup>
|
|
<MessageGroup>
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>I checked the registry addresses.</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
The component and example JSON now live under the UI registry.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
</MessageGroup>
|
|
<MessageGroup>
|
|
<Message align="end">
|
|
<MessageAvatar />
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>I checked the registry addresses.</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="end">
|
|
<MessageAvatar>
|
|
<Avatar>
|
|
<AvatarImage
|
|
src="https://avatar.vercel.sh/shadcn"
|
|
alt="@shadcn"
|
|
/>
|
|
<AvatarFallback>CN</AvatarFallback>
|
|
</Avatar>
|
|
</MessageAvatar>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
The component and example JSON now live under the UI registry.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
</MessageGroup>
|
|
<MessageGroup>
|
|
<Message>
|
|
<MessageAvatar />
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>I checked the registry addresses.</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageAvatar>
|
|
<Avatar>
|
|
<AvatarImage
|
|
src="https://avatar.vercel.sh/shadcn"
|
|
alt="@shadcn"
|
|
/>
|
|
<AvatarFallback>CN</AvatarFallback>
|
|
</Avatar>
|
|
</MessageAvatar>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
The component and example JSON now live under the UI registry.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
</MessageGroup>
|
|
</div>
|
|
</Example>
|
|
)
|
|
}
|
|
|
|
function MessageGroupChat() {
|
|
return (
|
|
<Example title="Group Chat">
|
|
<div className="flex w-full max-w-md flex-col gap-10">
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Bubble variant="tinted">
|
|
<BubbleContent>
|
|
Why is the dashboard showing zero users
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<MessageGroup>
|
|
<Message>
|
|
<MessageAvatar />
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>Define "zero."</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageAvatar>
|
|
<Avatar>
|
|
<AvatarImage
|
|
src="https://github.com/evilrabbit.png"
|
|
alt="@evilrabbit"
|
|
/>
|
|
<AvatarFallback>ER</AvatarFallback>
|
|
</Avatar>
|
|
</MessageAvatar>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>What do you mean?</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
</MessageGroup>
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Bubble variant="tinted">
|
|
<BubbleContent>
|
|
<span className="font-medium">@eve</span> can you check?
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<Marker role="status">
|
|
<MarkerIcon>
|
|
<Spinner />
|
|
</MarkerIcon>
|
|
<MarkerContent>Checking the logs...</MarkerContent>
|
|
</Marker>
|
|
</Message>
|
|
<Message>
|
|
<MessageContent>
|
|
<MessageHeader>Eve</MessageHeader>
|
|
<Bubble variant="ghost">
|
|
<BubbleContent>
|
|
<span className="whitespace-pre-wrap">
|
|
{`I found the following error in the logs:
|
|
|
|
- Error: No users found
|
|
- Stack trace:
|
|
- at src/users/services/user-service.ts:123
|
|
- at src/users/services/get-user.ts:230
|
|
|
|
Can you check if the users are being created?`}
|
|
</span>
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
</div>
|
|
</Example>
|
|
)
|
|
}
|
|
|
|
function MessageHeaderFooter() {
|
|
return (
|
|
<Example title="Header and Footer">
|
|
<div className="flex w-full max-w-md flex-col gap-12">
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<MessageHeader className="justify-end">You</MessageHeader>
|
|
<Bubble>
|
|
<BubbleContent>
|
|
Can we keep this style quiet and send the update today?
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<MessageFooter className="gap-2">
|
|
<span>Delivered</span>
|
|
<Button variant="ghost" size="xs">
|
|
Undo
|
|
</Button>
|
|
</MessageFooter>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="start">
|
|
<MessageContent>
|
|
<MessageHeader>
|
|
<span>Olivia</span>
|
|
<span className="ml-auto font-normal">1m ago</span>
|
|
</MessageHeader>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
I checked the logs. The retry finished and the missing invoices
|
|
are included now.
|
|
</BubbleContent>
|
|
<BubbleReactions>
|
|
<Button variant="ghost" size="xs">
|
|
Thanks
|
|
</Button>
|
|
</BubbleReactions>
|
|
</Bubble>
|
|
<MessageFooter className="gap-2">
|
|
<span>From Support queue</span>
|
|
<Button variant="ghost" size="xs">
|
|
Copy
|
|
</Button>
|
|
</MessageFooter>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<MessageHeader>Marina</MessageHeader>
|
|
<Bubble variant="outline">
|
|
<BubbleContent>
|
|
I drafted the reply with the exact filter path and a link to the
|
|
export history.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<MessageFooter className="gap-2">
|
|
<span>Draft ready</span>
|
|
<Button variant="outline" size="xs">
|
|
Review
|
|
</Button>
|
|
<Button size="xs">Send</Button>
|
|
</MessageFooter>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="start">
|
|
<MessageContent>
|
|
<MessageHeader>Assistant note</MessageHeader>
|
|
<Bubble variant="ghost">
|
|
<BubbleContent>
|
|
Ghost messages keep long-form assistant notes aligned with the
|
|
surrounding text instead of adding a frame.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<MessageFooter className="gap-2">
|
|
<span>Internal only</span>
|
|
<Button variant="ghost" size="xs">
|
|
Pin
|
|
</Button>
|
|
</MessageFooter>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<MessageHeader className="justify-end">
|
|
Automation warning
|
|
</MessageHeader>
|
|
<Bubble variant="destructive">
|
|
<BubbleContent>
|
|
The old export link expires in 10 minutes. Regenerate before
|
|
sending this transcript externally.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<MessageFooter className="gap-2">
|
|
<span>Needs action</span>
|
|
<Button variant="destructive" size="xs">
|
|
Regenerate
|
|
</Button>
|
|
</MessageFooter>
|
|
</MessageContent>
|
|
</Message>
|
|
</div>
|
|
</Example>
|
|
)
|
|
}
|
|
|
|
function MessageAttachment() {
|
|
return (
|
|
<Example title="Attachment">
|
|
<div className="flex w-full max-w-sm flex-col gap-8">
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Attachment orientation="vertical">
|
|
<AttachmentMedia variant="image">
|
|
<img
|
|
src="https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=900&auto=format&fit=crop&q=80"
|
|
alt="Workspace"
|
|
/>
|
|
</AttachmentMedia>
|
|
</Attachment>
|
|
<Bubble>
|
|
<BubbleContent>
|
|
Here's the image. Can you add it to the PDF? Use it for the
|
|
cover page.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
Done. Here's the PDF with the image added as the cover
|
|
page.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<Attachment>
|
|
<AttachmentMedia>
|
|
<IconPlaceholder
|
|
lucide="FileTextIcon"
|
|
tabler="IconFileText"
|
|
hugeicons="FileIcon"
|
|
phosphor="FileTextIcon"
|
|
remixicon="RiFileTextLine"
|
|
/>
|
|
</AttachmentMedia>
|
|
<AttachmentContent>
|
|
<AttachmentTitle>sales-dashboard.pdf</AttachmentTitle>
|
|
<AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>
|
|
</AttachmentContent>
|
|
<AttachmentActions>
|
|
<AttachmentAction
|
|
type="button"
|
|
title="Download"
|
|
aria-label="Download"
|
|
size="icon-sm"
|
|
variant="secondary"
|
|
>
|
|
<IconPlaceholder
|
|
lucide="DownloadIcon"
|
|
tabler="IconDownload"
|
|
hugeicons="Download01Icon"
|
|
phosphor="DownloadIcon"
|
|
remixicon="RiDownloadLine"
|
|
/>
|
|
</AttachmentAction>
|
|
</AttachmentActions>
|
|
</Attachment>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Bubble>
|
|
<BubbleContent>Thanks. Looks good.</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
</div>
|
|
</Example>
|
|
)
|
|
}
|
|
|
|
function MessageAttachmentGroup() {
|
|
return (
|
|
<Example title="Attachment Group">
|
|
<div className="flex w-full max-w-sm flex-col gap-8">
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<AttachmentGroup className="w-[80%]">
|
|
<Attachment orientation="vertical">
|
|
<AttachmentMedia variant="image">
|
|
<img
|
|
src="https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=900&auto=format&fit=crop&q=80"
|
|
alt="Workspace"
|
|
/>
|
|
</AttachmentMedia>
|
|
</Attachment>
|
|
<Attachment orientation="vertical">
|
|
<AttachmentMedia variant="image">
|
|
<img
|
|
src="https://images.unsplash.com/photo-1497215728101-856f4ea42174?w=900&auto=format&fit=crop&q=80"
|
|
alt="Desk"
|
|
/>
|
|
</AttachmentMedia>
|
|
</Attachment>
|
|
<Attachment orientation="vertical">
|
|
<AttachmentMedia variant="image">
|
|
<img
|
|
src="https://images.unsplash.com/photo-1497366811353-6870744d04b2?w=900&auto=format&fit=crop&q=80"
|
|
alt="Office"
|
|
/>
|
|
</AttachmentMedia>
|
|
</Attachment>
|
|
</AttachmentGroup>
|
|
<Bubble>
|
|
<BubbleContent>
|
|
Here are the brand photos. Can you add them to the deck?
|
|
</BubbleContent>
|
|
</Bubble>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
Done. Here's the draft deck and the exported assets.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<AttachmentGroup className="w-full">
|
|
<Attachment className="w-64">
|
|
<AttachmentMedia>
|
|
<IconPlaceholder
|
|
lucide="FileTextIcon"
|
|
tabler="IconFileText"
|
|
hugeicons="FileIcon"
|
|
phosphor="FileTextIcon"
|
|
remixicon="RiFileTextLine"
|
|
/>
|
|
</AttachmentMedia>
|
|
<AttachmentContent>
|
|
<AttachmentTitle>brand-deck.pdf</AttachmentTitle>
|
|
<AttachmentDescription>PDF · 3.1 MB</AttachmentDescription>
|
|
</AttachmentContent>
|
|
<AttachmentActions>
|
|
<AttachmentAction
|
|
type="button"
|
|
title="Download"
|
|
aria-label="Download brand-deck.pdf"
|
|
size="icon-sm"
|
|
variant="secondary"
|
|
>
|
|
<IconPlaceholder
|
|
lucide="DownloadIcon"
|
|
tabler="IconDownload"
|
|
hugeicons="Download01Icon"
|
|
phosphor="DownloadIcon"
|
|
remixicon="RiDownloadLine"
|
|
/>
|
|
</AttachmentAction>
|
|
</AttachmentActions>
|
|
</Attachment>
|
|
<Attachment className="w-64">
|
|
<AttachmentMedia>
|
|
<IconPlaceholder
|
|
lucide="PresentationIcon"
|
|
tabler="IconPresentation"
|
|
hugeicons="Presentation01Icon"
|
|
phosphor="PresentationIcon"
|
|
remixicon="RiSlideshowLine"
|
|
/>
|
|
</AttachmentMedia>
|
|
<AttachmentContent>
|
|
<AttachmentTitle>brand-deck.key</AttachmentTitle>
|
|
<AttachmentDescription>Keynote · 9 MB</AttachmentDescription>
|
|
</AttachmentContent>
|
|
<AttachmentActions>
|
|
<AttachmentAction
|
|
type="button"
|
|
title="Download"
|
|
aria-label="Download brand-deck.key"
|
|
size="icon-sm"
|
|
variant="secondary"
|
|
>
|
|
<IconPlaceholder
|
|
lucide="DownloadIcon"
|
|
tabler="IconDownload"
|
|
hugeicons="Download01Icon"
|
|
phosphor="DownloadIcon"
|
|
remixicon="RiDownloadLine"
|
|
/>
|
|
</AttachmentAction>
|
|
</AttachmentActions>
|
|
</Attachment>
|
|
<Attachment className="w-64">
|
|
<AttachmentMedia>
|
|
<IconPlaceholder
|
|
lucide="FileArchiveIcon"
|
|
tabler="IconFileZip"
|
|
hugeicons="File01Icon"
|
|
phosphor="FileZipIcon"
|
|
remixicon="RiFileZipLine"
|
|
/>
|
|
</AttachmentMedia>
|
|
<AttachmentContent>
|
|
<AttachmentTitle>brand-assets.zip</AttachmentTitle>
|
|
<AttachmentDescription>ZIP · 4.2 MB</AttachmentDescription>
|
|
</AttachmentContent>
|
|
<AttachmentActions>
|
|
<AttachmentAction
|
|
type="button"
|
|
title="Download"
|
|
aria-label="Download brand-assets.zip"
|
|
size="icon-sm"
|
|
variant="secondary"
|
|
>
|
|
<IconPlaceholder
|
|
lucide="DownloadIcon"
|
|
tabler="IconDownload"
|
|
hugeicons="Download01Icon"
|
|
phosphor="DownloadIcon"
|
|
remixicon="RiDownloadLine"
|
|
/>
|
|
</AttachmentAction>
|
|
</AttachmentActions>
|
|
</Attachment>
|
|
</AttachmentGroup>
|
|
</MessageContent>
|
|
</Message>
|
|
</div>
|
|
</Example>
|
|
)
|
|
}
|
|
|
|
function MessageActions() {
|
|
return (
|
|
<Example title="Actions">
|
|
<div className="flex w-full max-w-md flex-col gap-10">
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Bubble>
|
|
<BubbleContent>
|
|
What is the status of the deployment?
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<MessageFooter>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
aria-label="Copy"
|
|
title="Copy"
|
|
>
|
|
<IconPlaceholder
|
|
lucide="CopyIcon"
|
|
tabler="IconCopy"
|
|
hugeicons="Copy01Icon"
|
|
phosphor="CopyIcon"
|
|
remixicon="RiFileCopyLine"
|
|
/>
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
aria-label="Retry"
|
|
title="Retry"
|
|
>
|
|
<IconPlaceholder
|
|
lucide="RefreshCcwIcon"
|
|
tabler="IconRefresh"
|
|
hugeicons="ReloadIcon"
|
|
phosphor="ArrowClockwiseIcon"
|
|
remixicon="RiRefreshLine"
|
|
/>
|
|
</Button>
|
|
</MessageFooter>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message>
|
|
<MessageAvatar>
|
|
<Avatar>
|
|
<AvatarImage
|
|
src="https://github.com/evilrabbit.png"
|
|
alt="@evilrabbit"
|
|
/>
|
|
<AvatarFallback>ER</AvatarFallback>
|
|
</Avatar>
|
|
</MessageAvatar>
|
|
<MessageContent>
|
|
<Bubble variant="muted">
|
|
<BubbleContent>
|
|
The install failure is coming from the workspace package. I can
|
|
open the exact log line or retry the deployment.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<MessageFooter>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
aria-label="Copy"
|
|
title="Copy"
|
|
>
|
|
<IconPlaceholder
|
|
lucide="CopyIcon"
|
|
tabler="IconCopy"
|
|
hugeicons="Copy01Icon"
|
|
phosphor="CopyIcon"
|
|
remixicon="RiFileCopyLine"
|
|
/>
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
aria-label="Like: thumbs up"
|
|
title="Like: thumbs up"
|
|
>
|
|
<IconPlaceholder
|
|
lucide="ThumbsUpIcon"
|
|
tabler="IconThumbUp"
|
|
hugeicons="ThumbsUpIcon"
|
|
phosphor="ThumbsUpIcon"
|
|
remixicon="RiThumbUpLine"
|
|
/>
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
aria-label="Like: thumbs down"
|
|
title="Like: thumbs down"
|
|
>
|
|
<IconPlaceholder
|
|
lucide="ThumbsDownIcon"
|
|
tabler="IconThumbDown"
|
|
hugeicons="ThumbsDownIcon"
|
|
phosphor="ThumbsDownIcon"
|
|
remixicon="RiThumbDownLine"
|
|
/>
|
|
</Button>
|
|
</MessageFooter>
|
|
</MessageContent>
|
|
</Message>
|
|
<Message align="end">
|
|
<MessageContent>
|
|
<Bubble>
|
|
<BubbleContent>
|
|
Okay drop me a link. I'll check it out.
|
|
</BubbleContent>
|
|
</Bubble>
|
|
<MessageFooter className="gap-2">
|
|
<span className="font-normal text-destructive">
|
|
Failed to send
|
|
</span>
|
|
<Button variant="secondary" size="xs">
|
|
Retry
|
|
</Button>
|
|
</MessageFooter>
|
|
</MessageContent>
|
|
</Message>
|
|
</div>
|
|
</Example>
|
|
)
|
|
}
|