--- description: Create automated UI demos with a visible "ghost cursor" that simulates user interaction. --- # UI Automation Demo Skill This skill provides a standard pattern for creating automated "self-driving" UI demos using a simulated cursor. This is useful for showcasing workflows without user interaction. ## 1. HTML Structure (The Cursor) Add this cursor element to the `` of your HTML file. It uses Tailwind CSS for positioning and transitions. ```html
``` **Key Attributes:** - `fixed`: Ensures it floats above everything relative to the viewport. - `pointer-events-none`: Crucial so it doesn't intercept actual clicks. - `z-[9999]`: Must be the highest layer. - `transition-transform duration-700`: Controls the smoothness of the movement. ## 2. JavaScript Module Pattern Implement a dedicated module (e.g., `DemoAutomationModule`) to control the cursor. ### Core Helpers ```javascript const DemoAutomationModule = { // Helper: Promisified Wait wait: ms => new Promise(r => setTimeout(r, ms)), // Helper: Move Cursor async moveCursorTo(selector) { const cursor = document.getElementById('demo-cursor'); const el = document.querySelector(selector); if (!el) { console.warn(`Target not found: ${selector}`); return; } if (!cursor) { console.warn("Cursor not found"); return; } // Ensure Visibility cursor.classList.remove('opacity-0'); cursor.style.opacity = '1'; // Calculate Position const rect = el.getBoundingClientRect(); // Target center const x = rect.left + (rect.width / 2); const y = rect.top + (rect.height / 2); // Move cursor.style.transform = `translate(${x}px, ${y}px)`; // Wait for transition to finish (match CSS duration + buffer) await this.wait(800); }, // Helper: Simulate Click (Visual + Logic) async clickCursor() { const cursor = document.getElementById('demo-cursor'); const icon = cursor?.querySelector('i'); const effect = document.getElementById('click-effect'); // Visual Down if (icon) icon.style.transform = "scale(0.8) rotate(-15deg)"; if (effect) { effect.classList.remove('hidden'); setTimeout(() => effect.classList.add('hidden'), 300); } await this.wait(150); // Visual Up if (icon) icon.style.transform = "scale(1) rotate(-15deg)"; await this.wait(150); }, // Helper: Initialize Sequence reliably init: function() { // Run immediately if DOM is ready, otherwise wait if (document.readyState === 'complete' || document.readyState === 'interactive') { this.runSequence(); } else { window.addEventListener('DOMContentLoaded', () => this.runSequence()); } }, // THE SEQUENCE async runSequence() { console.log("Starting Demo Sequence..."); await this.wait(1000); // Initial delay // Example Step 1: Click a button await this.moveCursorTo('#my-button-id'); await this.clickCursor(); document.getElementById('my-button-id')?.click(); // Trigger actual event await this.wait(1000); // Wait for reaction // Loop using reload (Optional) // window.location.reload(); } }; ``` ## 3. Usage Rules 1. **Unique IDs**: Ensure every element interactable by the demo has a unique `id` for reliable selection. 2. **Visual vs Logical**: The `clickCursor()` function is purely *visual*. You MUST manually trigger the actual click logic (e.g., `.click()`) or function call immediately after it. 3. **Timing**: Always add `await this.wait(ms)` after actions to allow UI animations (modals opening, panels sliding) to complete before moving to the next step. 4. **Robust Initialization**: Use the `readyState` check in `init` to avoid race conditions where `load` listeners might be missed.