Refined UI

An opinionated, customizable superset of shadcn/ui

AgentsPrimitivesSkillsSkins
Integration
Guides
  • Overview
  • Get started
  • Shadcn compatibility
  • Architecture
  • Agent surface
Agents
  • Action Bar
  • Audio Recorder
  • Audio VisualizerBetaBeta — the props contract is close to final, but small breaking changes can still land.
  • Chat Input
  • Chat Input Attachment
  • Chat Message
  • Chat Scene
  • Code Block Editor
  • Dynamic NotificationExpExperimental — the contract and the rendering can change without notice. Own the installed copy before shipping it.
  • Environment Variables
  • Inline Attachment
  • Markdown Block
  • Task List
  • Thread Rail
  • Tool Call
  • User Ask
Blocks
  • Chat
  • Theme toggle
  1. Agents
  2. InlineAttachment
Agents

InlineAttachment

Inline file and media previews for chat turns.

Composition

The preferred shape for composing the installed agent from its exported parts.

Parts

Exported compound parts from the installed source.

InlineAttachment
├── InlineAttachmentMedia
├── InlineAttachmentContent
├── InlineAttachmentTitle
├── InlineAttachmentDescription
├── InlineAttachmentActions
└── InlineAttachmentAction

This agent installs from the chat registry. Install the bundle with the command above, or inspect the source below.

Registry command
npx shadcn@latest add http://127.0.0.1:3000/r/refined/chat.json
See registry manifest

Usage

import {
InlineAttachment,
InlineAttachmentContent,
InlineAttachmentMedia,
InlineAttachmentTitle,
} from "@/components/refined-ui/inline-attachment";
​
export function Example() {
return (
<InlineAttachment name="note.jpeg">
<InlineAttachmentMedia />
<InlineAttachmentContent>
<InlineAttachmentTitle />
</InlineAttachmentContent>
</InlineAttachment>
);
}
​

Raw code

Primary installed agent source

Component
src/registry/sources/refined/inline-attachment.tsx
"use client";
​
import type { ComponentProps } from "react";
import { createContext, useContext } from "react";
​
import { cn } from "@/components/refined-ui/lib/cn";
import { skinSlot } from "@/components/refined-ui/skin";
​
type InlineAttachmentContextValue = {
name: string;
};
​
const InlineAttachmentContext = createContext<InlineAttachmentContextValue | null>(null);
​
function useInlineAttachmentContext() {
const context = useContext(InlineAttachmentContext);
if (!context) throw new Error("InlineAttachment compound components must be rendered inside <InlineAttachment>.");
return context;
}
​
export type InlineAttachmentProps = ComponentProps<"button"> & {
name: string;
};
​
export function InlineAttachment({ name, className, children, ...props }: InlineAttachmentProps) {
return (
<InlineAttachmentContext.Provider value={{ name }}>
<button
type="button"
aria-label={`Open attachment: ${name}`}
data-ui="agent"
data-component="inline-attachment"
className={cn(
"group relative aspect-[1.26] w-full max-w-72 cursor-pointer overflow-hidden rounded-scene bg-muted text-left shadow-sm ring-1 ring-border transition hover:shadow-md",
skinSlot("inline-attachment", {}),
className,
)}
{...props}
>
{children}
</button>
</InlineAttachmentContext.Provider>
);
}
​
export type InlineAttachmentMediaProps = ComponentProps<"div"> & {
src?: string;
alt?: string;
};
​
export function InlineAttachmentMedia({ src, alt, className, children, ...props }: InlineAttachmentMediaProps) {
const { name } = useInlineAttachmentContext();
​
return (
<div className={cn("size-full", skinSlot("inline-attachment-image", {}), className)} {...props}>
{src ? (
<img
src={src}
alt={alt ?? name}
loading="lazy"
decoding="async"
className="size-full object-cover transition duration-[var(--duration-slow)] group-hover:scale-[1.02]"
/>
) : (
(children ?? (
<div className="size-full bg-[linear-gradient(135deg,#f8fafc_0%,#f4f4f5_46%,#e7e5e4_100%)] p-5">
<div className="h-full rounded-2xl bg-card/78 p-4 shadow-sm ring-1 ring-foreground/10">
<div className="h-2 w-20 rounded-full bg-foreground/12" />
<div className="mt-5 grid gap-2">
<div className="h-1.5 w-44 rounded-full bg-foreground/16" />
<div className="h-1.5 w-36 rounded-full bg-foreground/12" />
<div className="h-1.5 w-48 rounded-full bg-foreground/14" />
<div className="h-1.5 w-28 rounded-full bg-foreground/10" />
</div>
<div className="mt-5 h-9 w-24 rounded-xl border border-dashed border-foreground/18" />
</div>
</div>
))
)}
</div>
);
}
​
export type InlineAttachmentContentProps = ComponentProps<"div">;
​
export function InlineAttachmentContent({ className, ...props }: InlineAttachmentContentProps) {
return (
<div
className={cn(
"absolute inset-x-3 bottom-3 rounded-[var(--radius-control)] bg-background/85 px-3 py-1 text-meta font-medium text-foreground shadow-sm backdrop-blur",
skinSlot("inline-attachment-content", {}),
className,
)}
{...props}
/>
);
}
​
export type InlineAttachmentTitleProps = ComponentProps<"div">;
​
export function InlineAttachmentTitle({ children, className, ...props }: InlineAttachmentTitleProps) {
const { name } = useInlineAttachmentContext();
​
return (
<div className={cn("truncate", className)} {...props}>
{children ?? name}
</div>
);
}
​
export type InlineAttachmentDescriptionProps = ComponentProps<"div">;
​
export function InlineAttachmentDescription({ className, ...props }: InlineAttachmentDescriptionProps) {
return <div className={cn("truncate text-micro opacity-65", className)} {...props} />;
}
​
export type InlineAttachmentActionsProps = ComponentProps<"div">;
​
export function InlineAttachmentActions({ className, ...props }: InlineAttachmentActionsProps) {
return <div className={cn("mt-1 flex items-center gap-1", className)} {...props} />;
}
​
export type InlineAttachmentActionProps = ComponentProps<"span">;
​
export function InlineAttachmentAction({ className, ...props }: InlineAttachmentActionProps) {
return (
<span
className={cn(
"rounded-[var(--radius-control)] bg-foreground/8 px-1.5 py-0.5 text-micro",
skinSlot("inline-attachment-action", {}),
className,
)}
{...props}
/>
);
}
​

On this page

  • Preview
  • Composition
  • Installation
  • Usage
  • Source