embeddable-ai-chat
v0.2.5
Published
Stencil Component Starter
Readme
embeddable-ai-chat
A framework-agnostic Web Component that renders a streaming AI chat widget and connects to an Embeddable orchestrator backend over a STOMP WebSocket.
Built with Stencil — works in plain HTML, React, Vue, Angular, or any other framework.
Quick start
Add this script to load the component latest version from unpkg (no build step required):
<script type="module" src="https://unpkg.com/[email protected]/dist/embeddable-ai-chat/embeddable-ai-chat.esm.js"></script>Then add the component to your page. Choose between modal (floating panel) or inline (fills its container):
- Modal mode - the chat opens in a floating panel when the trigger is clicked
<em-ai-chat
mode="modal"
placement="bottom-right"
orchestrator-url="wss://api.[us|eu].embeddable.com/ws"
embeddable-tokens='["your-security-token"]'
input-placeholder="Ask anything..."
>
<button slot="trigger">Open chat</button> >
</em-ai-chat>- Inline mode - the chat fills its container
<div style="width: 420px; height: 600px;">
<em-ai-chat
mode="inline"
orchestrator-url="wss://api.[us|eu].embeddable.com/ws"
embeddable-tokens='["your-security-token"]'
input-placeholder="Ask anything..."
></em-ai-chat>
</div>See demo.html for a full working example.
Props
| Attribute | Type | Default | Description |
| ------------------- | -------------------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| orchestrator-url | string | — | WebSocket URL of the Embeddable orchestrator backend (ws:// or wss://) |
| embeddable-tokens | string | — | JSON-encoded array of security tokens. The first token is used as the Bearer credential. |
| mode | "modal" \| "inline" | "inline" | modal opens a floating draggable panel; inline fills its container |
| placement | "bottom-right" \| "bottom-left" \| "top-right" \| "top-left" | "top-right" | Where the modal panel appears relative to the trigger element |
| welcome-message | string | — | Message shown in the empty state before the user sends their first message |
| input-placeholder | string | — | Placeholder text inside the message input |
| supports-preview | "true" \| "false" | "true" | Tells the orchestrator whether this client can render Embeddable component previews. Sent as the Embeddable-Component-Preview-Supported header on the STOMP CONNECT frame. |
| feedback-comment-title | string | "Tell us more" | Title of the feedback comment dialog |
| feedback-comment-placeholder | string | "Share more (optional)" | Placeholder text inside the feedback comment input |
| feedback-dismiss-label | string | "Dismiss" | Label for the dismiss button in the feedback dialog |
| feedback-submit-label | string | "Submit rating" | Label for the submit button in the feedback dialog |
| feedback-thanks-label | string | "Thanks for your feedback" | Confirmation message shown after feedback is submitted |
Slots
| Name | Description |
| --------- | ----------------------------------------------------------------------- |
| trigger | (modal mode only) The element that toggles the chat panel open/closed |
Theming
The component's visual properties are defined as --em-ai-chat-* CSS custom properties. By default, these resolve to tokens from the remarkable-ui design system — the --em-sem-* (semantic) and --em-core-* (primitive) token layers. You can see the full mapping in src/em-ai-chat/em-ai-chat.css.
If an <em-mbeddable> is already present in your app, the tokens --em-sem-* (semantic) and --em-core-* (primitive) will be automatically injected into the :root, and the style will be applied automatically.
If no <em-mbeddable> is present, or if you need specific styles for the chat, set the --em-ai-chat-* variables directly on :root (or on the element itself) to match your own design:
:root {
/* Shell */
--em-ai-chat-shell-background: #ffffff;
--em-ai-chat-shell-border-color: #e4e4ea;
/* Input composer */
--em-ai-chat-composer-input-border-color: #e4e4ea;
--em-ai-chat-composer-input-border-color--focus: #2d52cc;
/* Primary icon button (send / cancel) */
--em-ai-chat-icon-button-primary-background: #2d52cc;
--em-ai-chat-icon-button-primary-color: #ffffff;
/* Panel size (modal mode) */
--em-ai-chat-width: 23.75rem;
--em-ai-chat-height: 32.5rem;
}The full list of available variables is in src/em-ai-chat/em-ai-chat.css.
Framework integration
React / Vue / Angular (ESM import)
import { defineCustomElements } from 'embeddable-ai-chat/loader';
defineCustomElements();Then use <em-ai-chat> like any other HTML element:
return (
<em-ai-chat embeddable-tokens={JSON.stringify([token])} orchestrator-url="wss://..." mode="modal">
<button slot="trigger">Chat</button>
</em-ai-chat>
);Vite users — add this to vite.config.ts to prevent Vite from pre-bundling the loader (which breaks Stencil's lazy chunk resolution):
export default defineConfig({
optimizeDeps: {
exclude: ['embeddable-ai-chat'],
},
});Local development
npm install
npm start # dev server with hot reload at http://localhost:3333Edit src/index.html to change props or tokens while developing locally. The orchestrator backend is expected at ws://localhost:8080/ws by default.
npm run build # production build → dist/Architecture
em-ai-chat is the root component. It owns the STOMP WebSocket connection, holds all message state, and coordinates the child components below.
em-ai-chat
│ Root. Manages the STOMP WebSocket lifecycle, accumulates streaming
│ events into a display list, and switches between modal and inline modes.
│
├── em-ai-chat-modal-shell (modal mode only)
│ Floating panel with trigger-based open/close, drag-to-reposition,
│ and mouse/keyboard resize. Persists panel size and position across toggles.
│
├── em-ai-chat-messages
│ Scrolling feed of user messages, assistant responses, tool calls, errors,
│ and injected components. Groups tool calls under their parent message
│ and auto-scrolls to the latest entry.
│
│ ├── em-ai-chat-message-feedback
│ │ Thumbs up/down rating attached to each assistant message.
│ │
│ └── em-ai-chat-component
│ Sandboxed host for Embeddable component bundles injected by the AI.
│ Injects theme tokens, scales height via ResizeObserver, and proxies
│ data-fetch requests to the orchestrator.
│
└── em-ai-chat-input
Textarea composer. Enter sends, Shift+Enter adds a newline. Auto-focuses
on visibility and toggles the action button between send and cancel
while a response is streaming.Incoming server events are processed by reduceEvent() in src/utils/messages.utils.ts. It maintains a Map<id, message> for O(1) streaming updates and returns a new array reference on each change to trigger Stencil re-renders.
