Ir al contenido
Evaluar mi proyecto
Componentes · IADisponible

Mensaje de conversación

Mensajes de persona, asistente, sistema y herramienta con jerarquía clara y sin cajas innecesarias.

María10:42
¿Qué pedidos se retrasaron esta semana?
Asistente10:42
Tres pedidos llevan retraso: #4821, #4833 y #4840. El más antiguo lleva seis días esperando material de bodega.
1.2 s
consultar_pedidos10:42
{ "filas": 3, "latencia_ms": 120 }

Código completo

Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.

ai-message-bubble.tsx89 líneas
Descargar
/**
 * 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.

terminal.sh1 líneas
npx shadcn@latest add https://www.gassierconsulting.com/r/ai-message-bubble

Ejemplo 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.

ejemplo.tsx5 líneas
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.

PropiedadTipoDescripción
role?"user" | "assistant" | "system" | "tool"Opcional.
name?stringOpcional.
time?stringOpcional.
avatar?ReactNodeMarca opcional junto al nombre, por ejemplo una inicial o un logotipo.
body?ReactNodeOpcional.
footer?stringOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

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.

VariableValor 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.
Licencia
MIT
Dependencias
React >=18

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-*