visual-overlay
SKILL.md
Visual Overlay for AgentPulse
Add animated cursor, typing, and click effects to show users what the AI agent is doing.
Quick Start
Add VisualOverlay inside your AgentPulseProvider:
import { AgentPulseProvider, VisualOverlay } from 'agentpulse';
function App() {
return (
<AgentPulseProvider endpoint="ws://localhost:3100/ws">
<VisualOverlay />
<MyApp />
</AgentPulseProvider>
);
}
When an AI agent calls expose_set or expose_call, users see:
- Animated cursor moving to the target element
- Character-by-character typing for text inputs
- Click ripple effects for button actions
Configuration
<VisualOverlay
enabled={true} // Master toggle (default: true)
cursor={true} // Show AI cursor (default: true)
clickRipple={true} // Show click effects (default: true)
typingAnimation={true} // Character-by-character typing (default: true)
typingSpeed={12} // Characters per second (default: 12)
/>
Element Targeting
The overlay finds elements using data-agentpulse-id attributes.
Naming Convention
Format: data-agentpulse-id="componentId-normalizedKey"
Where normalizedKey = binding key with set prefix removed, lowercased.
| Binding | Normalized Key | Attribute |
|---|---|---|
setName |
name |
data-agentpulse-id="form-name" |
setEmail |
email |
data-agentpulse-id="form-email" |
submitForm |
submitform |
data-agentpulse-id="form-submitform" |
setValue |
value |
data-agentpulse-id="input-value" |
Example
function ContactForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
useExpose('contact-form', {
setName: (v) => setName(v),
setEmail: (v) => setEmail(v),
submitForm: () => handleSubmit(),
});
return (
<form>
<input data-agentpulse-id="contact-form-name" value={name} />
<input data-agentpulse-id="contact-form-email" value={email} />
<button data-agentpulse-id="contact-form-submitform">Submit</button>
</form>
);
}
Auto-Discovery Fallback Chain
If data-agentpulse-id is missing, the resolver tries (in order):
[data-agentpulse-id="componentId-normalizedKey"]- Form container
[data-agentpulse-id="componentId"]→ input by name input[name="key"]ortextarea[name="key"]getElementById(key)orgetElementById("componentId-key")input[placeholder*="key"](case-insensitive)[aria-label*="key"](case-insensitive)- Submit button detection for
submitFormactions - Open/close button detection for modal actions
Custom CSS Selectors
For complex layouts where auto-discovery fails:
<VisualOverlay
targets={{
'contact-form': {
name: 'input[name="fullName"]',
email: 'input[type="email"]',
submit: 'button[type="submit"]',
},
'sidebar': {
toggle: '#sidebar-toggle',
},
}}
/>
Or configure programmatically:
import { setAnimationConfig, clearAnimationConfig } from 'agentpulse';
setAnimationConfig({
'my-form': {
username: '#username-input',
password: '#password-input',
},
});
// Clear when done
clearAnimationConfig();
Common Patterns
Form with Multiple Fields
useExpose('signup-form', {
setEmail: (v) => setEmail(v),
setPassword: (v) => setPassword(v),
submit: () => handleSubmit(),
});
// Add data attributes to each input
<input data-agentpulse-id="signup-form-email" />
<input data-agentpulse-id="signup-form-password" />
<button data-agentpulse-id="signup-form-submit">Sign Up</button>
Third-Party Component Libraries
For MUI, Chakra, etc., wrap or pass the data attribute:
// MUI TextField
<TextField
inputProps={{ 'data-agentpulse-id': 'form-email' }}
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
// Or use CSS selector config
<VisualOverlay
targets={{
'form': {
email: '.MuiTextField-root input',
},
}}
/>
Search Box
useExpose('search', {
query,
setQuery,
search: () => performSearch(),
});
<input data-agentpulse-id="search-query" />
<button data-agentpulse-id="search-search">Search</button>
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Cursor goes to wrong element | Key mismatch | Check normalized key matches attribute |
| No animation on action | Missing attribute | Add data-agentpulse-id to element |
| Animation on wrong form field | Duplicate attributes | Make each attribute unique per component |
| Third-party input not found | Nested DOM structure | Use CSS selector config |
Debug Targeting
Open browser console and look for resolver logs:
[TargetResolver] Found element for contact-form.name using selector: ...
[TargetResolver] Auto-discovered element for contact-form.email
[TargetResolver] No element found for contact-form.phone
Process
- Add VisualOverlay - Import and add inside AgentPulseProvider
- Identify target elements - Which inputs/buttons need animations?
- Add data attributes - Use
data-agentpulse-id="componentId-normalizedKey" - Test with agent - Call
expose_setorexpose_calland verify animations - Configure fallbacks - Use CSS selectors for complex layouts
More Details
See references/TARGETING_PATTERNS.md for:
- Full fallback chain explanation
- Naming convention edge cases
- Third-party component strategies
- Debug techniques
Weekly Installs
5
Repository
dang-hai/agentpulseFirst Seen
Feb 2, 2026
Security Audits
Installed on
codex5
opencode4
gemini-cli4
github-copilot4
kimi-cli4
amp4