npm.io
0.2.0 • Published 22h ago

@cpcyber/insideai

Licence
MIT
Version
0.2.0
Deps
0
Size
23 kB
Vulns
0
Weekly
0

@cpcyber/insideai

Embed InsideAI chat in any web app — a full-page chat, a floating popup, or both.

npm install @cpcyber/insideai

No build step, no bundled CSS, no UI framework. React is an optional peer dependency, used only by the /react entry point.

How sign-in works

The chat itself runs inside an InsideAI iframe, so this package is the plumbing around it: it fetches a one-time sign-in code, mounts the frame, and keeps the two sides talking.

An API key must never reach a browser. Your backend holds the key and exposes one endpoint that proxies InsideAI's POST /api/v1/embed/session/ (one call with the insideai Python package, if you're on Django/Flask/FastAPI). That endpoint returns:

{ "enabled": true, "code": "iaiec_…", "chat_url": "…/embed/chat", "mini_url": "…/embed/mini" }

You hand this package a fetchSession function that resolves to exactly that. Codes are single-use and expire in 60 seconds, so it is called on every mount and again if a session ever lapses.

The launcher has to call it at mount too, to know whether to render its button at all. It keeps that session for the first open rather than minting a second one seconds later, and falls back to a fresh call once the code is too near expiry to trust.

When enabled is false — InsideAI not configured, embedding paused for your org — the components render nothing. Mount the launcher once in a layout and forget about it.

React

import { InsideAIChat, InsideAILauncher } from "@cpcyber/insideai/react";

const loadSession = async () => {
    const response = await fetch("/api/insideai/session/");
    return response.json();
};

// A page of its own
<InsideAIChat fetchSession={loadSession} height="calc(100vh - 64px)" />

// Or a floating button on every page
<InsideAILauncher fetchSession={loadSession} page={href} />
Prop Both Meaning
fetchSession Resolves to the session object above. Required. Passing an inline arrow is fine — it will not remount the frame.
page The host page's URL, so the AI can answer "what am I looking at?". Update it on navigation; the frame is not reloaded. Defaults to the URL at mount.
height InsideAIChat CSS height of the container. Defaults to 100%.
fallback InsideAIChat Rendered instead of the chat when the session comes back disabled. On a page of its own, an empty box with no explanation is worse than a sentence.
label InsideAILauncher Accessible label for the button.

In Next.js App Router, feed page from the router so context follows the user around:

"use client";
const pathname = usePathname();
<InsideAILauncher fetchSession={loadSession} page={`${location.origin}${pathname}`} />

Without React

import { mountChat, mountLauncher } from "@cpcyber/insideai";

const chat = mountChat(document.getElementById("chat"), { fetchSession });
const launcher = mountLauncher(document.body, { fetchSession });

launcher.setPage(location.href);   // on navigation
launcher.destroy();                // on teardown

mountChat resolves .ready, mountLauncher resolves .enabled — both false when embedding is off. The launcher also exposes open() and close().

Theming

Everything is styled through CSS variables on the .insideai-root container, so you can restyle it from your own stylesheet with no imports:

.insideai-root {
    --insideai-accent: #6c2bd9;
    --insideai-on-accent: #fff;
    --insideai-bar: #6c2bd9;
    --insideai-on-bar: #fff;
    --insideai-radius: 4px;
    --insideai-width: 460px;
    --insideai-height: 680px;
    --insideai-offset: 24px;
    --insideai-z: 1500;
}

--insideai-bar and --insideai-on-bar are the popup's header strip, which by default is transparent over the surface and wears whatever text colour it inherits. Set them when the popup should wear your brand rather than blend in.

The popup goes full-screen under 640px and follows prefers-color-scheme for its surface color. If your app has a dark mode of its own that is a runtime toggle rather than an OS preference, set the colour variables from your theme as an inline style on the mount element — that outranks both the default and the media query, which a stylesheet rule cannot reliably do.

postMessage protocol

If you host the iframe yourself, this is the contract the embed speaks.

An iframe cannot know its embedder's origin, so the two directions are protected differently, and a hand-rolled host must do the same. Host → embed messages are sent to the InsideAI origin only — never "*" — because one of them carries a sign-in code. Embed → host messages are posted to "*", since the embed has no origin to target, but they carry no data at all: they are notifications, and the embed accepts inbound messages only from window.parent.

Direction Message Meaning
host → embed {type: "iai:page", url} The host navigated.
host → embed {type: "iai:code", code} A fresh single-use code.
embed → host {type: "iai:ready"} Mounted and signed in.
embed → host {type: "iai:auth-required"} Session lapsed; send a code.

Exported as HOST_TO_EMBED and EMBED_TO_HOST.

iai:ready is the one message these components do not act on: readiness is already known from the session resolving, and waiting for the frame to speak would leave a host stuck forever against an older embed that never sends it. It is part of the contract and exported for hand-rolled hosts that do want to gate a loading state on it.

Keywords