Documentation Index
Fetch the complete documentation index at: https://mintlify.com/badlogic/pi-mono/llms.txt
Use this file to discover all available pages before exploring further.
The @mariozechner/pi-web-ui package provides React components for building web-based AI chat interfaces.
Import
import {
ChatPanel,
AgentInterface,
MessageList,
Input,
ModelSelector,
SettingsDialog,
} from "@mariozechner/pi-web-ui";
Core Components
ChatPanel
Complete chat interface with message list, input, and agent integration.
import { ChatPanel } from "@mariozechner/pi-web-ui";
function App() {
return (
<ChatPanel
agent={agent}
onSendMessage={(text) => agent.prompt(text)}
theme="dark"
/>
);
}
Agent instance from @mariozechner/pi-agent-core
Callback when user sends a message
Theme mode. Default: 'light'
AgentInterface
Complete agent interface with sidebar and settings.
import { AgentInterface } from "@mariozechner/pi-web-ui";
function App() {
return (
<AgentInterface
agent={agent}
onSendMessage={(text, attachments) => {
agent.prompt(text);
}}
showSidebar
enableAttachments
/>
);
}
Callback with message text and optional attachments
Show sidebar with sessions. Default: false
Enable file attachments. Default: false
MessageList
Scrollable message list with custom renderers.
import { MessageList } from "@mariozechner/pi-web-ui";
function Chat() {
return (
<MessageList
messages={agent.state.messages}
streamMessage={agent.state.streamMessage}
/>
);
}
List of messages to display
Currently streaming message
Message input with autocomplete and attachments.
import { Input } from "@mariozechner/pi-web-ui";
function ChatInput() {
return (
<Input
onSubmit={(text, attachments) => {
console.log(`Send: ${text}`);
}}
placeholder="Type a message..."
enableAttachments
/>
);
}
Callback when message is submitted
Enable file attachments. Default: false
Disable input. Default: false
Message Components
UserMessage
Render a user message.
import { UserMessage } from "@mariozechner/pi-web-ui";
function MyMessage() {
return <UserMessage content="Hello!" />;
}
AssistantMessage
Render an assistant message with markdown.
import { AssistantMessage } from "@mariozechner/pi-web-ui";
function MyMessage() {
return (
<AssistantMessage
content="Here's some **markdown** text."
thinking="Internal reasoning..."
/>
);
}
content
string | Content[]
required
Message content
Reasoning/thinking content to display
Render a tool execution result.
import { ToolMessage } from "@mariozechner/pi-web-ui";
function MyMessage() {
return (
<ToolMessage
toolName="search"
content={[{ type: "text", text: "Results..." }]}
details={{ count: 10 }}
/>
);
}
Dialog Components
ModelSelector
Dialog for selecting models.
import { ModelSelector } from "@mariozechner/pi-web-ui";
function App() {
const [open, setOpen] = useState(false);
return (
<ModelSelector
open={open}
onClose={() => setOpen(false)}
currentModel={agent.state.model}
onSelectModel={(model, thinkingLevel) => {
agent.updateState({ model, thinkingLevel });
}}
/>
);
}
Callback when model is selected
SettingsDialog
Dialog for application settings.
import { SettingsDialog } from "@mariozechner/pi-web-ui";
function App() {
const [open, setOpen] = useState(false);
return (
<SettingsDialog
open={open}
onClose={() => setOpen(false)}
tabs={["general", "api-keys", "proxy"]}
/>
);
}
Tabs to show. Default: ['general', 'api-keys']
SessionListDialog
Dialog for browsing and switching sessions.
import { SessionListDialog } from "@mariozechner/pi-web-ui";
function App() {
const [open, setOpen] = useState(false);
return (
<SessionListDialog
open={open}
onClose={() => setOpen(false)}
sessions={sessions}
currentSessionId={currentSessionId}
onSelectSession={(sessionId) => {
// Switch to session
}}
/>
);
}
Utility Components
ThinkingBlock
Collapsible thinking/reasoning display.
import { ThinkingBlock } from "@mariozechner/pi-web-ui";
function Message() {
return (
<ThinkingBlock thinking="Internal reasoning here..." />
);
}
ConsoleBlock
Code execution console output.
import { ConsoleBlock } from "@mariozechner/pi-web-ui";
function ToolResult() {
return (
<ConsoleBlock
logs={[
{ type: "log", message: "Hello" },
{ type: "error", message: "Error!" },
]}
/>
);
}
ExpandableSection
Collapsible section with header.
import { ExpandableSection } from "@mariozechner/pi-web-ui";
function Details() {
return (
<ExpandableSection title="Details" defaultExpanded={false}>
<div>Content here...</div>
</ExpandableSection>
);
}
AttachmentTile
File attachment preview.
import { AttachmentTile } from "@mariozechner/pi-web-ui";
function Attachments() {
return (
<AttachmentTile
attachment={{
name: "image.png",
type: "image/png",
data: base64Data,
}}
onRemove={() => console.log("Removed")}
/>
);
}
Register custom renderers for tool results:
import { registerToolRenderer } from "@mariozechner/pi-web-ui";
registerToolRenderer("my_tool", (props) => {
return (
<div>
<h3>My Tool Result</h3>
<pre>{JSON.stringify(props.details, null, 2)}</pre>
</div>
);
});
Custom Message Renderers
Register custom renderers for message types:
import { registerMessageRenderer } from "@mariozechner/pi-web-ui";
registerMessageRenderer("artifact", (props) => {
return (
<div className="artifact">
<h3>{props.message.title}</h3>
<div>{props.message.content}</div>
</div>
);
});
Example: Complete App
import React from "react";
import { Agent } from "@mariozechner/pi-agent-core";
import { getModel } from "@mariozechner/pi-ai";
import {
AgentInterface,
SettingsDialog,
ModelSelector,
} from "@mariozechner/pi-web-ui";
function App() {
const [agent] = React.useState(() => new Agent({
initialState: {
systemPrompt: "You are a helpful assistant.",
model: getModel("anthropic", "claude-4.5-sonnet-20250514"),
thinkingLevel: "medium",
tools: [],
messages: [],
},
}));
const [settingsOpen, setSettingsOpen] = React.useState(false);
const [modelSelectorOpen, setModelSelectorOpen] = React.useState(false);
const handleSendMessage = async (text: string, attachments?: Attachment[]) => {
// Convert attachments to content
const content = [
{ type: "text", text },
...attachments?.map(a => ({
type: "image",
data: a.data,
mimeType: a.type,
})) || [],
];
const stream = agent.prompt(content);
await stream.result();
};
return (
<div className="app">
<AgentInterface
agent={agent}
onSendMessage={handleSendMessage}
showSidebar
enableAttachments
onOpenSettings={() => setSettingsOpen(true)}
onOpenModelSelector={() => setModelSelectorOpen(true)}
/>
<SettingsDialog
open={settingsOpen}
onClose={() => setSettingsOpen(false)}
/>
<ModelSelector
open={modelSelectorOpen}
onClose={() => setModelSelectorOpen(false)}
currentModel={agent.state.model}
onSelectModel={(model, thinkingLevel) => {
agent.updateState({ model, thinkingLevel });
setModelSelectorOpen(false);
}}
/>
</div>
);
}
export default App;