@bnagaraju7/chat-widget
v0.1.0
Published
GarudaVPN support chat widget — embeddable via npm (React) or a CDN <script> bundle.
Readme
@bnagaraju7/chat-widget
The GarudaVPN support chat widget — AI chatbot, voice call + OTP, and browser speech-to-text input — packaged for reuse outside the main Next.js app:
- as an npm package for other React apps
- as a CDN
<script>bundle for any third-party website
Everything talks to your backend over plain REST (apiBaseUrl) — there is
no Supabase or any other direct database dependency in this package.
Published under the
@bnagaraju7npm scope. Forking this for your own org? Changenameinpackage.jsonto your own scope (@your-npm-username/chat-widget) or an unscoped name (garuda-chat-widget) before runningnpm publish.
Scope
This package is intentionally smaller than the chat experience inside the main app. It covers:
- AI chatbot (text chat via
POST {apiBaseUrl}/api/v1/agents/chat) - Voice call + OTP verification (via
{apiBaseUrl}/api/v1/call/*, Twilio Voice SDK) - Browser speech-to-text input (Web Speech API)
- End-of-chat resolution/rating flow (via
POST {apiBaseUrl}/api/v1/ratings/respond)
It does not include session history, human-agent live handoff, or edited-message display — those depended on the main app's direct browser-to-Supabase access, which this package doesn't have. If you need them here, your backend would need to expose REST endpoints for sessions/messages/agent-routing/agent-messages (with polling in place of Supabase Realtime).
Build
cd chat
npm install
npm run buildProduces:
dist/index.mjs,dist/index.cjs,dist/index.d.ts— the npm packagedist/cdn/garuda-chat-widget.iife.global.js— the CDN bundle (React/ReactDOM included)
Usage — CDN
<script
src="https://cdn.example.com/garuda-chat-widget.iife.global.js"
data-api-base-url="https://api.example.com"
data-organisation-id="your-org-id"
data-support-phone="+1 555 0100"
async
></script>The script auto-mounts using the data-* attributes on its own <script>
tag. For full control (custom user, authToken, callbacks, or a dynamic
SPA re-init), call init yourself instead of relying on auto-mount:
<script src="https://cdn.example.com/garuda-chat-widget.iife.global.js"></script>
<script>
window.GarudaChat.init({
apiBaseUrl: "https://api.example.com",
organisationId: "your-org-id",
supportPhone: "+1 555 0100",
});
</script>Usage — npm
npm install @bnagaraju7/chat-widgetimport { GarudaChatWidget } from "@bnagaraju7/chat-widget";
<GarudaChatWidget
config={{
apiBaseUrl: "https://api.example.com",
organisationId: "your-org-id",
supportPhone: "+1 555 0100",
}}
/>;react and react-dom are peer dependencies (>=18), so consumer apps
don't end up with a duplicate React copy. The package ships a "use client"
banner, so it works as-is in a React Server Components tree too (see Next.js
below) — no extra client wrapper needed.
Usage — Next.js
Works with both the App Router and the Pages Router.
App Router — the component can be imported directly into a Server
Component; it renders itself on the client because the bundle already
carries "use client":
// app/layout.tsx
import { GarudaChatWidget } from "@bnagaraju7/chat-widget";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<GarudaChatWidget
config={{
apiBaseUrl: process.env.NEXT_PUBLIC_CHAT_API_BASE_URL!,
organisationId: process.env.NEXT_PUBLIC_CHAT_ORG_ID,
supportPhone: "+1 555 0100",
}}
/>
</body>
</html>
);
}Pages Router — mount it once in _app.tsx:
// pages/_app.tsx
import type { AppProps } from "next/app";
import { GarudaChatWidget } from "@bnagaraju7/chat-widget";
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<GarudaChatWidget
config={{ apiBaseUrl: process.env.NEXT_PUBLIC_CHAT_API_BASE_URL! }}
/>
</>
);
}Use NEXT_PUBLIC_* env vars for any config value needed in the browser —
plain (non-NEXT_PUBLIC_) env vars aren't available client-side.
Usage — Odoo 19
Odoo isn't React-based, so integrate via the CDN <script> bundle
(self-contained, includes React) rather than the npm package.
Host dist/cdn/garuda-chat-widget.iife.global.js somewhere Odoo can reach it
(your own static hosting, or as a public asset in a custom module) and add
the <script> tag using one of:
Website Builder, no custom module — Website ‣ Configuration ‣ Settings ‣ SEO section has a "Custom Code" / head-and-body-script field (or Website ‣ Site ‣ Settings ‣ Tracking Codes on some builds) — paste the snippet into the "Body" custom code box so it loads on every page:
<script src="https://your-static-host.example.com/garuda-chat-widget.iife.global.js" data-api-base-url="https://api.example.com" data-organisation-id="your-org-id" data-support-phone="+1 555 0100" async ></script>Custom module — bundle the JS file under
your_module/static/src/js/and add it to the website's assets bundle in__manifest__.py:"assets": { "web.assets_frontend": [ "your_module/static/src/js/garuda-chat-widget.iife.global.js", ], },then set the config once from a small QWeb-injected script (e.g. in a
website.layoutinherited template) instead ofdata-*attributes:<xpath expr="//body" position="inside"> <script t-if="request.website"> window.GarudaChat && window.GarudaChat.init({ apiBaseUrl: "https://api.example.com", organisationId: "your-org-id", }); </script> </xpath>
Either way, make sure the backend behind apiBaseUrl has CORS enabled for
your Odoo site's domain (see "Backend prerequisite" below).
Config reference
| Field | Required | Description |
| ---------------------------- | -------- | ----------------------------------------------------------------------------- |
| apiBaseUrl | yes | Base URL of the support/chat REST API. |
| organisationId | no | Sent as the organisation_id header on every REST request. |
| supportPhone | no | Phone number for the voice-call fallback. Omit to hide the call button. |
| user | no | { id, fullname?, email? } — used for the welcome message and rating submission. |
| authToken | no | Bearer token for REST requests. Omit for guest (unauthenticated) mode. |
| onNavigateToKnowledgeBase | no | Called when a user clicks a knowledge-base article result. |
| onUnauthorized | no | Called on a 401 from an authenticated request. |
Backend prerequisite
The backend behind apiBaseUrl (chat, voice token, OTP, ratings endpoints)
needs CORS enabled for cross-origin requests from whatever domains will
embed the widget.
Example
example/cdn-demo.html loads the built IIFE bundle with placeholder config
for manual smoke-testing — see that file for how to serve it locally.
