Documentation Index
Fetch the complete documentation index at: https://mintlify.com/pt-act/pi-mono/llms.txt
Use this file to discover all available pages before exploring further.
Reusable web UI components for building AI chat interfaces powered by @mariozechner/pi-ai and @mariozechner/pi-agent-core.
Built with mini-lit web components and Tailwind CSS v4.
Features
- Chat UI: Complete interface with message history, streaming, and tool execution
- Tools: JavaScript REPL, document extraction, and artifacts (HTML, SVG, Markdown, etc.)
- Attachments: PDF, DOCX, XLSX, PPTX, images with preview and text extraction
- Artifacts: Interactive HTML, SVG, Markdown with sandboxed execution
- Storage: IndexedDB-backed storage for sessions, API keys, and settings
- CORS Proxy: Automatic proxy handling for browser environments
- Custom Providers: Support for Ollama, LM Studio, vLLM, and OpenAI-compatible APIs
Installation
npm install @mariozechner/pi-web-ui @mariozechner/pi-agent-core @mariozechner/pi-ai
Quick Start
import { Agent } from '@mariozechner/pi-agent-core';
import { getModel } from '@mariozechner/pi-ai';
import {
ChatPanel,
AppStorage,
IndexedDBStorageBackend,
ProviderKeysStore,
SessionsStore,
SettingsStore,
setAppStorage,
defaultConvertToLlm,
ApiKeyPromptDialog,
} from '@mariozechner/pi-web-ui';
import '@mariozechner/pi-web-ui/app.css';
// Set up storage
const settings = new SettingsStore();
const providerKeys = new ProviderKeysStore();
const sessions = new SessionsStore();
const backend = new IndexedDBStorageBackend({
dbName: 'my-app',
version: 1,
stores: [
settings.getConfig(),
providerKeys.getConfig(),
sessions.getConfig(),
SessionsStore.getMetadataConfig(),
],
});
settings.setBackend(backend);
providerKeys.setBackend(backend);
sessions.setBackend(backend);
const storage = new AppStorage(settings, providerKeys, sessions, undefined, backend);
setAppStorage(storage);
// Create agent
const agent = new Agent({
initialState: {
systemPrompt: 'You are a helpful assistant.',
model: getModel('anthropic', 'claude-sonnet-4-5-20250929'),
thinkingLevel: 'off',
messages: [],
tools: [],
},
convertToLlm: defaultConvertToLlm,
});
// Create chat panel
const chatPanel = new ChatPanel();
await chatPanel.setAgent(agent, {
onApiKeyRequired: (provider) => ApiKeyPromptDialog.prompt(provider),
});
document.body.appendChild(chatPanel);
Architecture
The web UI follows a layered architecture:
┌─────────────────────────────────────────────────────┐
│ ChatPanel │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ AgentInterface │ │ ArtifactsPanel │ │
│ │ (messages, input) │ │ (HTML, SVG, MD) │ │
│ └─────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Agent (from pi-agent-core) │
│ - State management (messages, model, tools) │
│ - Event emission (agent_start, message_update, ...) │
│ - Tool execution │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ AppStorage │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Settings │ │ Provider │ │ Sessions │ │
│ │ Store │ │Keys Store│ │ Store │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ IndexedDBStorageBackend │
└─────────────────────────────────────────────────────┘
Core Components
ChatPanel
High-level chat interface with built-in artifacts panel.
const chatPanel = new ChatPanel();
await chatPanel.setAgent(agent, {
// Prompt for API key when needed
onApiKeyRequired: async (provider) => ApiKeyPromptDialog.prompt(provider),
// Hook before sending messages
onBeforeSend: async () => { /* save draft, etc. */ },
// Handle cost display click
onCostClick: () => { /* show cost breakdown */ },
// Custom sandbox URL for browser extensions
sandboxUrlProvider: () => chrome.runtime.getURL('sandbox.html'),
// Add custom tools
toolsFactory: (agent, agentInterface, artifactsPanel, runtimeProvidersFactory) => {
const replTool = createJavaScriptReplTool();
replTool.runtimeProvidersFactory = runtimeProvidersFactory;
return [replTool];
},
});
AgentInterface
Lower-level chat interface for custom layouts.
const chat = document.createElement('agent-interface') as AgentInterface;
chat.session = agent;
chat.enableAttachments = true;
chat.enableModelSelector = true;
chat.enableThinkingSelector = true;
chat.onApiKeyRequired = async (provider) => { /* ... */ };
chat.onBeforeSend = async () => { /* ... */ };
Properties:
session: Agent instance
enableAttachments: Show attachment button (default: true)
enableModelSelector: Show model selector (default: true)
enableThinkingSelector: Show thinking level selector (default: true)
showThemeToggle: Show theme toggle (default: false)
Storage System
Setup
import {
AppStorage,
IndexedDBStorageBackend,
SettingsStore,
ProviderKeysStore,
SessionsStore,
CustomProvidersStore,
setAppStorage,
} from '@mariozechner/pi-web-ui';
// Create stores
const settings = new SettingsStore();
const providerKeys = new ProviderKeysStore();
const sessions = new SessionsStore();
const customProviders = new CustomProvidersStore();
// Create backend with all store configs
const backend = new IndexedDBStorageBackend({
dbName: 'my-app',
version: 1,
stores: [
settings.getConfig(),
providerKeys.getConfig(),
sessions.getConfig(),
SessionsStore.getMetadataConfig(),
customProviders.getConfig(),
],
});
// Wire stores to backend
settings.setBackend(backend);
providerKeys.setBackend(backend);
sessions.setBackend(backend);
customProviders.setBackend(backend);
// Create and set global storage
const storage = new AppStorage(settings, providerKeys, sessions, customProviders, backend);
setAppStorage(storage);
Using Stores
SettingsStore - Key-value settings:
await storage.settings.set('proxy.enabled', true);
const enabled = await storage.settings.get<boolean>('proxy.enabled');
ProviderKeysStore - API keys by provider:
await storage.providerKeys.set('anthropic', 'sk-ant-...');
const key = await storage.providerKeys.get('anthropic');
const providers = await storage.providerKeys.list();
SessionsStore - Chat sessions with metadata:
// Save session
await storage.sessions.save(sessionData, metadata);
// Load session
const data = await storage.sessions.get(sessionId);
const metadata = await storage.sessions.getMetadata(sessionId);
// List sessions (sorted by lastModified)
const allMetadata = await storage.sessions.getAllMetadata();
// Update title
await storage.sessions.updateTitle(sessionId, 'New Title');
// Delete
await storage.sessions.delete(sessionId);
JavaScript REPL
Execute JavaScript in a sandboxed browser environment:
import { createJavaScriptReplTool } from '@mariozechner/pi-web-ui';
const replTool = createJavaScriptReplTool();
// Configure runtime providers for artifact/attachment access
replTool.runtimeProvidersFactory = () => [
new AttachmentsRuntimeProvider(attachments),
new ArtifactsRuntimeProvider(artifactsPanel, agent, true), // read-write
];
agent.setTools([replTool]);
Extract text from documents at URLs:
import { createExtractDocumentTool } from '@mariozechner/pi-web-ui';
const extractTool = createExtractDocumentTool();
extractTool.corsProxyUrl = 'https://corsproxy.io/?';
agent.setTools([extractTool]);
Built into ArtifactsPanel, supports: HTML, SVG, Markdown, text, JSON, images, PDF, DOCX, XLSX.
const artifactsPanel = new ArtifactsPanel();
artifactsPanel.agent = agent;
// The tool is available as artifactsPanel.tool
agent.setTools([artifactsPanel.tool]);
Attachments
Load and process files:
import { loadAttachment, type Attachment } from '@mariozechner/pi-web-ui';
// From File input
const file = inputElement.files[0];
const attachment = await loadAttachment(file);
// From URL
const attachment = await loadAttachment('https://example.com/doc.pdf');
// From ArrayBuffer
const attachment = await loadAttachment(arrayBuffer, 'document.pdf');
Attachment structure:
interface Attachment {
id: string;
type: 'image' | 'document';
fileName: string;
mimeType: string;
size: number;
content: string; // base64 encoded
extractedText?: string; // For documents
preview?: string; // base64 preview image
}
Supported formats: PDF, DOCX, XLSX, PPTX, images, text files.
Dialogs
SettingsDialog
import { SettingsDialog, ProvidersModelsTab, ProxyTab, ApiKeysTab } from '@mariozechner/pi-web-ui';
SettingsDialog.open([
new ProvidersModelsTab(), // Custom providers + model list
new ProxyTab(), // CORS proxy settings
new ApiKeysTab(), // API keys per provider
]);
SessionListDialog
import { SessionListDialog } from '@mariozechner/pi-web-ui';
SessionListDialog.open(
async (sessionId) => { /* load session */ },
(deletedId) => { /* handle deletion */ },
);
ApiKeyPromptDialog
import { ApiKeyPromptDialog } from '@mariozechner/pi-web-ui';
const success = await ApiKeyPromptDialog.prompt('anthropic');
ModelSelector
import { ModelSelector } from '@mariozechner/pi-web-ui';
ModelSelector.open(currentModel, (selectedModel) => {
agent.setModel(selectedModel);
});
CORS Proxy
For browser environments with CORS restrictions:
import { createStreamFn, shouldUseProxyForProvider } from '@mariozechner/pi-web-ui';
// AgentInterface auto-configures proxy from settings
// For manual setup:
agent.streamFn = createStreamFn(async () => {
const enabled = await storage.settings.get<boolean>('proxy.enabled');
return enabled ? await storage.settings.get<string>('proxy.url') : undefined;
});
Providers requiring proxy:
zai: always
anthropic: only OAuth tokens (sk-ant-oat-*)
Styling
Import the pre-built CSS:
import '@mariozechner/pi-web-ui/app.css';
Or use Tailwind with custom config:
@import '@mariozechner/mini-lit/themes/claude.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
Examples
See the example directory for a complete web app with:
- Session management
- Custom message types
- Artifact panel integration
- Storage setup
- Dialog usage
License
MIT