THEME
MOTION
HAPTIC
SOUND

AI PROVIDERS

Cathode UI is provider-agnostic. Four components — TextField, SearchBar, Chat, Button — have opt-in AI behavior. You supply a provider; Cathode routes through it.

The interface

interface CathodeAIProvider {
  /** Inline completion for a TextField. Yields the tail to append. */
  suggest(prefix: string, signal?: AbortSignal): AsyncIterable<string>;

  /** Streaming conversation for Chat. Yields assistant text chunks. */
  chat(messages: ChatMessage[], signal?: AbortSignal): AsyncIterable<string>;

  /** One-shot intent — Button AI actions, SearchBar semantic ranking. */
  act(intent: string, context?: unknown, signal?: AbortSignal): Promise<string>;
}

Wiring it up

import { CathodeProvider } from '@cathode-ui/react';
import { myProvider } from './my-ai-provider';  // your adapter

<CathodeProvider ai={myProvider}>
  {/* TextField / SearchBar / Chat / Button automatically light up */}
</CathodeProvider>

Example: tiny OpenAI adapter

Illustrative only — Cathode UI doesn't bundle this. Adapters live separately so the core package stays free of SDK weight.

import OpenAI from 'openai';

const client = new OpenAI({ apiKey: import.meta.env.VITE_OPENAI_KEY });

export const openAIProvider: CathodeAIProvider = {
  async *suggest(prefix, signal) {
    const stream = await client.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [
        { role: 'system', content: 'Return 3-8 words completing the user prefix. No quotes.' },
        { role: 'user', content: prefix },
      ],
      stream: true,
    }, { signal });
    for await (const chunk of stream) {
      const delta = chunk.choices[0]?.delta?.content;
      if (delta) yield delta;
    }
  },
  async *chat(messages, signal) {
    const stream = await client.chat.completions.create({
      model: 'gpt-4o-mini', messages, stream: true,
    }, { signal });
    for await (const chunk of stream) {
      const delta = chunk.choices[0]?.delta?.content;
      if (delta) yield delta;
    }
  },
  async act(intent, context, signal) {
    const r = await client.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [
        { role: 'system', content: `Respond to intent: ${intent}` },
        { role: 'user', content: JSON.stringify(context ?? {}) },
      ],
    }, { signal });
    return r.choices[0].message.content ?? '';
  },
};

Same shape works for Anthropic, Gemini, local Ollama, your own backend, or a mock for tests (see the Chat showcase for a working mock).