npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@embeddable.com/ai-chat

v0.1.6

Published

A framework-agnostic Web Component that renders a streaming AI chat widget and connects to an Embeddable orchestrator backend over a STOMP WebSocket.

Downloads

552

Readme

@embeddable.com/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

1. Configure your LLM

Before the ai-chat can respond, your workspace needs an LLM configured on the orchestrator backend. Send a POST to /api/v1/llm-config, authenticated with your Embeddable API key (Authorization: Bearer <api-key>). This is a one-time, per-workspace setup — the config is stored encrypted and reused for every chat session.

curl -X POST https://api.[us|eu].embeddable.com/api/v1/llm-config \
  -H "Authorization: Bearer YOUR_EMBEDDABLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "anthropic",
    "model": "claude-sonnet-4-5",
    "apiKey": "sk-ant-...",
    "temperature": 0.7,
    "maxTokens": 2048,
    "systemPrompt": "You are a helpful analytics assistant."
  }'

LLM config fields

| Field | Type | Required | Description | | -------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | provider | string | Yes | LLM provider ID. Must be one supported by the backend: anthropic, openai, google, openrouter, mistralai, bedrock, or ollama. An unsupported value returns 400. | | model | string | Yes | Model ID for the chosen provider (e.g. claude-sonnet-4-5, claude-haiku-4-5). Must be a model the provider supports; an unsupported value returns 400 with the accepted set. | | apiKey | string | Yes | Provider API key used to authenticate LLM calls. Stored encrypted and never returned in full — GET/POST responses mask it. | | temperature | number | No | Sampling temperature. Higher values produce more varied output. Provider default is used when omitted. | | maxTokens | integer | No | Maximum number of tokens to generate per response. Provider default is used when omitted. | | systemPrompt | string | No | System prompt prepended to every conversation to steer tone and behavior. |

Retrieve the current config (with the API key masked) with GET /api/v1/llm-config. If none is set, it returns 404.

2. Mint a security token

Each user needs a security token, minted server-side with your Embeddable API key and passed to the embeddable-tokens prop. See Security tokens for the request and its options (including making the canvas read-only).

3. Load the ai-chat

Add this script to load the component latest version from unpkg (no build step required):

<script type="module" src="https://unpkg.com/@embeddable.com/[email protected]/dist/ai-chat/ai-chat.esm.js"></script>

4. Add the ai-chat to your page

Choose between modal (floating panel) or inline (fills its container):

  1. 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"]'
>
  <button slot="trigger">Open chat</button>
</em-ai-chat>
  1. 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"]'></em-ai-chat>
</div>

See demo.html for a full working example.


Security tokens

Every request the ai-chat makes is authenticated with a security token. Mint one per user with POST /api/v1/security-token (authenticated with your Embeddable API key), then pass it to the embeddable-tokens prop as a JSON-encoded array — the first token is used as the Bearer credential.

curl -X POST https://api.[us|eu].embeddable.com/api/v1/security-token \
  -H "Authorization: Bearer YOUR_EMBEDDABLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "embeddableId": "your-embeddable-id",
    "user": "user-123",
    "securityContext": {},
    "expiryInSeconds": 3600
  }'

Disallowing custom canvas mutation

The AI can mutate the dashboard's custom canvas (for example, adding a chart it just generated). To make the canvas read-only for a token, add "customCanvasReadOnly": true to the request above. When set, the orchestrator denies canvas writes for that token and the ai-chat's tools are marked read-only — the assistant can still answer questions and preview charts, but cannot add to or otherwise modify the canvas. The flag defaults to false (mutations allowed) when omitted. Because it lives on the token, different users can be granted different levels of access with the same embedded widget.


Ai-chat 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. | | client-context | string | — | JSON-encoded dashboard client context (the same object passed to embeddables). Its timezone resolves dates consistently with the dashboard. Forwarded to the orchestrator and to injected components. | | 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 | | chart-preview-supported | "true" \| "false" | "true" | Tells the orchestrator whether this client can render Embeddable chart previews. Sent as the Embeddable-Chart-Preview-Supported header on the STOMP CONNECT frame. | | labels | Partial<ChatLabels> | — | Overrides the widget's UI text (welcome message, input placeholder, feedback dialog copy). This is an object property, not a string attribute — set it in JS via the DOM property (el.labels = {…}), not as an HTML attribute. See Labels below. |

Labels

All user-facing text is customized through the labels property (type ChatLabels). Provide any subset of the keys below; omitted keys fall back to the defaults.

| Key | Default | Description | | ----------------------------- | ------------------------------ | ------------------------------------------------------------------------ | | welcomeMessage | (built-in greeting) | Message shown in the empty state before the first user message | | inputPlaceholder | "Ask anything..." | Placeholder text inside the message input | | feedbackTitlePositive | "Tell us what you liked" | Title of the feedback comment dialog after a thumbs-up rating | | feedbackTitleNegative | "How can we improve?" | Title of the feedback comment dialog after a thumbs-down rating | | feedbackPlaceholderPositive | "Share more" | Placeholder text inside the feedback comment input after a thumbs-up | | feedbackPlaceholderNegative | "Share more" | Placeholder text inside the feedback comment input after a thumbs-down | | feedbackDismiss | "Dismiss" | Label for the dismiss button in the feedback dialog | | feedbackSubmit | "Submit feedback" | Label for the submit button in the feedback dialog | | feedbackThanksForFeedback | "Thanks for your feedback" | Confirmation message shown after a written comment is submitted | | feedbackThanksForRating | "Thanks for your rating" | Confirmation message shown after only a thumbs up/down rating is given |

const chat = document.querySelector('em-ai-chat');
chat.labels = {
  welcomeMessage: 'Hi! Ask me anything about your dashboard.',
  inputPlaceholder: 'Type a question…',
};

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.tokens.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-panel-width: 23.75rem;
  --em-ai-chat-panel-height: 32.5rem;
}

The full list of available variables is in src/em-ai-chat/em-ai-chat.tokens.css.


Framework integration

React / Vue / Angular (ESM import)

import { defineCustomElements } from '@embeddable.com/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.com/ai-chat'],
  },
});

Local development

npm install
npm start        # dev server with hot reload at http://localhost:3333

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