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: falseleaves out the round button.open(),close(),toggle()andsend()keep working.triggers: "events"shows no bubble and firestrigger_showninstead, 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 saysWhile 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
| Value | What happens |
|---|---|
true (default) | Proactive messages show as a bubble. |
"events" | No bubble. trigger_shown fires with { message, triggerId, triggerType }. |
false | Proactive messages are off. |
Attributes on the widget's elements
| Selector | Element |
|---|---|
[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.