kai-composer
Lets a user reference a skill, an agent, or a plugin inline and hands the backend structured references.
- Shadow DOM
- Atomic entity pills
- / and @ trigger menus
- Keyword highlighting
- Undo / redo
- Structured doc model
- Pre-populate via doc value
Preview
Section titled “Preview”Register web components once, then set the trigger definitions as a JS property and listen for events:
<kai-composer id="composer" style="display:block; width:100%;"></kai-composer>
<script type="module"> import '@kitn.ai/ui/web-components';
await customElements.whenDefined('kai-composer'); const composer = document.getElementById('composer');
// `/` inserts skills; `@` opens a sectioned menu of agents + plugins. composer.triggers = [ { char: '/', kind: 'skill', items: [ { id: 'summarize', label: 'Summarize', description: 'Summarize the thread' }, ]}, { char: '@', kind: 'agent', items: [ { id: 'code-reviewer', label: 'Code Reviewer', group: 'Agents' }, { id: 'documents', label: 'Documents', kind: 'plugin', group: 'Plugins' }, ]}, ];
composer.addEventListener('kai-submit', (e) => { // e.detail = { doc, text, entities } — references, not resolved content. console.log(e.detail.text, e.detail.entities); });</script>- Set objects as properties:
triggers,highlights,kindIcons, and a documentvalueare arrays/objects, so assign them in JavaScript. Scalars (placeholder,disabled,loading,maxHeight,submitOnEnter) work as attributes. - The document is the source of truth. Each pill is an
EntityRef({ kind, id, label, icon?, promptText?, data? }) and the composer emits references, never resolved content; your backend expands/summarizeor@code-reviewerbefore the model call. textis a default flatten: each entity becomes itspromptText ?? label. Pass your own serializer for a different wire format.- Per-kind decoration. Skills and agents render as sigil-led text (
/summarize,@code-reviewer); plugins render as a richer chip, since a plugin bundles tools, skills, and agents. Recolor with--kai-pill-skill/--kai-pill-agent.
Examples
Section titled “Examples”Entity Pills
Section titled “Entity Pills”Pre-Populated Pills
Section titled “Pre-Populated Pills”A ComposerDoc value seeds pills on mount and stays editable; kai-submit still emits the flattened text plus doc and entities.
Keyword Highlighting
Section titled “Keyword Highlighting”highlights decorates matching words via the CSS Custom Highlight API. Decoration only: the text stays plain and editable, and highlights never enter the document model.
| Property | Type | Default | Notes |
|---|---|---|---|
| theme | "light" | "dark" | "auto" | 'auto' | Color mode (`auto` follows prefers-color-scheme). |
| value | — | Controlled value: a string or a full ComposerDoc (set as JS property). | |
| placeholder | string | — | Placeholder text shown when the composer is empty. |
| disabled | boolean | false | Disable the composer entirely (non-interactive). |
| loading | boolean | false | Show a loading/streaming state and block submit. |
| maxHeight | number | string | 240 | Maximum height in px before the content scrolls. Default 240. |
| submitOnEnter | boolean | true | Whether pressing Enter (without Shift) submits. Default true. |
| triggers | — | Trigger definitions. Set as a JS property. | |
| highlights | — | Keyword highlight rules. Set as a JS property. | |
| kindIcons | Record<string, string> | — | Default icon per entity kind (kind → image URL/data-URI) for items without their own `icon`. Overrides the built-in agent/plugin glyphs. JS property. |
Events
Section titled “Events”| Event | Detail | Notes |
|---|---|---|
| kai-blur | | The composer lost focus. |
| kai-entity-add | | An entity pill was inserted into the composer. |
| kai-entity-remove | | An entity pill was deleted from the composer. |
| kai-focus | | The composer gained focus. |
| kai-submit | | The user submitted (Enter or programmatic submit). |
| kai-trigger | | A trigger character was detected at the caret (e.g. `/` or `@`). |
| kai-trigger-close | Record<string, never> | The active trigger was dismissed (Escape, space, or outside click). |
| kai-value-change | | The content changed (fires on every input event). |
Methods
Section titled “Methods”| Method | Signature | Notes |
|---|---|---|
| focus | (options?: FocusOptions): void | Focus the editable element. `focus`/`blur` are NOT composed natively, so a host-level focus() can't reach the editable inside the shadow root. This is the only way to focus the composer programmatically. |
| blur | (): void | Blur the editable element. |
| clear | (): void | Empty the composer to a blank doc (resets the internal value + history baseline; fires kai-value-change). |
| send | (): void | Submit the current content programmatically, on the same path as Enter (fires kai-submit). Named `send`, not `submit`, to match the shared vocabulary and avoid any submit collision. |
| insertEntity | (entity: EntityRef): void | Insert an atomic entity pill (skill/agent/plugin) at the caret without typing a trigger (fires kai-entity-add). |