npm package: @gnomondigital/nebulas-kit-react
Description: React components for A2A chat. Requires `@gnomondigital/nebulas-kit-core` and a server proxy.
Latest version: 0.4.2

# @gnomondigital/nebulas-kit-react

React components for A2A chat. Requires `@gnomondigital/nebulas-kit-core` and a server proxy.

## Install

```bash
npm install @gnomondigital/nebulas-kit-react @gnomondigital/nebulas-kit-core
```

## Usage

```tsx
import { A2AChat } from "@gnomondigital/nebulas-kit-react";

export default function ChatPage() {
  return (
    <A2AChat
      a2aEndpoint="/api/a2a"
      configEndpoint="/api/a2a/config"
      user={{ name: "John", picture: "https://..." }}
      labels={{
        placeholder: "Ask anything...",
        newChat: "New chat",
        howCanIHelp: "How can I help?",
        askAbout: "Ask me anything.",
      }}
      suggestedPrompts={[
        "Find top 5 Python developers",
        "Summarize the pipeline",
      ]}
    />
  );
}
```

### File attachments

The composer’s attach-file control (paperclip) is **hidden by default**. Set `showAttachFileButton` on `A2AChat` or `A2AChatFloating` to show it and allow PDF, Word, Markdown, and plain-text uploads (same accepted types as the underlying input).

```tsx
<A2AChat
  a2aEndpoint="/api/a2a"
  configEndpoint="/api/a2a/config"
  showAttachFileButton
/>
```

### Auth0 session (cookies)

Same-origin `fetch` sends session cookies to your `/api/a2a` route. Optionally pass `user` for avatars. No `getAuthHeaders` required when the proxy resolves the user on the server.

### Env ROP (server-side service user)

When your API uses `createA2AProxyHandlerFromEnvROP`, the browser does not send a token. Use `A2AChat` or `A2AChatFloating` with only `a2aEndpoint` and `configEndpoint`.

### Resource Owner Password (browser token)

Use `getAuthHeaders` so config and chat requests include `Authorization: Bearer …`. Pass `authDependency` (e.g. the token string) so config reloads after login without relying on a stable function reference.

```tsx
import { A2AChat, useA2AAuth } from "@gnomondigital/nebulas-kit-react";

export default function ChatPage() {
  const { token, login, logout } = useA2AAuth({ authEndpoint: "/api/a2a/auth" });

  if (!token) {
    return (
      <form
        onSubmit={(e) => {
          e.preventDefault();
          const form = e.target as HTMLFormElement;
          login(
            (form.elements.namedItem("username") as HTMLInputElement).value,
            (form.elements.namedItem("password") as HTMLInputElement).value
          );
        }}
      >
        <input name="username" />
        <input name="password" type="password" />
        <button type="submit">Login</button>
      </form>
    );
  }

  return (
    <>
      <button type="button" onClick={logout}>
        Log out
      </button>
      <A2AChat
        a2aEndpoint="/api/a2a"
        configEndpoint="/api/a2a/config"
        getAuthHeaders={() => ({ Authorization: `Bearer ${token}` })}
        authDependency={token}
      />
    </>
  );
}
```

### Floating widget

Add once in a root layout: fixed chat button (bottom-right), panel with the same chat UI.

**Auth0 session** — optional `user` from your auth library; endpoints use your session proxy.

```tsx
import { A2AChatFloating } from "@gnomondigital/nebulas-kit-react";

export function RootChat() {
  return (
    <A2AChatFloating
      a2aEndpoint="/api/a2a"
      configEndpoint="/api/a2a/config"
      nebulasBaseUrl="/api/nebulas"
      user={{ name: "Jane", picture: "https://..." }}
      panelTitle="Support"
      suggestedPrompts={["What can you help with?"]}
    />
  );
}
```

**Env ROP** — no extra client props beyond endpoints (and optional `suggestedPrompts` / `labels`).

```tsx
<A2AChatFloating a2aEndpoint="/api/a2a" configEndpoint="/api/a2a/config" />
```

**Browser ROP** — built-in sign-in form in the panel; wire `handleA2AAuthNextJsPOST` (or your token endpoint) at `authEndpoint`.

```tsx
<A2AChatFloating
  authEndpoint="/api/a2a/auth"
  a2aEndpoint="/api/a2a"
  configEndpoint="/api/a2a/config"
/>
```

`A2AChatFloating` renders into `document.body` by default (`usePortal`) so parent stacking contexts do not clip it. Set `usePortal={false}` to keep it in the React tree.

## Requirements

- Tailwind CSS (configure in your app)
- Server proxy (Next.js or Express) — see `@gnomondigital/nebulas-kit-core`