Skip to main content

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
Agent
required
Agent instance from @mariozechner/pi-agent-core
onSendMessage
function
required
Callback when user sends a message
theme
'light' | 'dark'
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
    />
  );
}
agent
Agent
required
Agent instance
onSendMessage
function
required
Callback with message text and optional attachments
showSidebar
boolean
Show sidebar with sessions. Default: false
enableAttachments
boolean
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}
    />
  );
}
messages
AgentMessage[]
required
List of messages to display
streamMessage
AgentMessage | null
Currently streaming message

Input

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
    />
  );
}
onSubmit
function
required
Callback when message is submitted
placeholder
string
Input placeholder text
enableAttachments
boolean
Enable file attachments. Default: false
disabled
boolean
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
thinking
string
Reasoning/thinking content to display

ToolMessage

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 }}
    />
  );
}
toolName
string
required
Name of the tool
content
Content[]
required
Tool result content
details
any
Tool-specific metadata

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 });
      }}
    />
  );
}
open
boolean
required
Whether dialog is open
onClose
function
required
Callback to close dialog
currentModel
Model
required
Currently selected model
onSelectModel
function
required
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"]}
    />
  );
}
open
boolean
required
Whether dialog is open
onClose
function
required
Callback to close dialog
tabs
string[]
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")}
    />
  );
}

Custom Tool Renderers

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;

Build docs developers (and LLMs) love