The smallest working surface
One tag. It creates its own services element, so a single element on the page is a working chat. Type something and press Enter.
Markup
<machvive-chat-syncopation>
<machvive-chat-syncopation-canvas></machvive-chat-syncopation-canvas>
<machvive-chat-syncopation-prompt slot="footer"></machvive-chat-syncopation-prompt>
</machvive-chat-syncopation>
A support surface
Suggested openings for the empty state, because an empty prompt box is the highest-friction moment in any chat interface. Click a chip β it fills the composer rather than sending, so you stay the author of your own message.
Support
Wait 20 seconds without typing to see the idle nudge. It fires once per conversation β a surface that keeps asking whether you are still there is a surface people close.
Streaming, and interrupting it
Switch the transport above to scripted and send anything. The reply streams a paragraph. While it generates, the button is Stop β not disabled, because a disabled control leaves you watching output you cannot interrupt. Stopping keeps the partial text you already read.
Scroll up mid-stream and the transcript stays where you left it. Autoscroll follows the bottom only while you are already there.
A command line instead of a composer
Same conversation, different input surface. Try /help,
/clear, /transport, and the Up arrow. Slash commands
run locally and never reach the model β /clear is not a prompt.
/order 1234 is registered from page code, which is how you add
domain verbs without forking the component.
Voice, honestly gated
What your browser cannot do is disabled with the reason shown, rather than rendered as a button that silently does nothing. Dictation fills the composer; it never sends.
SpeechRecognition in most browsers sends audio to a vendor
service. It is not local β worth saying out loud if the rest of your surface
is private.
The instruments
The inspector reads the bus and shows topics it was never taught, including
the playground:* events this page emits. History lists what is
stored and gives it back β export as JSON, or delete it for real.
This is the only surface on the page with persist set.
Persistence is off by default β storing someone's conversation is a decision a
page has to make deliberately.
Two surfaces, no shared state
Components find their services element by walking up the DOM, so nesting is the wiring. There is no registry and no global, which is what makes these two conversations genuinely independent. Type in one; the other does not move.
Surface B sets theme="dark". An explicit choice wins in either
direction β it stays dark in a light OS, and the one next to it stays with
your OS preference.
Every role and status
User, assistant, system and tool turns, plus the pending and error states. Error turns are visible on purpose: silently dropping a failed turn leaves someone staring at a prompt that appears to have done nothing.
The last bubble contains <img src=x onerror=alert(1)>.
It renders as those characters β record text goes through
textContent everywhere, inspector included, because model output
is untrusted input.