AI & chat
AI Response
Production-ready streaming AI response with sources and actions
detent-ai-response
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 3
- Wave
- phase-3-data
- Group
- AI & chat
36.9 KB gzip for the class module
Variants
A finished answer with its code block, sources and actions, and the same component mid-stream (caret, live region,
Streaming status). The answer uses the documented Markdown allow-list; incomplete links stay inert while
streaming, and every source URL passes safeHref, so the second source in the answer above renders inert
with a Link blocked for your safety notice. Text, sources and tool rows are a local simulation; nothing here
calls a model or a network service.
Recorded states
focus-visiblerecorded state; no setterloadingrecorded state; no settererrorrecorded state; no setterstreamingrecorded state; no setterreduced motionrecorded state; no setteridlerecorded state; no settergeneratingrecorded state; no settercompleterecorded state; no settercancelledrecorded state; no setter
Usage
Import
import '/components/ai-response/define.js';
// or register it yourself
import { DetentAIResponse } from '/components/ai-response/ai-response.js';
Attributes
accent-colorcodecontentdefault-expandedfilenamelanguagemodel-badgemodel-namestatustimestamptitletool-count
Properties
activedonFeedbackonRegenerateonToggleDetailssourcestools
Slots
defaulthreficon
Events
detent-regenerate— {}detent-toggle-details— {}detent-feedback— {}
Example
Runnable example: site/examples/ai-response.ts. The snippet below is generated from the catalogued attributes.
<detent-ai-response
accent-color
code
content=""
default-expanded
filename
language
model-badge
model-name="EasyAI 2.0"
status="complete"
timestamp
title
tool-count
></detent-ai-response>
Property-only values
const element = document.querySelector('detent-ai-response');
if (element !== null) {
// sources: AIResponseSource[]
element.sources = [];
// onRegenerate: () => void
element.onRegenerate = () => {};
// onFeedback: like
element.onFeedback = undefined;
}
Source
The pinned upstream source for this row:
src/components/ui/AIResponse.tsxsha256 78a10e1b65eb…
Native files
The native module and its artifacts:
src/components/ai-response/ai-response.tssrc/components/ai-response/styles.csssrc/components/ai-response/define.tsdocs/components/ai-response.mdsite/examples/ai-response.tstests/browser/ai-response.spec.tsevidence/components/ai-response.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
accent-color | — | — | — | Recorded in the catalog as an attribute. |
code | — | — | — | Recorded in the catalog as an attribute. |
content | content | string | '' | Kebab-case attribute; camelCase property. |
default-expanded | — | — | — | Recorded in the catalog as an attribute. |
filename | — | — | — | Recorded in the catalog as an attribute. |
language | — | — | — | Recorded in the catalog as an attribute. |
model-badge | — | — | — | Recorded in the catalog as an attribute. |
model-name | modelName | string | 'EasyAI 2.0' | Kebab-case attribute; camelCase property. |
status | status | 'idle' | 'generating' | 'streaming' | 'complete' | 'error' | 'cancelled' | 'complete' | Kebab-case attribute; camelCase property. |
timestamp | — | — | — | Recorded in the catalog as an attribute. |
title | — | — | — | Recorded in the catalog as an attribute. |
tool-count | — | — | — | Recorded in the catalog as an attribute. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
active | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
d | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
onFeedback | like | undefined | Property-only: assign from script; not accepted as an attribute. |
onRegenerate | () => void | undefined | Property-only: assign from script; not accepted as an attribute. |
onToggleDetails | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
sources | AIResponseSource[] | [] | Property-only: assign from script; not accepted as an attribute. |
tools | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-regenerate | onRegenerate | () => void | Composed, bubbles; fires only for a user action. |
detent-toggle-details | onToggleDetails | () => void | Composed, bubbles; fires only for a user action. |
detent-feedback | onFeedback | (type: "like" | "dislike") => void | Composed, bubbles; fires only for a user action. |
Slots
| Slot | Notes |
|---|---|
default | Consumer content; rendered through a slot, never re-created. |
href | Consumer content; rendered through a slot, never re-created. |
icon | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- content uses Marked 18.1.2 plus DOMPurify 3.4.17 with RETURN_DOM_FRAGMENT and the documented Markdown allow-list; raw HTML stays literal, refused URLs are inert, and incomplete link targets stay inert while streaming. The parser grammar and sanitization policy are recorded in docs/components/ai-response.md.
- CodeBlock, AIResponseHeader, AIResponseContent, AIResponseSources, AIResponseToolsSummary and AIResponseActions ship as documented parts of the one tag (PC-2.1); safeHref ships as a module function and useAIResponseContext() becomes the element's own properties (a reader with no React context).
- content / filename / language are the CodeBlock projection of the row: they render the code block when code is set.
- d (an SVG path) and active (orb spin) from sourceOnlyProps are upstream internal render parameters with no native equivalent; the orb is a local SVG/CSS gradient whose spin is bound to status.
- The pinned source has no syntax highlighter: code fences render as code text in a labelled pre/code block with the copy action.
- Framer Motion's AnimatePresence/spring transitions become CSS transitions and animations on the shared duration tokens; no shared spring is used because every animated property here is decorative (caret opacity, skeleton opacity, orb rotation).
- className is replaced by documented parts and custom properties; there is no Tailwind in consumers.
What it does
A streaming-ready assistant message: header with model orb and live status, the answer body, a code block with a copy trigger, expandable cited sources, a tools summary and feedback actions.
Upstream behaviour
- Progressive streaming state with typing indicator and loading skeleton
- Markdown rendering with headings, lists, blockquotes, tables, and code fences
- Code blocks with dedicated one-click copy buttons
- Composable cited sources list with expandable link pills and previews
- Built-in user feedback actions (Copy, Regenerate, Like, Dislike)
- Seamless integration with Agent Activity and tool execution traces
- Fully accessible keyboard navigation and ARIA live regions for streaming updates
Differences from upstream
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- content uses Marked 18.1.2 plus DOMPurify 3.4.17 with RETURN_DOM_FRAGMENT and the documented Markdown allow-list; raw HTML stays literal, refused URLs are inert, and incomplete link targets stay inert while streaming. The parser grammar and sanitization policy are recorded in docs/components/ai-response.md.
- CodeBlock, AIResponseHeader, AIResponseContent, AIResponseSources, AIResponseToolsSummary and AIResponseActions ship as documented parts of the one tag (PC-2.1); safeHref ships as a module function and useAIResponseContext() becomes the element's own properties (a reader with no React context).
- content / filename / language are the CodeBlock projection of the row: they render the code block when code is set.
- d (an SVG path) and active (orb spin) from sourceOnlyProps are upstream internal render parameters with no native equivalent; the orb is a local SVG/CSS gradient whose spin is bound to status.
- The pinned source has no syntax highlighter: code fences render as code text in a labelled pre/code block with the copy action.
- Framer Motion's AnimatePresence/spring transitions become CSS transitions and animations on the shared duration tokens; no shared spring is used because every animated property here is decorative (caret opacity, skeleton opacity, orb rotation).
- className is replaced by documented parts and custom properties; there is no Tailwind in consumers.