UNPKG

realtimecursor

Version:

Real-time collaboration system with cursor tracking and approval workflow

242 lines (205 loc) 7.73 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>RealtimeCursor HTML Test</title> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } h1 { text-align: center; } .editor-container { position: relative; height: 300px; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 20px; } #editor { width: 100%; height: 100%; padding: 16px; box-sizing: border-box; font-family: monospace; font-size: 14px; line-height: 1.5; border: none; resize: none; outline: none; } #cursors-container { position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; } .collaborators-panel { border: 1px solid #ddd; border-radius: 4px; padding: 16px; } .connection-status { display: flex; align-items: center; margin-bottom: 16px; } .status-indicator { width: 10px; height: 10px; border-radius: 50%; margin-right: 8px; } .connected { background-color: #10b981; } .disconnected { background-color: #ef4444; } </style> </head> <body> <h1>RealtimeCursor HTML Test</h1> <div class="connection-status"> <div id="status-indicator" class="status-indicator disconnected"></div> <div id="status-text">Disconnected</div> </div> <div class="editor-container"> <textarea id="editor" placeholder="Start typing...">Welcome to RealtimeCursor HTML Test! This is a simple test page to verify that the SDK works without React. Try opening this page in multiple browser windows to see real-time collaboration in action.</textarea> <div id="cursors-container"></div> </div> <div class="collaborators-panel"> <h3>Active Collaborators (<span id="collaborator-count">0</span>)</h3> <div id="collaborators-container"></div> </div> <script type="module"> // Import the SDK from the local build import { RealtimeCursor } from '../fixed-sdk/dist/index.esm.js'; // Initialize the cursor client const cursorClient = new RealtimeCursor({ apiUrl: 'http://localhost:3001', projectId: 'html-test', user: { id: `user-${Math.floor(Math.random() * 10000)}`, name: `User ${Math.floor(Math.random() * 10000)}`, color: getRandomColor() } }); // Connect to the real-time service cursorClient.connect(); // Set up event handlers cursorClient.onConnect = () => { console.log('Connected to real-time service'); document.getElementById('status-indicator').classList.remove('disconnected'); document.getElementById('status-indicator').classList.add('connected'); document.getElementById('status-text').textContent = 'Connected'; }; cursorClient.onDisconnect = () => { console.log('Disconnected from real-time service'); document.getElementById('status-indicator').classList.remove('connected'); document.getElementById('status-indicator').classList.add('disconnected'); document.getElementById('status-text').textContent = 'Disconnected'; }; cursorClient.onCursorsChange = (cursors) => { console.log('Cursors updated:', cursors); renderCursors(cursors); }; cursorClient.onCollaboratorsChange = (collaborators) => { console.log('Collaborators updated:', collaborators); renderCollaborators(collaborators); }; cursorClient.onContentUpdate = (data) => { console.log('Content updated:', data); document.getElementById('editor').value = data.content; }; // Update cursor position on mouse move document.querySelector('.editor-container').addEventListener('mousemove', (e) => { const rect = e.currentTarget.getBoundingClientRect(); cursorClient.updateCursor({ x: e.clientX, y: e.clientY, relativeX: e.clientX - rect.left, relativeY: e.clientY - rect.top }); }); // Update content when changed document.getElementById('editor').addEventListener('input', (e) => { cursorClient.updateContent(e.target.value); }); // Disconnect when the page is closed window.addEventListener('beforeunload', () => { cursorClient.disconnect(); }); // Helper functions function renderCursors(cursors) { const container = document.getElementById('cursors-container'); container.innerHTML = ''; Object.values(cursors).forEach(cursor => { const cursorElement = document.createElement('div'); cursorElement.style.position = 'absolute'; cursorElement.style.left = `${cursor.position.x || cursor.position.relativeX || 0}px`; cursorElement.style.top = `${cursor.position.y || cursor.position.relativeY || 0}px`; cursorElement.style.pointerEvents = 'none'; cursorElement.style.zIndex = '9999'; cursorElement.style.transition = 'transform 0.1s ease-out'; cursorElement.innerHTML = ` <svg width="24" height="24" viewBox="0 0 24 24" fill="none" style="transform: rotate(-45deg); filter: drop-shadow(0 1px 2px rgba(0,0,0,0.25))"> <path d="M1 1L11 11V19L7 21V13L1 1Z" fill="${cursor.user.color || '#3b82f6'}" stroke="white" stroke-width="1" /> </svg> <div style="position: absolute; left: 16px; top: 8px; background-color: ${cursor.user.color || '#3b82f6'}; color: white; padding: 2px 6px; border-radius: 4px; font-size: 12px; font-weight: bold; white-space: nowrap; box-shadow: 0 1px 2px rgba(0,0,0,0.25)"> ${cursor.user.name} </div> `; container.appendChild(cursorElement); }); } function renderCollaborators(collaborators) { const container = document.getElementById('collaborators-container'); const count = document.getElementById('collaborator-count'); count.textContent = collaborators.length; container.innerHTML = ''; if (collaborators.length === 0) { container.innerHTML = '<div style="color: #666; font-style: italic">No active collaborators</div>'; return; } collaborators.forEach(user => { const userElement = document.createElement('div'); userElement.style.display = 'flex'; userElement.style.alignItems = 'center'; userElement.style.marginBottom = '8px'; userElement.innerHTML = ` <div style="width: 24px; height: 24px; border-radius: 50%; background-color: ${user.color || '#3b82f6'}; margin-right: 8px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 12px"> ${user.name ? user.name.charAt(0).toUpperCase() : '?'} </div> <div> ${user.name} </div> `; container.appendChild(userElement); }); } function getRandomColor() { const colors = [ '#3b82f6', // blue '#ef4444', // red '#10b981', // green '#f59e0b', // yellow '#8b5cf6', // purple '#ec4899', // pink '#06b6d4', // cyan '#f97316', // orange ]; return colors[Math.floor(Math.random() * colors.length)]; } </script> </body> </html>