@kitn.ai/ui
Version:
Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS.
87 lines (86 loc) • 3.6 kB
TypeScript
import { JSX } from 'solid-js';
import { ComposerDoc, EntityRef } from '../primitives/composer-model';
export interface TriggerItem {
id: string;
label: string;
icon?: string;
/** Secondary muted text shown beside the label in the menu (Codex-style). */
description?: string;
/** Section header to group this item under in the menu (e.g. 'Plugins', 'Agents'). */
group?: string;
/** Entity kind for the inserted pill; overrides the trigger's default `kind` so
* one trigger can mix kinds across sections (e.g. agents + plugins under `@`). */
kind?: string;
promptText?: string;
data?: Record<string, unknown>;
}
export interface TriggerDef {
char: string;
kind: string;
items?: TriggerItem[];
}
export type HighlightRule = string | {
pattern: string;
flags?: string;
class?: string;
};
export interface ComposerChange {
doc: ComposerDoc;
text: string;
entities: EntityRef[];
}
/** Imperative handle exposed via `controllerRef` — surfaces the composer's latent
* capabilities (focus/blur the editable, clear the doc, submit, insert a pill) so
* the `<kai-composer>` facade can forward them as instance methods. */
export interface ComposerController {
focus(options?: FocusOptions): void;
blur(): void;
clear(): void;
send(): void;
insertEntity(entity: EntityRef): void;
}
export interface ComposerProps {
value?: string | ComposerDoc;
placeholder?: string;
disabled?: boolean;
loading?: boolean;
maxHeight?: number | string;
submitOnEnter?: boolean;
triggers?: TriggerDef[];
highlights?: HighlightRule[];
/** Default icon per entity kind (kind → image URL/data-URI), shown on a pill +
* menu item when the item has no `icon` of its own. Overrides the built-in
* glyphs (agent/plugin). Example: `{ agent: '/icons/bot.svg' }`. */
kindIcons?: Record<string, string>;
/** Render WITHOUT the rounded frame/background/padding — just the editable +
* placeholder + menu. For embedding inside another frame (e.g. PromptInput). */
bare?: boolean;
/** Override the editable element's classes (used in `bare` mode to match an
* existing input's exact look). The placeholder mirrors these for alignment. */
editableClass?: string;
/** Accessible name for the editable (role=textbox). Falls back to the
* placeholder, then "Message input". */
ariaLabel?: string;
/** Receive the editable element (e.g. to register it for click-to-focus). */
editableRef?: (el: HTMLDivElement) => void;
/** Receive the imperative controller once mounted. The `<kai-composer>` facade
* forwards these as element methods (focus/blur/clear/send/insertEntity). */
controllerRef?: (controller: ComposerController) => void;
onChange?: (change: ComposerChange) => void;
onSubmit?: (change: ComposerChange) => void;
onTrigger?: (info: {
char: string;
query: string;
rect: DOMRect;
}) => void;
onTriggerClose?: () => void;
onEntityAdd?: (entity: EntityRef) => void;
onEntityRemove?: (entity: EntityRef) => void;
/** The editable gained focus. `focus`/`blur` are NOT composed, so they don't
* escape the shadow root — this re-exposes them. (keydown/paste/focusin/focusout
* are composed and already reach the host as native events; no wrapper needed.) */
onFocus?: (e: FocusEvent) => void;
/** The editable lost focus. */
onBlur?: (e: FocusEvent) => void;
}
export declare function Composer(props: ComposerProps): JSX.Element;