Conversation
The thread, the turn, the generative border and the composer — the four pieces of an assistant that can be read as a record rather than a chat.
The thread
Code
<GdAiThread :revision="revision">
<GdAiMessage v-for="t in turns" :key="t.id" :from="t.from" :at="t.at">
{{ t.text }}
</GdAiMessage>
<GdAiMessage v-if="generating" generating>Reading the ruleset…</GdAiMessage>
</GdAiThread>
<GdAiComposer v-model="draft" :generating="generating" @send="ask" />Generative border
Riverton City Plan 2020 · Table 5.5.5.1 · reading zone rules for the selected lot…
Reading the ruleset
Reading the rulesetCode
<GdAiGenerating active label="Reading the ruleset">
<p>The answer, as it arrives…</p>
</GdAiGenerating>Composer
Code
<GdAiComposer v-model="draft" @send="ask" @stop="abort" />API
GdAiThread
Props
revisionnumber0A count that changes when a turn is added or a token streams. The thread follows the tail when it changes — a number rather than the messages, so the caller keeps its own data shape.
labelstring"Conversation"The accessible name of the log.
GdAiMessage
Props
from"person" | "assistant""assistant"Who is speaking. The two do NOT mirror — a person gets a right-aligned bubble, the assistant gets the full column. See Why this way.
namestring"GRIDD AI"Shown beside the mark on an assistant turn. Override it when a named skill answered — "Certifier check" reads better than the product name when that is what ran.
atstring—A timestamp, already formatted. Set in mono and quiet. Format it absolutely: a relative time in a record goes stale the moment the record is read back.
generatingboolean—The model is still writing this turn. Draws the generative border around the body.
Slots
defaultThe turn's content. On an assistant turn this is prose from a model and gets the column's measure, list and paragraph spacing.
sourcesA GdAiSources below the answer. Below, never above: a reader wants the answer first and the evidence when they doubt it.
actionsFeedback controls. Deliberately the caller's — what a thumb-down DOES is product policy, and a design system that shipped the handler would be deciding it.
GdAiGenerating
Props
activeboolean—Is a model producing content right now? This is the ONLY thing this component may mean.
labelstring"Generating"What is being produced, in three or four words. Announced to assistive tech and shown in the reduced-motion form. A generic "Loading" wastes the one chance to say what the wait is for.
quietboolean—Hide the visible label. The accessible one still fires — it is the only signal a screen reader gets.
GdAiComposer
Props
modelValuestring—The question being typed, as v-model. Cleared on send.
placeholderstring"Ask about this project…"Ask a real question. "Type a message…" teaches nothing about what this assistant can answer.
generatingboolean—A model is producing an answer — Ask becomes Stop. One control in one place, whose label says what pressing it does now.
disabledboolean—No session, no project loaded, over quota.
labelstring"Ask the assistant"The field's accessible name. A placeholder is not one — it vanishes the moment the user types.
Events
sendstringThe trimmed question. Enter sends; Shift+Enter breaks the line; an IME's composing Enter does neither.
stopvoidAbandon the generation in flight. A first-class control: a generation costs money and seconds, and a person who can see it going wrong in the first line should not wait out the other twelve.
Behavior & Anatomy
The two turns do not mirror
A person's turn is short and is a record of what they asked; a bubble suits it and the alignment makes the thread scannable. The assistant's turn is the answer, and in this product an answer runs to paragraphs, a table of controls, a citation list. Putting that in a bubble narrows the measure, breaks tables, and makes the reply look like an opinion rather than a document. It is content, so it gets the column.
The thread follows the tail only when you are at the tail
A log that always scrolls to the bottom yanks the page out from under someone who scrolled up to re-read an earlier answer — which, when an answer is a paragraph of planning reasoning, is exactly what people do while the next one streams. So it pins while you are at the bottom and stops the moment you leave. scroll-behavior: smooth is deliberately not used: fired on every streamed token it becomes a permanent slow slide.
Stop is a control, not a nicety
A generation costs money and takes seconds. The send button becomes stop rather than sitting beside it — one control, in one place, whose label says what pressing it does now.