Mensaje de conversación
Mensajes de persona, asistente, sistema y herramienta con jerarquía clara y sin cajas innecesarias.
{ "filas": 3, "latencia_ms": 120 }Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
/**
* Mensaje de una conversación con un asistente, en cuatro roles. La persona escribe en un
* bloque gris alineado a la derecha; el asistente responde en texto corrido, sin caja; el sistema es una línea centrada; la herramienta es un bloque de
* salida en monoespaciada. Transparente: toma el fondo de la página. Los colores se
* sobrescriben con --gcai-ink, --gcai-well, --gcai-line, --gcai-muted y --gcai-accent.
*
* <AiMessageBubble
* role="user"
* name="María"
* time="10:42"
* body="¿Qué pedidos se retrasaron esta semana?"
* />
*/
import type { CSSProperties, ReactNode } from "react";
export type AiMessageBubbleProps = {
role?: "user" | "assistant" | "system" | "tool";
name?: string;
time?: string;
/** Marca opcional junto al nombre, por ejemplo una inicial o un logotipo. */
avatar?: ReactNode;
body?: ReactNode;
footer?: string;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcmb-root { --gcmb-ink: var(--gcai-ink, #ededee); --gcmb-muted: var(--gcai-muted, #9b9ca3); --gcmb-line: var(--gcai-line, #26272c); --gcmb-well: var(--gcai-well, #1a1b1e); display: grid; gap: 6px; box-sizing: border-box; width: min(100%, 680px); margin-inline: auto; color: var(--gcmb-ink); font: 14px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
.gcmb-head { display: flex; align-items: center; gap: 8px; color: var(--gcmb-muted); font-size: 12.5px; }
.gcmb-name { color: var(--gcmb-ink); font-weight: 600; }
.gcmb-time { font-variant-numeric: tabular-nums; }
.gcmb-avatar { display: grid; place-items: center; width: 22px; height: 22px; overflow: hidden; border: 1px solid var(--gcmb-line); border-radius: 50%; color: var(--gcmb-ink); font-size: 11px; font-weight: 600; }
.gcmb-msg { overflow-wrap: anywhere; }
.gcmb-msg code { padding: 1px 5px; border-radius: 4px; background: var(--gcmb-well); font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: .86em; }
.gcmb-foot { color: var(--gcmb-muted); font-size: 12.5px; font-variant-numeric: tabular-nums; }
.gcmb-root[data-role="user"] { justify-items: end; }
.gcmb-root[data-role="user"] .gcmb-msg { max-width: min(100%, 520px); padding: 10px 14px; border-radius: 16px 16px 4px 16px; background: var(--gcmb-well); font-size: 15px; line-height: 1.55; }
.gcmb-root[data-role="assistant"] .gcmb-msg { max-width: 62ch; font: 15.5px/1.7 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
.gcmb-root[data-role="system"] { justify-items: center; }
.gcmb-root[data-role="system"] .gcmb-msg { display: flex; align-items: center; gap: 14px; width: 100%; color: var(--gcmb-muted); font-size: 13px; text-align: center; }
.gcmb-root[data-role="system"] .gcmb-msg::before, .gcmb-root[data-role="system"] .gcmb-msg::after { content: ""; flex: 1; height: 1px; background: var(--gcmb-line); }
.gcmb-root[data-role="tool"] .gcmb-msg { overflow-x: auto; padding: 12px 14px; border-radius: 10px; border: 1px solid var(--gcmb-line); background: #050506; color: #d6d7db; font: 13px/1.6 ui-monospace, "SF Mono", Menlo, Consolas, monospace; }
.gcmb-root[data-role="tool"] .gcmb-msg pre { margin: 0; white-space: pre-wrap; }
`;
const ROLE_NAMES: Record<string, string> = {
user: "Tú",
assistant: "Asistente",
system: "Sistema",
tool: "Herramienta",
};
export function AiMessageBubble({
role = "assistant",
name,
time = "10:42",
avatar,
body = "Tres pedidos llevan retraso esta semana: #4821, #4833 y #4840.",
footer,
className,
style,
}: AiMessageBubbleProps) {
const displayName = name ?? ROLE_NAMES[role] ?? "Asistente";
const showHead = role !== "system";
return (
<>
<style>{CSS}</style>
<article
className={["gcmb-root", className].filter(Boolean).join(" ")}
style={style}
data-role={role}
aria-label={`${displayName}, ${time}`}
>
{showHead && (
<div className="gcmb-head">
{avatar && <span className="gcmb-avatar" aria-hidden="true">{avatar}</span>}
<span className="gcmb-name">{displayName}</span>
<span className="gcmb-time">{time}</span>
</div>
)}
<div className="gcmb-msg">{role === "tool" ? <pre>{body}</pre> : body}</div>
{footer && <span className="gcmb-foot">{footer}</span>}
</article>
</>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/ai-message-bubbleEjemplo de uso de Mensaje de conversación
Después de copiar ai-message-bubble.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { AiMessageBubble } from "./ai-message-bubble";
export default function Ejemplo() {
return <AiMessageBubble role="user" time="Ejemplo" />;
}Propiedades de AiMessageBubble
AiMessageBubble acepta 8 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
role? | "user" | "assistant" | "system" | "tool" | Opcional. |
name? | string | Opcional. |
time? | string | Opcional. |
avatar? | ReactNode | Marca opcional junto al nombre, por ejemplo una inicial o un logotipo. |
body? | ReactNode | Opcional. |
footer? | string | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Variantes disponibles
role:user,assistant,system,tool
Personaliza Mensaje de conversación con variables CSS
Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.
| Variable | Valor por defecto |
|---|---|
--gcai-ink | #ededee |
--gcai-muted | #9b9ca3 |
--gcai-line | #26272c |
--gcai-well | #1a1b1e |
Accesibilidad y rendimiento
El archivo tiene 89 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Mensaje de conversación por quien lo usa:
- Declara roles ARIA para lectores de pantalla: user, assistant, system, tool.
Acerca del producto
La persona escribe en un bloque gris a la derecha, el asistente responde en texto corrido, el sistema es una línea centrada y la herramienta es un bloque de salida en monoespaciada. Es transparente: toma el fondo de tu página.
Incluye
Un solo archivo React + TypeScript con estilos dentro. Sin Tailwind ni dependencias externas.
- Cuatro roles con tratamiento propio
- Nombre, hora y pie opcionales
- Marca opcional junto al nombre
- Código en línea y salida de herramienta
- Colores por variables --gcai-*