Skip to content
kitn AI/UI

Prompt Input

kai-prompt-input

Handles the input, the attachments, and the send, so a host only listens for the submitted text.

  • Shadow DOM
  • Uncontrolled by default
  • Starter suggestions
  • Entity pills (/ skills, @ agents)
  • Custom toolbar buttons
  • Pre-seeded attachments

Register web components once, then wire up JS properties and DOM events:

<kai-prompt-input id="input" style="display:block; width:100%;"></kai-prompt-input>
<script type="module">
import '@kitn.ai/ui/web-components';
await customElements.whenDefined('kai-prompt-input');
const input = document.getElementById('input');
input.suggestions = ['Summarize this thread', 'Draft a reply'];
// `/` inserts skill pills, `@` opens an agent/plugin menu.
input.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' }] },
];
input.addEventListener('kai-submit', (e) => console.log('send:', e.detail.value, e.detail.entities, e.detail.attachments));
input.addEventListener('kai-value-change', (e) => console.log('typing:', e.detail.value));
</script>
  • Array props: suggestions, triggers, kindIcons, attachments: set them in JavaScript, not as attributes.
  • Scalars: placeholder, disabled, loading, webSearch (web-search), voice, stoppable, suggestionMode: attributes or properties.
  • Loading + stoppable: loading blocks submit while a response streams; stoppable swaps send for a Stop button that fires kai-stop.
  • Custom toolbar buttons: <kai-action id icon tooltip> children inside the element fire kai-toolbar-action with detail.action.
  • Entity pills: triggers makes / and @ insert atomic skill/agent/plugin pills, and kai-submit / kai-value-change carry the structured doc + entities.
  • Pre-populate pills: set value to an array of segments ({ type: 'text', text }, { type: 'entity', entity }) instead of a string to seed pills programmatically. The prop tables call that shape a ComposerDoc; it is a structural type, not something you import.
Child elementAttributesText contentNotes
actioniconidlabeltooltip
Yes—

Type and press Enter, or click a chip, to fire kai-submit.

webSearch and voice add Globe and Mic buttons, firing kai-web-search and kai-voice. Renamed from search / kai-search, with no alias.

A ComposerDoc value seeds pills on mount and stays editable; submit still emits the flattened value plus doc + entities.

attachments pre-populates staged files; the paperclip adds more, each chip has a remove button.

Project controls into the shadow: a status strip above the textarea, or leading/trailing toolbar clusters for a + menu or a model switcher.

SlotModePurpose
injectInside the card, above the textarea (e.g. an inline status strip). For content above/below the whole card, use your own layout; that is light DOM you control.
injectLeading controls in the input toolbar, where a + menu goes.
injectTrailing controls in the toolbar, before the Send button.

Restyle the send button via ::part(send), or hide it for an Enter-only composer.

PartPurposeExample
The send button. Restyle from outside, or hide it entirely (Enter-only). Hiding is pure CSS, which is why there is no `submit="never"`.
kai-prompt-input::part(send) { display: none } /* Enter-only; or restyle: background, border-radius, … */
PropertyTypeDefaultNotes
theme'auto'Color mode (`auto` follows prefers-color-scheme).
value—Value of the input: a **string** is the controlled text mirror, a **ComposerDoc** is a one-time pill seed.
placeholder'Send a message...'Placeholder text shown in the empty input.
disabledfalseDisable the input and submit button entirely (non-interactive).
loadingfalseShow the loading/streaming state and block submit (use while awaiting a reply).
suggestions—Starter prompts shown above the input. Clicking one follows `suggestionMode`. Set as a JS property.
suggestionMode'submit'What clicking a suggestion does. Defaults to `'submit'`.
webSearchfalseShow a web-search (Globe) button in the left toolbar; clicking it fires a `kai-web-search` event. Attribute: `web-search`.
voicefalseShow a Voice (Mic) button in the left toolbar; clicking it fires a `voice` event.
stoppablefalseWhen set and `loading` is true, the send button is replaced by a Stop button (square icon, "Stop" aria-label). Clicking it fires `kai-stop`.
submit'always'Send-button visibility. Defaults to `'always'`.
attachtrueShow the built-in paperclip attach button. Default `true`.
attachments—Attachments to seed the input with. Each `url` must be a `data:` URI or https URL, never `blob:`.
triggers—Rich entity triggers. Each `{ char, kind, items }` opens a caret-anchored menu that inserts an atomic pill. JS property.
kindIcons—Default icon per entity kind (kind → image URL/data-URI) for pills/menu items without their own `icon`. Overrides the built-in agent/plugin glyphs. JS property.
EventDetailNotes
The staged attachments changed (file added or removed). Carries the full current list so a consumer can react in real time.
Record<string, never> The Stop button was clicked while `stoppable` and `loading` are both true.
The user submitted the prompt (Enter or send button). `value` is the flattened text.
A suggestion was clicked while `suggestion-mode="fill"`.
A custom `<kai-action>` toolbar button was clicked. `action` is the `id` of the `<kai-action>` element that was clicked.
The input changed (fires on every edit). Carries the flattened `value` plus the structured `doc` + `entities`.
Record<string, never> The Voice (Mic) toolbar button was clicked.
Record<string, never> The web-search (Globe) toolbar button was clicked.
MethodSignatureNotes
(options?: FocusOptions): voidFocus the text editor inside the shadow root (not the hidden file input).
(): voidBlur the focused input control.
(): voidClear the text and any staged attachments (fires kai-value-change / kai-attachments-change so a controlled consumer can react).
(): voidSend the current value programmatically, on the same path as Enter / the send button (fires kai-submit, then clears staged attachments). Named `send`, not `submit`, to avoid colliding with the `submit` prop.