AI Integration Guide
October 2, 2025 ยท View on GitHub
Real-world examples and patterns for integrating Forge with AI systems.
Table of Contents
- ChatGPT Integration
- Claude Integration
- GitHub Copilot Integration
- Custom AI Agents
- Voice Assistants
- Automated Testing
- Analytics & Monitoring
- Common UI Patterns
- Best Practices
ChatGPT Integration
Basic UI Context Provider
// chatgpt-integration.js
import { Configuration, OpenAIApi } from 'openai';
class ChatGPTUIAssistant {
constructor(apiKey) {
this.openai = new OpenAIApi(new Configuration({ apiKey }));
}
// Gather context from all Forge components on the page
gatherUIContext() {
const components = document.querySelectorAll('[data-semantic-role]');
const context = [];
components.forEach(component => {
if (typeof component.explainState === 'function') {
context.push({
type: component.tagName.toLowerCase(),
state: component.explainState(),
actions: component.getPossibleActions?.() || [],
context: component.getAttribute('ai-context'),
role: component.getAttribute('semantic-role')
});
}
});
return context;
}
// Ask ChatGPT about the current UI state
async askAboutUI(userQuestion) {
const uiContext = this.gatherUIContext();
const response = await this.openai.createChatCompletion({
model: 'gpt-4',
messages: [
{
role: 'system',
content: 'You are a UI assistant. Help users interact with the application.'
},
{
role: 'user',
content: `Current UI state: ${JSON.stringify(uiContext, null, 2)}\n\nUser question: ${userQuestion}`
}
]
});
return response.data.choices[0].message.content;
}
// Suggest next action based on UI state
async suggestNextAction() {
const uiContext = this.gatherUIContext();
const availableActions = uiContext
.flatMap(c => c.actions)
.filter(a => a.available);
const response = await this.openai.createChatCompletion({
model: 'gpt-4',
messages: [
{
role: 'system',
content: 'Analyze the UI and suggest the most logical next action for the user.'
},
{
role: 'user',
content: `Available actions: ${JSON.stringify(availableActions)}\nUI Context: ${JSON.stringify(uiContext)}`
}
]
});
return response.data.choices[0].message.content;
}
}
// Usage
const assistant = new ChatGPTUIAssistant('your-api-key');
// Example: Help user complete a form
const formButton = document.querySelector('forge-button[semantic-role="form-submit"]');
if (!formButton.getPossibleActions().find(a => a.name === 'click').available) {
const suggestion = await assistant.askAboutUI('Why can\'t I submit the form?');
console.log(suggestion); // "The form cannot be submitted because required fields are empty..."
}
Form Validation Assistant
class FormValidationAI {
constructor(openai) {
this.openai = openai;
}
async validateWithAI(formElement) {
// Gather all form fields
const fields = formElement.querySelectorAll('forge-input, forge-select, forge-checkbox');
const fieldData = [];
fields.forEach(field => {
fieldData.push({
name: field.name || field.id,
type: field.type,
value: field.value,
required: field.required,
state: field.explainState(),
semanticRole: field.getAttribute('semantic-role')
});
});
// Ask ChatGPT to validate the form data
const response = await this.openai.createChatCompletion({
model: 'gpt-4',
messages: [
{
role: 'system',
content: 'Validate form data and provide specific, helpful feedback.'
},
{
role: 'user',
content: `Validate this form data: ${JSON.stringify(fieldData)}`
}
]
});
return JSON.parse(response.data.choices[0].message.content);
}
// Real-time field suggestions
async suggestFieldValue(field) {
const context = {
fieldName: field.name,
fieldType: field.type,
currentValue: field.value,
semanticRole: field.getAttribute('semantic-role'),
formContext: field.closest('form')?.getAttribute('ai-context')
};
const response = await this.openai.createChatCompletion({
model: 'gpt-3.5-turbo',
messages: [
{
role: 'user',
content: `Suggest a value for field: ${JSON.stringify(context)}`
}
]
});
return response.data.choices[0].message.content;
}
}
Claude Integration
Claude UI Navigator
// claude-integration.js
class ClaudeUINavigator {
constructor(apiKey) {
this.apiKey = apiKey;
this.apiUrl = 'https://api.anthropic.com/v1/messages';
}
async analyzeComponent(element) {
if (!element.aiState) return null;
const analysis = {
state: element.aiState,
explanation: element.explainState(),
actions: element.getPossibleActions(),
context: element.getAttribute('ai-context'),
role: element.getAttribute('semantic-role'),
performance: {
renderTime: element.renderTime,
renderCount: element.renderCount
}
};
const response = await fetch(this.apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': this.apiKey,
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: 'claude-3-opus-20240229',
messages: [{
role: 'user',
content: `Analyze this UI component and suggest improvements: ${JSON.stringify(analysis)}`
}],
max_tokens: 1024
})
});
const data = await response.json();
return data.content[0].text;
}
// Interactive UI guide
async createInteractiveGuide() {
const components = document.querySelectorAll('[semantic-role]');
const guide = [];
for (const component of components) {
const actions = component.getPossibleActions?.() || [];
const availableActions = actions.filter(a => a.available);
if (availableActions.length > 0) {
guide.push({
element: component.tagName.toLowerCase(),
location: this.getElementPath(component),
purpose: component.getAttribute('semantic-role'),
currentState: component.explainState(),
nextSteps: availableActions.map(a => a.description)
});
}
}
const response = await fetch(this.apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': this.apiKey,
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: 'claude-3-opus-20240229',
messages: [{
role: 'user',
content: `Create a step-by-step guide for using this interface: ${JSON.stringify(guide)}`
}],
max_tokens: 2048
})
});
const data = await response.json();
return data.content[0].text;
}
getElementPath(element) {
const path = [];
let current = element;
while (current && current !== document.body) {
let selector = current.tagName.toLowerCase();
if (current.id) {
selector += `#${current.id}`;
} else if (current.className) {
selector += `.${current.className.split(' ')[0]}`;
}
path.unshift(selector);
current = current.parentElement;
}
return path.join(' > ');
}
}
// Usage Example
const claude = new ClaudeUINavigator('your-api-key');
// Analyze a specific component
const button = document.querySelector('forge-button[semantic-role="checkout"]');
const analysis = await claude.analyzeComponent(button);
console.log(analysis);
// Generate interactive guide
const guide = await claude.createInteractiveGuide();
document.getElementById('help-content').textContent = guide;
Claude-Powered Accessibility Checker
class AccessibilityAI {
constructor(claudeApi) {
this.claude = claudeApi;
}
async checkAccessibility() {
const components = document.querySelectorAll('forge-button, forge-input, forge-select');
const issues = [];
components.forEach(component => {
// Check for missing labels
if (!component.getAttribute('aria-label') && !component.label) {
issues.push({
element: component.tagName.toLowerCase(),
issue: 'Missing accessible label',
severity: 'high',
suggestion: 'Add aria-label or label attribute',
currentState: component.explainState()
});
}
// Check for missing semantic roles
if (!component.getAttribute('semantic-role')) {
issues.push({
element: component.tagName.toLowerCase(),
issue: 'Missing semantic role',
severity: 'medium',
suggestion: 'Add semantic-role attribute for AI understanding'
});
}
});
// Ask Claude for accessibility recommendations
const response = await this.claude.askClaude({
prompt: `Review these accessibility issues and provide fixes: ${JSON.stringify(issues)}`,
context: 'WCAG 2.1 AA compliance'
});
return response;
}
}
GitHub Copilot Integration
Copilot-Friendly Component Usage
// copilot-integration.js
/**
* Copilot can understand Forge components through their AI metadata
* Type annotations help Copilot suggest the right properties
*/
// When you type this comment, Copilot understands the component state
// Create a primary button that submits the user registration form
const submitButton = document.createElement('forge-button');
submitButton.variant = 'primary';
submitButton.setAttribute('semantic-role', 'form-submit');
submitButton.setAttribute('ai-context', 'user-registration');
// Copilot can suggest actions based on component state
if (submitButton.explainState().stateDescription.includes('disabled')) {
// Copilot suggests: Enable the button when form is valid
const form = document.querySelector('form');
form.addEventListener('input', () => {
const isValid = form.checkValidity();
submitButton.disabled = !isValid;
});
}
// Copilot understands available actions
const actions = submitButton.getPossibleActions();
// Copilot autocompletes: Find the 'click' action and check if available
const clickAction = actions.find(a => a.name === 'click');
if (clickAction?.available) {
submitButton.addEventListener('click', handleSubmit);
}
Type-Safe Component Creation with Copilot
// copilot-types.ts
import type { ForgeButton, ForgeInput, ForgeSelect } from '@nexcraft/forge';
/**
* Copilot uses these types to provide better suggestions
*/
class FormBuilder {
/**
* When you start typing, Copilot knows all available properties
*/
createSmartForm() {
// Copilot suggests: Create form with AI metadata
const form = document.createElement('form');
form.setAttribute('ai-context', 'user-profile');
// Copilot knows ForgeInput properties from AI metadata
const emailInput = document.createElement('forge-input') as ForgeInput;
emailInput.type = 'email'; // Copilot suggests valid types
emailInput.required = true;
emailInput.setAttribute('semantic-role', 'email-input');
// Copilot can suggest validation based on component state
emailInput.addEventListener('forge-change', (e) => {
const state = emailInput.explainState();
// Copilot suggests: Check if input is valid
if (state.stateDescription.includes('invalid')) {
this.showError('Please enter a valid email');
}
});
return form;
}
}
Custom AI Agents
Building a Custom UI Agent
// custom-ai-agent.js
class CustomUIAgent {
constructor(config) {
this.config = config;
this.memory = new Map(); // Remember past interactions
}
// Learn from user interactions
learnFromInteraction(component, action, outcome) {
const key = `${component.tagName}-${action}`;
if (!this.memory.has(key)) {
this.memory.set(key, []);
}
this.memory.get(key).push({
state: component.aiState,
action: action,
outcome: outcome,
timestamp: Date.now()
});
}
// Predict user's next action
predictNextAction(currentComponent) {
const state = currentComponent.aiState;
const possibleActions = currentComponent.getPossibleActions();
// Check memory for similar states
const history = this.memory.get(currentComponent.tagName) || [];
const similarStates = history.filter(h =>
JSON.stringify(h.state) === JSON.stringify(state)
);
if (similarStates.length > 0) {
// Return most common action for this state
const actionCounts = {};
similarStates.forEach(s => {
actionCounts[s.action] = (actionCounts[s.action] || 0) + 1;
});
const predictedAction = Object.entries(actionCounts)
.sort(([,a], [,b]) => b - a)[0][0];
return possibleActions.find(a => a.name === predictedAction);
}
// Default to first available action
return possibleActions.find(a => a.available);
}
// Auto-complete forms based on patterns
async autoCompleteForm(form) {
const inputs = form.querySelectorAll('forge-input, forge-select');
for (const input of inputs) {
const semanticRole = input.getAttribute('semantic-role');
const aiContext = input.getAttribute('ai-context');
// Use AI to suggest values based on semantic role
const suggestion = await this.suggestValue({
role: semanticRole,
context: aiContext,
type: input.type,
currentState: input.explainState()
});
if (suggestion && !input.value) {
input.value = suggestion;
input.dispatchEvent(new Event('change'));
}
}
}
async suggestValue(config) {
// Implement your AI logic here
const suggestions = {
'email-input': 'user@example.com',
'name-input': 'John Doe',
'phone-input': '555-0123',
'address-input': '123 Main St'
};
return suggestions[config.role];
}
}
// Usage
const agent = new CustomUIAgent({ learning: true });
// Track all interactions
document.addEventListener('click', (e) => {
if (e.target.tagName.startsWith('FORGE-')) {
agent.learnFromInteraction(e.target, 'click', 'success');
}
});
// Predict and highlight next action
setInterval(() => {
const activeElement = document.activeElement;
if (activeElement?.tagName.startsWith('FORGE-')) {
const prediction = agent.predictNextAction(activeElement);
if (prediction) {
console.log(`Suggested next action: ${prediction.description}`);
}
}
}, 1000);
Voice Assistants
Alexa/Google Assistant Integration
// voice-assistant.js
class VoiceUIController {
constructor() {
this.recognition = new webkitSpeechRecognition();
this.synthesis = window.speechSynthesis;
this.setupRecognition();
}
setupRecognition() {
this.recognition.continuous = true;
this.recognition.interimResults = true;
this.recognition.onresult = (event) => {
const command = event.results[event.results.length - 1][0].transcript;
this.processVoiceCommand(command);
};
}
async processVoiceCommand(command) {
// Find relevant components based on voice command
const components = document.querySelectorAll('[semantic-role]');
for (const component of components) {
const state = component.explainState();
const actions = component.getPossibleActions();
// Match command to component actions
if (command.includes('click') || command.includes('press')) {
const clickAction = actions.find(a => a.name === 'click' && a.available);
if (clickAction && state.stateDescription.toLowerCase().includes(command.toLowerCase())) {
component.click();
this.speak(`Clicked ${component.getAttribute('semantic-role')}`);
break;
}
}
if (command.includes('fill') || command.includes('type')) {
if (component.tagName === 'FORGE-INPUT') {
const value = command.split('type')[1]?.trim();
if (value) {
component.value = value;
this.speak(`Filled ${component.getAttribute('semantic-role')} with ${value}`);
break;
}
}
}
}
}
speak(text) {
const utterance = new SpeechSynthesisUtterance(text);
this.synthesis.speak(utterance);
}
// Describe current UI state
describeUI() {
const components = document.querySelectorAll('[semantic-role]');
const descriptions = [];
components.forEach(component => {
if (component.explainState) {
descriptions.push(component.explainState().stateDescription);
}
});
this.speak(`Current page has ${descriptions.length} interactive elements. ${descriptions.join('. ')}`);
}
}
// Usage
const voiceUI = new VoiceUIController();
// Voice commands
// "Click the submit button"
// "Fill email with john@example.com"
// "Describe the page"
Automated Testing
AI-Driven Test Generation
// ai-test-generator.js
class AITestGenerator {
generateTestsForComponent(componentTag) {
const element = document.createElement(componentTag);
const tests = [];
// Generate tests based on AI metadata
const actions = element.getPossibleActions();
// Test each action
actions.forEach(action => {
tests.push({
name: `should ${action.description}`,
test: async () => {
const el = document.createElement(componentTag);
document.body.appendChild(el);
if (action.available) {
// Perform action
if (typeof el[action.name] === 'function') {
el[action.name](...(action.params || []));
}
// Verify state changed
const newState = el.explainState();
expect(newState).not.toBe(el.explainState());
} else {
// Verify action is not available
expect(() => el[action.name]()).toThrow();
}
document.body.removeChild(el);
}
});
});
// Test state transitions
tests.push({
name: 'should handle state transitions',
test: async () => {
const el = document.createElement(componentTag);
const initialState = el.aiState;
// Trigger state changes
el.disabled = true;
expect(el.aiState.disabled).toBe(true);
expect(el.explainState().stateDescription).toContain('disabled');
el.disabled = false;
expect(el.aiState.disabled).toBe(false);
expect(el.explainState().stateDescription).not.toContain('disabled');
}
});
return tests;
}
// Generate E2E tests from user flows
generateE2ETests(userFlow) {
const tests = [];
userFlow.steps.forEach((step, index) => {
tests.push({
name: `Step ${index + 1}: ${step.description}`,
test: async () => {
const element = document.querySelector(step.selector);
// Verify element is in expected state
expect(element.explainState().stateDescription).toContain(step.expectedState);
// Perform action
if (step.action) {
const action = element.getPossibleActions()
.find(a => a.name === step.action);
expect(action.available).toBe(true);
element[step.action](...(step.params || []));
}
// Verify outcome
if (step.expectedOutcome) {
await new Promise(resolve => setTimeout(resolve, 100));
expect(element.explainState().stateDescription).toContain(step.expectedOutcome);
}
}
});
});
return tests;
}
}
Analytics & Monitoring
AI-Powered Analytics Dashboard
// ai-analytics.js
class AIAnalytics {
constructor() {
this.metrics = [];
this.startTracking();
}
startTracking() {
// Track all Forge component interactions
document.addEventListener('click', this.trackInteraction.bind(this));
document.addEventListener('forge-change', this.trackInteraction.bind(this));
// Monitor performance
setInterval(() => this.collectPerformanceMetrics(), 5000);
}
trackInteraction(event) {
const target = event.target;
if (!target.tagName.startsWith('FORGE-')) return;
const metric = {
timestamp: Date.now(),
component: target.tagName.toLowerCase(),
event: event.type,
state: target.aiState,
explanation: target.explainState(),
semanticRole: target.getAttribute('semantic-role'),
aiContext: target.getAttribute('ai-context'),
performance: {
renderTime: target.renderTime,
renderCount: target.renderCount
}
};
this.metrics.push(metric);
this.analyzePattern(metric);
}
collectPerformanceMetrics() {
const components = document.querySelectorAll('[data-semantic-role]');
const performanceData = [];
components.forEach(component => {
if (component.renderTime !== undefined) {
performanceData.push({
component: component.tagName.toLowerCase(),
renderTime: component.renderTime,
renderCount: component.renderCount,
performanceMode: component.performanceMode,
violations: component.renderTime > (component.maxRenderMs || Infinity)
});
}
});
// Identify performance bottlenecks
const slowComponents = performanceData
.filter(d => d.violations)
.sort((a, b) => b.renderTime - a.renderTime);
if (slowComponents.length > 0) {
console.warn('Performance violations detected:', slowComponents);
this.suggestOptimizations(slowComponents);
}
}
generateInsights() {
return {
mostUsedComponents: this.getMostUsedComponents(),
averageRenderTimes: this.getAverageRenderTimes(),
userFlow: this.detectUserFlow(),
bottlenecks: this.identifyBottlenecks(),
recommendations: this.generateRecommendations()
};
}
}
Common UI Patterns
Authentication Patterns
Login Form
import { ForgeInput, ForgeButton, ForgeCard, ForgeAlert } from '@nexcraft/forge/integrations/react'
function LoginForm() {
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
setError('')
try {
const formData = new FormData(e.target)
await login(formData.get('email'), formData.get('password'))
} catch (err) {
setError('Invalid email or password')
} finally {
setLoading(false)
}
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<ForgeCard className="w-full max-w-md">
<div className="p-8">
<h2 className="text-2xl font-bold text-center text-gray-900 mb-8">
Sign in to your account
</h2>
{error && (
<ForgeAlert severity="error" className="mb-6">
{error}
</ForgeAlert>
)}
<form onSubmit={handleSubmit} className="space-y-6">
<ForgeInput
name="email"
type="email"
label="Email address"
required
autoComplete="email"
semantic-role="email-input"
ai-context="login-form"
/>
<ForgeInput
name="password"
type="password"
label="Password"
required
autoComplete="current-password"
semantic-role="password-input"
ai-context="login-form"
/>
<ForgeButton
type="submit"
variant="primary"
className="w-full"
loading={loading}
semantic-role="form-submit"
ai-context="login-form.submit"
>
Sign in
</ForgeButton>
</form>
</div>
</ForgeCard>
</div>
)
}
Dashboard Patterns
KPI Cards
import { ForgeCard, ForgeBadge, ForgeProgress } from '@nexcraft/forge/integrations/react'
function KPICards({ data }) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<ForgeCard semantic-role="kpi-card" ai-context="dashboard.revenue">
<div className="p-6">
<p className="text-sm font-medium text-gray-500 uppercase">Total Revenue</p>
<p className="text-3xl font-bold text-gray-900">${data.revenue.toLocaleString()}</p>
<div className="mt-4">
<ForgeBadge variant="success">+12.5%</ForgeBadge>
<span className="text-sm text-gray-500 ml-2">vs last period</span>
</div>
</div>
</ForgeCard>
</div>
)
}
Form Patterns
Contact Form
import { ForgeInput, ForgeButton, ForgeCard, ForgeSelect } from '@nexcraft/forge/integrations/react'
function ContactForm() {
const [submitted, setSubmitted] = useState(false)
const [loading, setLoading] = useState(false)
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
const formData = new FormData(e.target)
try {
await submitContactForm(Object.fromEntries(formData))
setSubmitted(true)
} catch (error) {
console.error('Failed to submit form:', error)
} finally {
setLoading(false)
}
}
return (
<ForgeCard className="max-w-2xl mx-auto" semantic-role="contact-form" ai-context="customer-support">
<div className="p-8">
<h2 className="text-2xl font-bold text-gray-900 mb-6">Get in Touch</h2>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<ForgeInput
name="firstName"
label="First name"
required
semantic-role="name-input"
ai-context="contact-form.firstname"
/>
<ForgeInput
name="lastName"
label="Last name"
required
semantic-role="name-input"
ai-context="contact-form.lastname"
/>
</div>
<ForgeInput
name="email"
type="email"
label="Email address"
required
semantic-role="email-input"
ai-context="contact-form.email"
/>
<ForgeSelect
name="subject"
label="Subject"
required
semantic-role="category-select"
ai-context="contact-form.subject"
options={[
{ value: 'general', label: 'General Inquiry' },
{ value: 'support', label: 'Technical Support' },
{ value: 'sales', label: 'Sales Question' }
]}
/>
<ForgeButton
type="submit"
variant="primary"
loading={loading}
semantic-role="form-submit"
ai-context="contact-form.submit"
>
Send Message
</ForgeButton>
</form>
</div>
</ForgeCard>
)
}
Best Practices
1. Always Set Semantic Roles
<!-- Good: AI understands the purpose -->
<forge-button semantic-role="checkout" ai-context="shopping-cart">
Proceed to Checkout
</forge-button>
<!-- Bad: No context for AI -->
<forge-button>Proceed to Checkout</forge-button>
2. Use AI Context Hierarchies
<div ai-context="user-profile">
<section ai-context="user-profile.settings">
<forge-input semantic-role="email-preference" />
<forge-button semantic-role="save-settings" />
</section>
</div>
3. Monitor Performance in Production
// Set up performance monitoring for all components
document.querySelectorAll('[semantic-role]').forEach(component => {
component.setAttribute('max-render-ms', '5');
component.setAttribute('performance-mode', 'auto');
});
4. Cache AI Responses
class CachedAI {
constructor() {
this.cache = new Map();
}
async getAIResponse(component) {
const key = JSON.stringify(component.aiState);
if (this.cache.has(key)) {
return this.cache.get(key);
}
const response = await this.fetchAIResponse(component);
this.cache.set(key, response);
// Clear old cache entries
if (this.cache.size > 100) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
return response;
}
}
5. Component Discovery by AI Agents
AI agents can discover these methods through:
- AI Manifest -
ai-manifest.jsondocuments all AI methods - TypeScript Definitions -
.d.tsfiles show method signatures - Package Documentation - README and docs explain usage
- Runtime Inspection - Methods are available on component instances