How pi-web Implements Chat Input Controls for Models, Tools, Thinking, and Compaction
pi-web implements chat input controls through the ChatInput component in components/ChatInput.tsx, using React state-managed dropdowns for model selection, tool presets, and thinking levels, plus conditional buttons for compaction actions that communicate with the parent ChatWindow via typed callbacks.
The π-Web repository provides a React-based chat interface with sophisticated input controls for managing AI model behavior. These pi-web chat input controls reside in the ChatInput component and handle four distinct interaction patterns: dynamic model selection, tool-preset toggling, thinking-level adjustment, and manual context compaction. Each control maintains local UI state while delegating persistent changes to the parent component and underlying RPC manager.
Model Selection Controls
State Management for Model Dropdowns
The model selector uses local React state to manage dropdown visibility and positioning. Two state variables control the interaction: modelDropdownOpen tracks whether the dropdown is visible, while modelDropdownRect stores the screen coordinates for absolute positioning.
The component constructs available options by prioritizing the modelList prop over the legacy modelNames mapping. According to the source code in components/ChatInput.tsx, the option generation logic handles both data structures:
// lines 48-58
const modelOptions: ModelOption[] = (() => {
if (modelList && modelList.length > 0) {
return modelList.map(m => ({provider: m.provider, modelId: m.id, name: m.name}))
.sort(compareModelOptions);
}
return Object.entries(modelNames ?? {})
.map(([modelId, name]) => ({
provider: model?.provider ?? "unknown",
modelId,
name,
}))
.sort(compareModelOptions);
})();
Rendering and Selection
The UI renders a button displaying the current displayModelName (lines 1700–1740). When clicked, it reveals a grouped dropdown organized by provider. Selecting an option triggers the onModelChange(provider, modelId) callback passed from ChatWindow and immediately closes the dropdown panel.
Tool-Preset Selector Implementation
Tool presets are defined as static constants in components/ChatInput.tsx. The TOOL_PRESETS array provides the available options, while TOOL_PRESET_MAP translates user-facing labels to internal values (none, default, full) on lines 77–79.
The selector appears when users click the "tools" icon, toggling the toolDropdownOpen state. Each preset button executes a handler that validates the selection before notifying the parent:
// lines 20-26 (excerpt)
onClick={() => {
setToolDropdownOpen(false);
if (!isActive) onToolPresetChange(preset);
}}
The UI reflects the active state through the isActive boolean, displaying both the preset label (lvl) and description (desc) to provide context for the current tool configuration.
Thinking-Level Configuration
Thinking levels are declared as a constant array with an accompanying description map (lines 88–98). The component receives three relevant props: thinkingLevel (current value), thinkingLevelMap (optional translation for UI labels), and onThinkingLevelChange (callback).
Similar to other dropdowns, this control uses thinkingDropdownOpen state to manage visibility. When a user selects a level, the handler invokes onThinkingLevelChange(level) with the selected internal key and closes the dropdown. The optional thinkingLevelMap allows the interface to display user-friendly names while maintaining consistent internal identifiers.
Context Compaction Controls
Start and Abort Compaction
Compaction functionality appears only when the onCompact callback prop is provided and the stream is inactive (!isStreaming). The implementation supports two distinct operational modes through a single button interface.
When isCompacting is false, clicking the button initiates compaction via onCompact. During active compaction (isCompacting is true), the same button transforms into an abort control that calls onAbortCompaction. The button includes dynamic accessibility attributes:
- ARIA labels switch between "Compact context" and "Stop compaction"
- Tooltip text (
titleattribute) updates to reflect current action - Error display renders above the button when
compactErroris present (lines 40–49)
Integration with ChatWindow and Server
ChatWindow instantiates ChatInput with session-aware callbacks that bridge UI interactions to server-side effects. When users trigger any control change, the corresponding callback (onModelChange, onToolPresetChange, onThinkingLevelChange, onCompact, or onAbortCompaction) executes, updating session state through lib/rpc-manager.ts.
The RPC manager (rpc-manager.ts) persists these changes to the agent session, with updated values flowing back down to the UI via prop updates. This unidirectional data flow ensures the ChatInput component remains pure and predictable while supporting complex stateful operations like model switching and context compaction.
Summary
- pi-web chat input controls are centralized in
components/ChatInput.tsxas a React functional component with local state management for dropdown visibility. - Model selection supports dynamic option lists via
modelListor legacymodelNamesprops, sorted usingcompareModelOptions. - Tool presets use static constants (
TOOL_PRESETS,TOOL_PRESET_MAP) with visual feedback through theisActivestate. - Thinking levels accept an optional mapping prop (
thinkingLevelMap) to decouple internal keys from display labels. - Compaction exposes dual callbacks (
onCompact,onAbortCompaction) and error handling through thecompactErrorprop. - All control callbacks are forwarded from
ChatWindowand processed server-side bylib/rpc-manager.ts.
Frequently Asked Questions
How does pi-web handle model selection state?
pi-web manages model selection through React state variables modelDropdownOpen and modelDropdownRect in components/ChatInput.tsx. The component calculates available models by checking the modelList prop first, falling back to modelNames if undefined, and sorting results with a custom comparator. Selection changes trigger onModelChange(provider, modelId), which ChatWindow forwards to the RPC manager.
What props does the compaction button require?
The compaction button requires two optional callback props: onCompact to initiate compression and onAbortCompaction to cancel ongoing operations. It also consumes isStreaming (to disable during generation), isCompacting (to toggle button state), and compactError (to display failure messages). These props are defined in components/ChatInput.tsx lines 38–88.
Where are tool presets defined in pi-web?
Tool presets are defined as static constants in components/ChatInput.tsx. The TOOL_PRESETS array contains the available configurations, while TOOL_PRESET_MAP translates these into internal values (none, default, full). This mapping occurs on lines 77–79 of the source file.
How is the thinking level mapped to UI labels?
pi-web supports thinking level localization through the optional thinkingLevelMap prop. This allows internal keys (defined in the constant array on lines 88–98) to display as user-friendly strings. When rendering the dropdown, the component looks up each level in this map, defaulting to the raw key if no translation exists.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →