Chat Experience
The UI in dapp/components/chatBot is purpose-built for the agentic features exposed by the backend. It layers mode selection, context forms, tool chips, and a music player on top of the standard chat transcript—while leaning into Rito’s neon, crypto-rap personality.
Rito’s Signature Flow
- Branding & layout —
ChatContainer,ChatHeader, andChatWithPlayer.module.cssgive the interface its Blade Runner-inspired gradients, chunky type, and stacked layout that keeps the music bar anchored under the conversation. - Mode prompts — When a user picks a mode,
composeSystemPromptandcomposeWelcomeMessage(fromdapp/app/lib/llm/modes) inject Blade Runner-grade instructions plus the battle-form background so the very first assistant message sounds like Rito. - Music cues — The
MusicBarpowered byMusicPlayer/MusicProvider.tsxlets verses trigger<music>tags, so each rap session can bring in Rito’s catalog without leaving the chat.
Stores & Modals
- Mode selection —
useChatModeStoretracks the active mode, whether it was locked by props, and the rap-battle form data. When the app loads,ChatBotopensModeSelectModaluntil a mode is chosen. - Battle form —
BattleFormModal.tsxlets users describe themselves and RapBotRito. The inputs are persisted in the store socomposeWelcomeMessagecan stitch the background section into the initial assistant message. - General modal state —
useModalStorecentralizes the mode picker, error modal, confirm-reset dialog, and the battle form. This makes it trivial to gate input:ChatFormsimply checksopen !== 'none'before allowing submissions.
Streaming UI & Inline Tools
1. Render guards
ChatMessages.tsx splits message parts at the anchor stored in useToolActivityStore, so chips appear inline at the exact character offset where the tool was triggered.
2. Tool chips
ToolActivityRow pulls grouped chips for the current assistant message. Each chip delegates to a presenter (dapp/components/chatBot/ToolActivity/catalog) to render pending/success/error labels that match the tool’s personality.
3. Rich content
parseContentWithMedia.ts understands the inline tags the LLM emits:
<key-nft bgColor="#101820" keyColor="#FFCF00" />renders the signature colored key SVG.<chain-logo name="Ethereum" size="32" />drops in a network badge.<gif url="..." />,<img src="store://..." />, and<music song="Rito - Burn the Gate" />map to media renderers and the music controller.<goodbye />injects the refresh animation that resets the gate after a battle.
Behind the scenes, useHydrateToolImages.ts streams base64 payloads from generate_image_with_alt into useLocalImageStore, while pinecone_search feeds meme/rhyme datasets so Rito’s toolkit always has fresh crypto references to drop mid-verse.
Music & Atmosphere
The MusicProvider (dapp/components/chatBot/MusicPlayer/MusicProvider.tsx) exposes a Web Audio API controller so rap prompts can trigger <music> tags. The provider only unlocks audio after the first user interaction (to satisfy browser policies) and streams metadata to the MusicBar.
Hydrating Images
When a tool emits { kind: 'store-image' } JSON, useHydrateToolImages.ts writes the base64 payload into useLocalImageStore. parseContentWithMedia.ts then converts <img src="store://image/foo.png" /> into an inline <img> tag that reads from the hydrated store rather than fetching from the network.
Reset & State Hygiene
- Conversation reset — The trash button in
ChatFormclearsuseChatmessages, wipes the tool activity store, resets the music player, and, if the mode is not locked, brings the user back to the mode picker. - Goodbye handling — When the transcript includes
<goodbye />, the UI stores a session flag (ritoGoodbyeHardReloadAt) so a hard refresh is triggered even if the user navigates away and returns later. This honors the rap battle rule that the page must refresh after the post-battle goodbye.
The interface is opinionated on purpose: every UX detail (modals, chips, inline SVGs, music cues) mirrors a constraint enforced in the chat modes or MCP stack, so developers can reason about behavior from either side. The new MCP tool pages show how each backend action drives the chips referenced here.