Own Chat Button

By default the widget brings its round chat button and shows proactive messages as a bubble above it. In an app or PWA you often want neither: the chat should open from a tab in your bottom navigation, and a waiting message should show as a dot on that tab.

Three options make that work:

  • launcher: false leaves out the round button. open(), close(), toggle() and send() keep working.
  • triggers: "events" shows no bubble and fires trigger_shown instead, so you decide how to show it.
  • Fixed attributes on every element the widget adds, for your CSS and your tests.

Vanilla JavaScript

import { createWidget } from "@bitpalm/ai-agents";
 
const widget = createWidget({
  slug: "your-agent-slug",
  launcher: false,
  triggers: "events",
});
 
const chatTab = document.querySelector("#chat-tab");
const dot = document.querySelector("#chat-tab .dot");
 
chatTab.addEventListener("click", () => widget.open());
 
widget.on("trigger_shown", ({ message }) => {
  dot.hidden = false;
  chatTab.title = message;
});
widget.on("trigger_hidden", () => {
  dot.hidden = true;
});
 
widget.on("widget_opened", () => chatTab.classList.add("active"));
widget.on("widget_closed", () => chatTab.classList.remove("active"));

When a message is waiting and your button calls open(), the chat opens with that message as the agent's first line. It counts as clicked in the trigger statistics, exactly like a click on the bubble. trigger_hidden fires right after, so the dot goes away.

React

import { useState } from "react";
import { BitPalmAgent } from "@bitpalm/ai-agents/react";
import type { BitPalmAgentInstance } from "@bitpalm/ai-agents";
 
export function ChatTab() {
  const [widget, setWidget] = useState<BitPalmAgentInstance | null>(null);
  const [waiting, setWaiting] = useState(false);
 
  return (
    <>
      <BitPalmAgent
        slug="your-agent-slug"
        launcher={false}
        triggers="events"
        onReady={(instance) => {
          setWidget(instance);
          instance.on("trigger_shown", () => setWaiting(true));
          instance.on("trigger_hidden", () => setWaiting(false));
        }}
      />
      <button type="button" onClick={() => widget?.open()}>
        Chat {waiting ? <span className="dot" /> : null}
      </button>
    </>
  );
}

onReady is called once with the widget, right after it is created.

Script tag

<script
  async
  src="https://unpkg.com/@bitpalm/ai-agents"
  data-slug="your-agent-slug"
  data-launcher="false"
  data-triggers="false"
></script>
 
<button type="button" onclick="window.dispatchEvent(new CustomEvent('bitpalm-open'))">Chat</button>

Without a widget instance there is no on(), so with a script tag data-triggers="false" is the usual choice. For the dot, use the npm package.

Pausing proactive messages

widget.disableTriggers(); // for example while the checkout is open
widget.enableTriggers();  // back to what the triggers option says

While they are off, nothing fires and nothing is counted in the trigger statistics. A message that was waiting goes away and fires trigger_hidden.

The triggers option

ValueWhat happens
true (default)Proactive messages show as a bubble.
"events"No bubble. trigger_shown fires with { message, triggerId, triggerType }.
falseProactive messages are off.

Attributes on the widget's elements

SelectorElement
[data-bitpalm-launcher]The round chat button. Missing with launcher: false.
[data-bitpalm-trigger]The bubble of a proactive message, while it shows.
[data-bitpalm-chat]The chat window, an iframe.

Use them instead of guessing by z-index, for example to hide the button on one page or to find the chat in end-to-end tests.

On phones

On phones the chat opens full screen, over your bottom navigation, and has its own close button. On larger screens it opens as a window in the bottom right corner.