Skip to content

Framework Components

Deep dive into PACE.js's four core components.


Overview

Every PACE implementation has four components:

ComponentFilePurpose
ProductCatalogproduct-catalog.jsAI-guided product discovery
AboutPageabout-page.jsContext and trust building
ChatWidgetchat-widget.jsConversational interface
ExecutiveSummaryexecutive-summary.jsReal-time insights

ProductCatalog

Purpose

Displays products in a searchable, filterable grid. Groups by category, supports search, and emits selection events.

API

javascript
import { ProductCatalog } from '@semanticintent/pace-pattern'

const catalog = new ProductCatalog(products, state)

Constructor Parameters

ParameterTypeDescription
productsProduct[]Array of product objects
stateStateShared state instance

Methods

MethodParametersReturnsDescription
render()-stringGenerate HTML for catalog
search(query)stringProduct[]Filter products by query
filterByCategory(category)stringProduct[]Filter by category
attachEvents()-voidAttach DOM event listeners
groupByCategory()-objectGroup products by category

Product Data Structure

javascript
{
  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

javascript
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

EventPayloadWhen
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

javascript
import { AboutPage } from '@semanticintent/pace-pattern'

const about = new AboutPage(config, state)

Constructor Parameters

ParameterTypeDescription
configobjectAbout page configuration
stateStateShared state instance

Config Structure

javascript
{
  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

javascript
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

javascript
import { ChatWidget } from '@semanticintent/pace-pattern'

const chat = new ChatWidget(config, state)

Constructor Parameters

ParameterTypeDescription
configobjectChat configuration
stateStateShared state instance

Config Structure

javascript
{
  greeting: 'Welcome! How can I help?',
  placeholder: 'Type your message...',
  aiAdapter: claudeAdapter, // or openaiAdapter
  context: {
    products: products,
    storeName: 'Your Store'
  }
}

Methods

MethodParametersReturnsDescription
render()-stringGenerate chat HTML
sendMessage(message)stringPromise<void>Send user message
receiveMessage(message)stringvoidDisplay AI response
clearHistory()-voidClear chat history
attachEvents()-voidAttach event listeners

Usage Example

javascript
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

javascript
{
  role: 'user' | 'assistant',
  content: 'Message text',
  timestamp: Date.now(),
  metadata: {
    expertise: 'beginner' | 'intermediate' | 'advanced',
    products: ['sql-mcp'], // mentioned products
    intent: 'question' | 'comparison' | 'purchase'
  }
}

Events

EventPayloadWhen
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

javascript
import { ExecutiveSummary } from '@semanticintent/pace-pattern'

const summary = new ExecutiveSummary(config, state)

Constructor Parameters

ParameterTypeDescription
configobjectSummary configuration
stateStateShared state instance

Methods

MethodParametersReturnsDescription
render()-stringGenerate summary HTML
update(data)objectvoidUpdate summary data
trackProduct(product)ProductvoidAdd product to discussed list
setExpertise(level)stringvoidUpdate expertise level
addSuggestion(suggestion)stringvoidAdd next step suggestion

Summary Data Structure

javascript
{
  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

javascript
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

EventPayloadWhen
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

javascript
// 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

javascript
// Chat updates state
state.set('selectedProduct', product)

// Product catalog reacts
state.subscribe('selectedProduct', (product) => {
  catalog.highlightProduct(product.id)
})

2. Events

javascript
// 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

javascript
// Executive summary tracks chat activity
chat.on('message', (message) => {
  summary.trackMessage(message)
})

Customizing Components

Override Rendering

javascript
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

javascript
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

javascript
class Component {
  destroy() {
    // Remove event listeners
    this.element.removeEventListener('click', this.handler)

    // Clear references
    this.state = null
    this.config = null
  }
}

See Also


Master the components, master PACE.js. 🧩