helper.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. (function() {
  2. const WS_URL = 'ws://' + window.location.host;
  3. let ws = null;
  4. let eventQueue = [];
  5. function connect() {
  6. ws = new WebSocket(WS_URL);
  7. ws.onopen = () => {
  8. // Send any queued events
  9. eventQueue.forEach(e => ws.send(JSON.stringify(e)));
  10. eventQueue = [];
  11. };
  12. ws.onmessage = (msg) => {
  13. const data = JSON.parse(msg.data);
  14. if (data.type === 'reload') {
  15. window.location.reload();
  16. }
  17. };
  18. ws.onclose = () => {
  19. // Reconnect after 1 second
  20. setTimeout(connect, 1000);
  21. };
  22. }
  23. function send(event) {
  24. event.timestamp = Date.now();
  25. if (ws && ws.readyState === WebSocket.OPEN) {
  26. ws.send(JSON.stringify(event));
  27. } else {
  28. eventQueue.push(event);
  29. }
  30. }
  31. // Auto-capture clicks on interactive elements
  32. document.addEventListener('click', (e) => {
  33. const target = e.target.closest('button, a, [data-choice], [role="button"], input[type="submit"]');
  34. if (!target) return;
  35. // Don't capture regular link navigation
  36. if (target.tagName === 'A' && !target.dataset.choice) return;
  37. e.preventDefault();
  38. send({
  39. type: 'click',
  40. text: target.textContent.trim(),
  41. choice: target.dataset.choice || null,
  42. id: target.id || null,
  43. className: target.className || null
  44. });
  45. });
  46. // Auto-capture form submissions
  47. document.addEventListener('submit', (e) => {
  48. e.preventDefault();
  49. const form = e.target;
  50. const formData = new FormData(form);
  51. const data = {};
  52. formData.forEach((value, key) => { data[key] = value; });
  53. send({
  54. type: 'submit',
  55. formId: form.id || null,
  56. formName: form.name || null,
  57. data: data
  58. });
  59. });
  60. // Auto-capture input changes (debounced)
  61. let inputTimeout = null;
  62. document.addEventListener('input', (e) => {
  63. const target = e.target;
  64. if (!target.matches('input, textarea, select')) return;
  65. clearTimeout(inputTimeout);
  66. inputTimeout = setTimeout(() => {
  67. send({
  68. type: 'input',
  69. name: target.name || null,
  70. id: target.id || null,
  71. value: target.value,
  72. inputType: target.type || target.tagName.toLowerCase()
  73. });
  74. }, 500); // 500ms debounce
  75. });
  76. // Send to Claude - triggers server to exit and return all events
  77. function sendToClaude(feedback) {
  78. send({
  79. type: 'send-to-claude',
  80. feedback: feedback || null
  81. });
  82. // Show confirmation to user
  83. document.body.innerHTML = `
  84. <div style="display: flex; align-items: center; justify-content: center; height: 100vh; font-family: system-ui, sans-serif;">
  85. <div style="text-align: center; color: #666;">
  86. <h2 style="color: #333;">✓ Sent to Claude</h2>
  87. <p>Return to the terminal to see Claude's response.</p>
  88. </div>
  89. </div>
  90. `;
  91. }
  92. // Expose for explicit use if needed
  93. window.brainstorm = {
  94. send: send,
  95. choice: (value, metadata = {}) => send({ type: 'choice', value, ...metadata }),
  96. sendToClaude: sendToClaude
  97. };
  98. connect();
  99. })();