Framework Components
Deep dive into PACE.js's four core components.
Overview
Every PACE implementation has four components:
| Component | File | Purpose |
|---|---|---|
| ProductCatalog | product-catalog.js | AI-guided product discovery |
| AboutPage | about-page.js | Context and trust building |
| ChatWidget | chat-widget.js | Conversational interface |
| ExecutiveSummary | executive-summary.js | Real-time insights |
ProductCatalog
Purpose
Displays products in a searchable, filterable grid. Groups by category, supports search, and emits selection events.
API
import { ProductCatalog } from '@semanticintent/pace-pattern'
const catalog = new ProductCatalog(products, state)Constructor Parameters
| Parameter | Type | Description |
|---|---|---|
products | Product[] | Array of product objects |
state | State | Shared state instance |
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
render() | - | string | Generate HTML for catalog |
search(query) | string | Product[] | Filter products by query |
filterByCategory(category) | string | Product[] | Filter by category |
attachEvents() | - | void | Attach DOM event listeners |
groupByCategory() | - | object | Group products by category |
Product Data Structure
{
id: 'unique-id',
name: 'Product Name',
tagline: 'Short description',
category: 'Category Name',
description: '## Markdown\n\nFull description...',
action_label: 'Get Started',
action_url: 'https://example.com',
icon: 'https://example.com/icon.svg' // optional
}Usage Example
const products = [
{
id: 'sql-mcp',
name: 'SQL MCP',
tagline: 'Query databases with natural language',
category: 'MCP Servers',
description: '## SQL MCP\n\nNatural language database queries.',
action_label: 'Install',
action_url: 'https://github.com/example/sql-mcp'
}
]
const catalog = new ProductCatalog(products, state)
const html = catalog.render()
// Search
const results = catalog.search('database')
// Filter
const mcpServers = catalog.filterByCategory('MCP Servers')Events
| Event | Payload | When |
|---|---|---|
product:select | { product } | Product card clicked |
product:search | { query, results } | Search performed |
product:filter | { category } | Category filter applied |
AboutPage
Purpose
Displays origin story, philosophy, and context. Builds trust and explains what PACE is.
API
import { AboutPage } from '@semanticintent/pace-pattern'
const about = new AboutPage(config, state)Constructor Parameters
| Parameter | Type | Description |
|---|---|---|
config | object | About page configuration |
state | State | Shared state instance |
Config Structure
{
title: 'About Us',
sections: [
{
title: 'Our Story',
content: '## Origin\n\nMarkdown content...'
},
{
title: 'Philosophy',
content: '## Beliefs\n\nWhat we stand for...'
}
],
links: [
{ label: 'GitHub', url: 'https://github.com/...' },
{ label: 'Docs', url: 'https://docs.example.com' }
]
}Usage Example
const about = new AboutPage({
title: 'About MillPond',
sections: [
{
title: 'What is PACE?',
content: '## PACE Pattern\n\nPattern for Agentic Conversational Experience...'
},
{
title: 'Our Mission',
content: 'Guide users, don\'t make them hunt.'
}
],
links: [
{ label: 'GitHub', url: 'https://github.com/semanticintent/pace.js' }
]
}, state)
const html = about.render()ChatWidget
Purpose
The conversational interface. Handles user messages, communicates with AI adapter, displays chat history.
API
import { ChatWidget } from '@semanticintent/pace-pattern'
const chat = new ChatWidget(config, state)Constructor Parameters
| Parameter | Type | Description |
|---|---|---|
config | object | Chat configuration |
state | State | Shared state instance |
Config Structure
{
greeting: 'Welcome! How can I help?',
placeholder: 'Type your message...',
aiAdapter: claudeAdapter, // or openaiAdapter
context: {
products: products,
storeName: 'Your Store'
}
}Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
render() | - | string | Generate chat HTML |
sendMessage(message) | string | Promise<void> | Send user message |
receiveMessage(message) | string | void | Display AI response |
clearHistory() | - | void | Clear chat history |
attachEvents() | - | void | Attach event listeners |
Usage Example
import { ChatWidget, ClaudeAdapter } from '@semanticintent/pace-pattern'
const adapter = new ClaudeAdapter({
apiKey: process.env.CLAUDE_API_KEY,
model: 'claude-3-sonnet-20240229'
})
const chat = new ChatWidget({
greeting: 'Welcome to the pond. What are you fishing for?',
placeholder: 'Ask me anything...',
aiAdapter: adapter,
context: {
products: products,
storeName: 'MillPond'
}
}, state)
const html = chat.render()
// Send message programmatically
await chat.sendMessage('Tell me about SQL MCP')Message Structure
{
role: 'user' | 'assistant',
content: 'Message text',
timestamp: Date.now(),
metadata: {
expertise: 'beginner' | 'intermediate' | 'advanced',
products: ['sql-mcp'], // mentioned products
intent: 'question' | 'comparison' | 'purchase'
}
}Events
| Event | Payload | When |
|---|---|---|
chat:message | { message, role: 'user' } | User sends message |
chat:response | { message, role: 'assistant', metadata } | AI responds |
chat:error | { error } | AI error occurs |
chat:typing | { isTyping: boolean } | Typing indicator |
ExecutiveSummary
Purpose
Tracks conversation progress, displays discussed products, suggests next steps, shows user expertise level.
API
import { ExecutiveSummary } from '@semanticintent/pace-pattern'
const summary = new ExecutiveSummary(config, state)Constructor Parameters
| Parameter | Type | Description |
|---|---|---|
config | object | Summary configuration |
state | State | Shared state instance |
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
render() | - | string | Generate summary HTML |
update(data) | object | void | Update summary data |
trackProduct(product) | Product | void | Add product to discussed list |
setExpertise(level) | string | void | Update expertise level |
addSuggestion(suggestion) | string | void | Add next step suggestion |
Summary Data Structure
{
conversationSummary: '2-3 sentence overview',
productsDiscussed: [
{
id: 'sql-mcp',
name: 'SQL MCP',
status: 'interested' | 'viewed' | 'deferred'
}
],
userExpertise: 'beginner' | 'intermediate' | 'advanced',
detectedInterests: ['database', 'sql', 'mcp-servers'],
suggestedNextSteps: [
'Try SQL MCP',
'View documentation',
'Compare with alternatives'
],
metrics: {
messagesExchanged: 12,
productsViewed: 3,
timeSpent: 180 // seconds
}
}Usage Example
const summary = new ExecutiveSummary({
enableMetrics: true,
autoUpdate: true
}, state)
const html = summary.render()
// Update manually
summary.update({
conversationSummary: 'User is exploring database tools...',
productsDiscussed: [
{ id: 'sql-mcp', name: 'SQL MCP', status: 'interested' }
],
userExpertise: 'intermediate',
suggestedNextSteps: ['Try SQL MCP', 'View docs']
})
// Track product
summary.trackProduct({
id: 'sql-mcp',
name: 'SQL MCP'
})
// Update expertise
summary.setExpertise('advanced')Events
| Event | Payload | When |
|---|---|---|
summary:update | { data } | Summary data updated |
summary:product-tracked | { product } | Product added to discussed list |
summary:expertise-changed | { level } | Expertise level changed |
Component Lifecycle
All components follow this lifecycle:
Example
// 1. Create component
const catalog = new ProductCatalog(products, state)
// 2. Render to DOM
container.innerHTML = catalog.render()
// 3. Attach events
catalog.attachEvents()
// 4. User interacts...
// (search, filter, click products)
// 5. Update
catalog.search('database')
container.innerHTML = catalog.render()
// 6. Destroy when done
catalog.destroy()Component Communication
Components communicate via:
1. Shared State
// Chat updates state
state.set('selectedProduct', product)
// Product catalog reacts
state.subscribe('selectedProduct', (product) => {
catalog.highlightProduct(product.id)
})2. Events
// Product catalog emits
pace.emit('product:select', { product })
// Chat widget listens
pace.on('product:select', ({ product }) => {
chat.sendMessage(`Tell me about ${product.name}`)
})3. Direct Method Calls
// Executive summary tracks chat activity
chat.on('message', (message) => {
summary.trackMessage(message)
})Customizing Components
Override Rendering
class MyProductCatalog extends ProductCatalog {
renderProduct(product) {
return `
<div class="my-custom-product-card">
<h3>${product.name}</h3>
<p>${product.tagline}</p>
<button onclick="customAction('${product.id}')">
Custom Action
</button>
</div>
`
}
}
const catalog = new MyProductCatalog(products, state)Add Custom Behavior
class EnhancedChat extends ChatWidget {
async sendMessage(message) {
// Pre-process message
const enhanced = await this.enhanceMessage(message)
// Call parent
return super.sendMessage(enhanced)
}
async enhanceMessage(message) {
// Add context, fix typos, etc.
return message
}
}Best Practices
1. Keep Components Focused
✅ Good: ProductCatalog only handles products ❌ Bad: ProductCatalog also handles chat
2. Use State for Sharing
✅ Good: state.set('selectedProduct', product) ❌ Bad: Global variables
3. Emit Events for Actions
✅ Good: pace.emit('product:select', { product }) ❌ Bad: Direct function calls across components
4. Clean Up After Yourself
class Component {
destroy() {
// Remove event listeners
this.element.removeEventListener('click', this.handler)
// Clear references
this.state = null
this.config = null
}
}See Also
- Core Concepts — Architecture overview
- API Reference — Complete API docs
- Build Your First App — Hands-on tutorial
Master the components, master PACE.js. 🧩