Skip to content

ChatPanel:最上位レイアウトとツール組み立て

源码版本v0.73.1

ChatPanelpi-web-ui の最上位 customElement(<pi-chat-panel>)。自身は会話を走らせず、AgentInterfaceArtifactsPaneltoolsFactory を一つにまとめる役を担う。外部から渡された Agent を受け取り、agent-interface 子要素を生成して session を差し、画面幅に応じて artifacts の「並列」と「オーバーレイ」を切り替え、toolsFactory で host が追加の AgentTool を注入できるようにする。

責務

  1. agent と agentInterface の保持:@state agent@state agentInterface の 2 フィールド。agent は host から受け取り、agentInterfacesetAgent 時に document.createElement で生成する。packages/web-ui/src/ChatPanel.ts:17-25 参照。
  2. セッションホストの組み立て:setAgentAgentInterface を作り、enableAttachments/onApiKeyRequired/onBeforeSend などのコールバックを設定し、agentsession として取り付ける。packages/web-ui/src/ChatPanel.ts:56-85 参照。
  3. artifacts パネルの組み立て:new ArtifactsPanel()sandboxUrlProvider の注入、ArtifactsToolRenderer を renderer registry に登録。packages/web-ui/src/ChatPanel.ts:86-94 参照。
  4. runtime providers の構築:runtimeProvidersFactory が会話履歴中の添付を AttachmentsRuntimeProvider に集約し、読み書き可能な ArtifactsRuntimeProvider と合わせて REPL ツールが消費できるようにする。packages/web-ui/src/ChatPanel.ts:96-116 参照。
  5. レスポンシブレイアウト:windowWidth < 800 で overlay、それ以外は 50/50 の並列。artifacts 折りたたみ時は浮动 pill を表示。packages/web-ui/src/ChatPanel.ts:159-208 参照。

設計動機

なぜ ArtifactsPanel の組み立てを AgentInterface ではなく ChatPanel に置くのか。artifacts は「会話産物」という別軸の概念で、メッセージリストの描画と切り離したいからだ。AgentInterface はメッセージストリームだけを見て、ArtifactsPanel は artifact メッセージだけを見て自身の artifacts Map を保つ。両者は agent.state 経由でデータを共有するけれど、ライフサイクルも UI 上の位置も異なる。ChatPanel がレイアウト層としてこれらを組み立てると同時に、toolsFactory を host に晒す。これにより host は pi-web-ui のソースを変えずに業務専用ツール(例えばあるアプリの query_database)を注入できる。

BREAKPOINT = 800 は経験値だ。狭画面では artifacts がチャット領域を全画面覆い、2 列とも圧縮されて読めなくなるのを避ける。広画面では並列にして会話しながら産物を眺める。overlaycollapsedArtifactsPanel 自身に窓監視させるのではなく、ChatPanelrender で状態に基づいて押し込む。レイアウト判断を最上位に集約するためだ。

主要ファイル

setAgent 内での AgentInterface 生成とコールバック注入が、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 で host がツールを追加する一方で、artifacts ツールは常に先頭に置かれる:

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;

データフロー

setAgent が一本の筋で、host から渡された Agent を UI として組み上げる:

レイアウト切り替えは render 内の状態判定で駆動する:

境界と失敗

  • agent 未設定:render は「No agent set」をそのまま出し、agentInterface が undefined で吹き飛ぶのを防ぐ。packages/web-ui/src/ChatPanel.ts:160-164 参照。
  • 復元時の自動展開抑制:reconstructFromMessages の直前に onArtifactsChange を一時的に空にし、履歴の artifacts がパネルを勝手に開くのを止める。packages/web-ui/src/ChatPanel.ts:148-151 参照。
  • artifacts が 0 件:hasArtifacts=falseshowArtifactsPanel=false になり、pill は描画されず、右側パネルは隠れる。
  • 新規 artifact だけ自動展開:count > artifactCount のときだけ showArtifactsPanel を true にする。既存 artifact が再出現して無理やり開くのを避けるためだ。packages/web-ui/src/ChatPanel.ts:118-127 参照。
  • resize リスナの後始末:disconnectedCallbackwindow.resize リスナを外し、leak を防ぐ。packages/web-ui/src/ChatPanel.ts:51-54 参照。

まとめ

ChatPanel はレイアウト層であり、組み立て工場でもある。AgentInterfaceArtifactsPaneltoolsFactory を組み立て、窓幅でレイアウトを切り替える。内側に入ると AgentInterface セッションホスト、ツール描画の登録仕組みは ツールレンダラーレジストリ を参照。