Chat

The chat is the conversation for an AI assistant or a support desk. Answers stream in as they arrive and are written in Markdown, with highlighted code that copies in one click, tables, sources cited by number and the steps the assistant took. Files go with a message; suggestions start one. Stop an answer, ask again, edit and resend, rate it, and save the whole conversation as Markdown, text or JSON.

It holds no model and sends nothing anywhere. You give it a handler that asks your own service — your server, or a model you host — and return the reply as text, a stream or a fetch response; the chat does the rest. Answers are sanitised before they are shown, and cannot load anything unless you allow images.

A support assistant

"Nova" answers from this page with a stand-in handler that streams its answers a few characters at a time, as a model would. Ask about refunds for sources and a tool step, about the API for code, or about plans for a table. Press the send button while an answer streams to stop it.

Save as Markdown Start again
Read by everyone: the conversation is a live log that screen readers announce once an answer is finished, not chunk by chunk. Up and Down move between messages in the log, Up in an empty box edits your last message, and Escape stops an answer.
Get the code
<script src="/assets/bmx-components.min.js"></script>

<bmx-chat
  id="ct-assistant"
  lang="en-GB"
  label="Support assistant"
  heading="How can I help?"
  description="Ask about orders, refunds, the API or our plans."
  assistant-name="Nova"
  suggestions="How do refunds work?,Show me the API in TypeScript,Compare the plans,Where is my order?"
  attachments
  accept="image/*,.pdf,.txt"
  style="inline-size: 100%; --bmx-chat-height: 34rem"
></bmx-chat>

A support desk, with no handler

Without a handler the chat is driven by your page: it hears bmxChatSend and writes replies with startReply, appendToMessage and finishReply. Here a person on the desk answers each message after a moment; the buttons write as the desk.

Answer as the desk It left this morning Sent a new slot Show the items
Get the code
<script src="/assets/bmx-components.min.js"></script>

<bmx-chat id="ct-desk" lang="en-GB" label="Order help" user-name="Ravi" style="--bmx-chat-height: 26rem"></bmx-chat>

Your own service

The handler is a function. Return text, a promise of text, an async generator, a ReadableStream or a fetch response, and the chat streams it. Stop aborts the signal it is given.

const chat = document.querySelector('bmx-chat');

// A server that streams plain text.
chat.handler = ({ messages, signal }) =>
  fetch('/api/chat', { method: 'POST', body: JSON.stringify({ messages }), signal });

// Or one that streams server-sent events: read them and yield the text.
chat.handler = async function* ({ messages, signal, update }) {
  const res = await fetch('/api/chat/stream', { method: 'POST', body: JSON.stringify({ messages }), signal });
  const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
  for (let buffer = ''; ; ) {
    const { value, done } = await reader.read();
    if (done) return;
    buffer += value;
    const events = buffer.split('\n\n');
    buffer = events.pop();
    for (const event of events) {
      const data = JSON.parse(event.replace(/^data: /, ''));
      if (data.sources) update({ sources: data.sources });
      if (data.text) yield data.text;
    }
  }
};