In the previous chapter, Primary Communication Channel (BriefTool), we learned how the AI acts like a "Waiter," using BriefTool to deliver a structured message containing text and attachments.
But a Waiter behaves differently depending on the setting. In a fancy dining room, they might use a silver platter. At a drive-through, they use a paper bag. The food (the data) is the same, but the presentation (the UI) changes completely.
This chapter explains Context-Aware UI Rendering: how BriefTool takes one message and magically changes its appearance to fit the user's current screen mode.
Imagine the AI sends this message:
"I have analyzed the error." (Attachment:
error.log)
If we hard-coded the UI to look like a Chat Bubble, the Transcript would look terrible. If we hard-coded it to look like a Log, the Chat would look boring.
Context-Aware Rendering solves this by separating the Data (what is said) from the View (how it looks).
We want to write a rendering function that takes the AI's output and behaves like a responsive website:
{ message: "Done", sentAt: "10:00 PM" }isChatMode = trueOur system supports three specific "vibes" (contexts). The renderer checks which flag is active and draws the corresponding UI.
isTranscriptMode)isBriefOnly)
When BriefTool finishes, it passes its data to the UI Renderer. The Renderer looks at the user's settings (the options object) to decide which "costume" to put on the message.
Let's look at the implementation in UI.tsx. We use a specific function called renderToolResultMessage.
Note: We use a library called
Ink(React for Command Line Interfaces) to draw the UI.<Box>is like a<div>, and<Text>is like a<span>.
First, we check if there is actually anything to show. If the message is empty and there are no files, we render nothing.
// From UI.tsx
export function renderToolResultMessage(output, options) {
const hasAttachments = (output.attachments?.length ?? 0) > 0;
// If no text AND no files, don't draw anything
if (!output.message && !hasAttachments) {
return null;
}
If the user is viewing the history log (isTranscriptMode), we draw a distinct Black Circle (BLACK_CIRCLE) next to the text.
if (options?.isTranscriptMode) {
return (
<Box flexDirection="row" marginTop={1}>
<Box minWidth={2}>
{/* This is the โบ bullet point */}
<Text color="text">{BLACK_CIRCLE}</Text>
</Box>
{/* The actual content */}
<Box flexDirection="column">
<Markdown>{output.message}</Markdown>
<AttachmentList attachments={output.attachments} />
</Box>
</Box>
);
}
If we are in the interactive chat (isBriefOnly), we add style. We add padding to indent it and print the name "Claude" with the time.
if (options?.isBriefOnly) {
const ts = formatBriefTimestamp(output.sentAt);
return (
<Box flexDirection="column" marginTop={1} paddingLeft={2}>
<Box flexDirection="row">
{/* The visual label */}
<Text color="briefLabelClaude">Claude</Text>
<Text dimColor> {ts}</Text>
</Box>
{/* The message content */}
<Box flexDirection="column">
<Markdown>{output.message}</Markdown>
<AttachmentList attachments={output.attachments} />
</Box>
</Box>
);
}
You noticed <AttachmentList /> in the code above. This is a helper component that loops through the files and draws them with a small icon.
// Simplified from UI.tsx
export function AttachmentList({ attachments }) {
return (
<Box flexDirection="column">
{attachments.map(att => (
<Box key={att.path}>
{/* pointerSmall is a generic arrow icon */}
<Text dimColor>{figures.pointerSmall} [file] </Text>
<Text>{getDisplayPath(att.path)}</Text>
</Box>
))}
</Box>
)
}
Note: This code only handles the drawing of the file list. The complex logic of verifying that these files actually exist and are safe to read is handled in the next chapter: Attachment Resolution Pipeline.
In this chapter, we learned that raw data is not enough. To make a tool feel "native," it must adapt to the user's environment.
We have now covered how the AI speaks (Chapter 1) and how that speech looks (Chapter 2). But what happens when the AI wants to show you a file? It's not as simple as just printing the filename. We need to ensure the file exists, is readable, and isn't too huge.
Next Step: Let's look at how the system safely handles file references.
Next Chapter: Attachment Resolution Pipeline
Generated by Code IQ