Skip to content

ChatPanel: Top-level Layout and Wiring

源码版本v0.73.1

ChatPanel is the top-level customElement of pi-web-ui (<pi-chat-panel>). It does not run the session itself — it assembles AgentInterface, ArtifactsPanel, and toolsFactory: it takes the Agent passed in from outside, creates an agent-interface child element and assigns session to it, switches between the "side-by-side" and "overlay" artifacts layouts based on screen width, and calls toolsFactory so the host can inject additional AgentTools.

Responsibilities

  1. Hold agent and agentInterface: @state agent and @state agentInterface fields; agent is passed in by the host, agentInterface is created via document.createElement in setAgent, see packages/web-ui/src/ChatPanel.ts:17-25.
  2. Wire the session host: setAgent creates AgentInterface, sets up callbacks like enableAttachments/onApiKeyRequired/onBeforeSend, and attaches agent as session, see packages/web-ui/src/ChatPanel.ts:56-85.
  3. Wire the artifacts panel: new ArtifactsPanel(), injects sandboxUrlProvider, registers ArtifactsToolRenderer into the renderer registry, see packages/web-ui/src/ChatPanel.ts:86-94.
  4. Build runtime providers: runtimeProvidersFactory aggregates attachments from session history into an AttachmentsRuntimeProvider, plus a readable/writable ArtifactsRuntimeProvider, for REPL tools to consume, see packages/web-ui/src/ChatPanel.ts:96-116.
  5. Responsive layout: windowWidth < 800 switches to overlay; otherwise side-by-side 50/50; when artifacts are collapsed a floating pill is shown, see packages/web-ui/src/ChatPanel.ts:159-208.

Design motivation

Why put the ArtifactsPanel wiring in ChatPanel instead of AgentInterface? Because artifacts are a "session output" dimension, decoupled from message rendering: AgentInterface only watches the message stream, ArtifactsPanel only watches artifact messages and maintains its own artifacts Map. Both share data through agent.state, but their lifecycles and UI positions are different. ChatPanel as the layout layer assembles them, and at the same time exposes toolsFactory to the host so the host can inject business-specific tools (e.g. a query_database for some app) without modifying pi-web-ui source.

BREAKPOINT = 800 is an empirical value: on narrow screens artifacts cover the chat area full-screen, avoiding both columns being squeezed to unreadable widths; on wide screens they sit side by side so you can chat and inspect outputs at the same time. overlay and collapsed are pushed from ChatPanel to ArtifactsPanel inside render based on state, rather than letting the panel listen to window itself — that keeps layout decisions at the top level.

Key files

Inside setAgent, the creation of AgentInterface and injection of callbacks is the seam between host and session host:

typescript
// packages/web-ui/src/ChatPanel.ts:74-84
this.agentInterface = document.createElement("agent-interface") as AgentInterface;
this.agentInterface.session = agent;
this.agentInterface.enableAttachments = true;
this.agentInterface.enableModelSelector = true;
this.agentInterface.enableThinkingSelector = true;
this.agentInterface.showThemeToggle = false;
this.agentInterface.onApiKeyRequired = config?.onApiKeyRequired;
this.agentInterface.onModelSelect = config?.onModelSelect;
this.agentInterface.onBeforeSend = config?.onBeforeSend;
this.agentInterface.onCostClick = config?.onCostClick;

toolsFactory lets the host inject extra tools, while the artifacts tool is always added first:

typescript
// packages/web-ui/src/ChatPanel.ts:141-144
const additionalTools =
    config?.toolsFactory?.(agent, this.agentInterface, this.artifactsPanel, runtimeProvidersFactory) || [];
const tools = [this.artifactsPanel.tool, ...additionalTools];
this.agent.state.tools = tools;

Data flow

setAgent is the single thread that turns the Agent passed by the host into a usable UI:

Layout switching is driven by state checks inside render:

Edges and failures

  • Agent unset: render shows "No agent set" to avoid crashing when agentInterface is undefined, see packages/web-ui/src/ChatPanel.ts:160-164.
  • Suppress auto-expand during rebuild: reconstructFromMessages temporarily nulls onArtifactsChange so historical artifacts do not auto-pop the panel, see packages/web-ui/src/ChatPanel.ts:148-151.
  • Artifacts count is 0: hasArtifacts=false, showArtifactsPanel=false, the pill is not rendered, the right panel is hidden.
  • Only new artifacts auto-expand: showArtifactsPanel is set to true only when count > artifactCount, so an existing artifact reappearing does not force a pop, see packages/web-ui/src/ChatPanel.ts:118-127.
  • Resize listener cleanup: disconnectedCallback removes the window.resize listener to prevent leaks, see packages/web-ui/src/ChatPanel.ts:51-54.

Summary

ChatPanel is the layout layer plus the assembly workshop: it wires AgentInterface, ArtifactsPanel, and toolsFactory, and switches layout by window width. Look inside to see AgentInterface session host; for the registration mechanism of tool renderers see Tool renderer registry.