# @geepers/chat

Full-featured chat UI for multi-provider LLM APIs. Drop a `ChatUI` instance into any container and get streaming chat, conversation history, markdown rendering, and provider switching. No framework required.

Supports 9 providers out of the box: xAI, Anthropic, OpenAI, Cohere, Mistral, Perplexity, HuggingFace, Google Gemini, and Ollama.

Built from the [Slate/Camina](https://dr.eamer.dev/io/slate/) implementation in [IO Suite](https://dr.eamer.dev/io/).

## Install

```bash
npm install @geepers/chat
```

## Usage

```ts
import ChatUI from '@geepers/chat';

const chat = new ChatUI('#app', {
  gateway: 'https://api.dr.eamer.dev/v1',
  apiKey: 'sk_...',
  provider: 'anthropic',
  welcomeHints: [
    { label: 'How do LLMs work?', prompt: 'Explain how LLMs work in plain language.' },
  ],
});
```

That's it. The element at `#app` becomes a full chat interface with sidebar history, streaming responses, and a mobile-responsive layout.

## Headless mode

Use `ChatCore` without any DOM if you want to build your own UI on top:

```ts
import { ChatCore } from '@geepers/chat';

const core = new ChatCore({
  gateway: 'https://api.dr.eamer.dev/v1',
  apiKey: 'sk_...',
  provider: 'xai',
});

core.on('stream-chunk', (e) => {
  process.stdout.write(e.chunk);
});

await core.send('Hello!');
```

## Script tag (no build step)

```html
<div id="app" style="height: 600px;"></div>
<script src="https://unpkg.com/@geepers/chat/dist/index.iife.global.js"></script>
<script>
  new GChat.ChatUI('#app', { apiKey: 'sk_...' });
</script>
```

## Config

```ts
interface ChatConfig {
  gateway?:       string;       // API base URL (default: https://api.dr.eamer.dev/v1)
  apiKey?:        string;       // Falls back to localStorage 'api_key' if not set
  provider?:      Provider;     // Starting provider (default: 'xai')
  model?:         string;       // Override the default model for the provider
  systemPrompt?:  string;       // Prepended to every conversation
  theme?:         'light' | 'dark' | 'auto';  // Default: 'auto'
  welcomeHints?:  WelcomeHint[];  // Hint chips on the welcome screen
  storageKey?:    string;       // localStorage key prefix (namespace multiple instances)

  // Callbacks
  onMessage?:     (msg: Message) => void;
  onError?:       (err: Error) => void;
  onStreamStart?: (messageId: string) => void;
  onStreamEnd?:   (msg: Message) => void;
}
```

## ChatUI methods

| Method | Description |
|--------|-------------|
| `send(text)` | Send a message programmatically |
| `stop()` | Abort the current stream |
| `focus()` | Focus the message input |
| `setProvider(p)` | Switch provider (updates UI) |
| `setSystemPrompt(s)` | Update system prompt (updates UI) |
| `newConversation()` | Clear history and start fresh |
| `destroy()` | Remove the UI from the DOM |
| `chatCore` | Access the underlying `ChatCore` directly |

## Providers

| Key | Model | Label |
|-----|-------|-------|
| `xai` | grok-4 | xAI Grok 4 |
| `anthropic` | claude-sonnet-4-6 | Claude Sonnet 4.6 |
| `openai` | gpt-5.4 | OpenAI GPT-5.4 |
| `cohere` | command-r-plus-08-2024 | Cohere Command R+ |
| `mistral` | mistral-large-latest | Mistral Large |
| `perplexity` | sonar-pro | Perplexity Sonar Pro |
| `huggingface` | Qwen/Qwen3.5-9B | HuggingFace Qwen 3.5 |
| `gemini` | gemini-2.5-flash | Google Gemini 2.5 |
| `ollama` | llama3.2:3b | Ollama (Local) |

## CSS customization

The UI uses CSS custom properties scoped under `.gchat-root`. Override any token:

```css
.gchat-root {
  --gchat-accent: #e11d48;       /* primary color */
  --gchat-sidebar-w: 300px;      /* sidebar width */
  --gchat-font: 'Inter', sans-serif;
}
```

## License

MIT — [Luke Steuber](https://lukesteuber.com)
