@teneo-protocol/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/.dockerignore +14 -0
  2. package/.env.test.example +14 -0
  3. package/.eslintrc.json +26 -0
  4. package/.github/workflows/claude-code-review.yml +78 -0
  5. package/.github/workflows/claude-reviewer.yml +64 -0
  6. package/.github/workflows/publish-npm.yml +38 -0
  7. package/.github/workflows/push-to-main.yml +23 -0
  8. package/.node-version +1 -0
  9. package/.prettierrc +11 -0
  10. package/Dockerfile +25 -0
  11. package/LICENCE +661 -0
  12. package/README.md +709 -0
  13. package/dist/constants.d.ts +42 -0
  14. package/dist/constants.d.ts.map +1 -0
  15. package/dist/constants.js +45 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/core/websocket-client.d.ts +261 -0
  18. package/dist/core/websocket-client.d.ts.map +1 -0
  19. package/dist/core/websocket-client.js +875 -0
  20. package/dist/core/websocket-client.js.map +1 -0
  21. package/dist/formatters/response-formatter.d.ts +354 -0
  22. package/dist/formatters/response-formatter.d.ts.map +1 -0
  23. package/dist/formatters/response-formatter.js +575 -0
  24. package/dist/formatters/response-formatter.js.map +1 -0
  25. package/dist/handlers/message-handler-registry.d.ts +155 -0
  26. package/dist/handlers/message-handler-registry.d.ts.map +1 -0
  27. package/dist/handlers/message-handler-registry.js +216 -0
  28. package/dist/handlers/message-handler-registry.js.map +1 -0
  29. package/dist/handlers/message-handlers/agent-selected-handler.d.ts +112 -0
  30. package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -0
  31. package/dist/handlers/message-handlers/agent-selected-handler.js +40 -0
  32. package/dist/handlers/message-handlers/agent-selected-handler.js.map +1 -0
  33. package/dist/handlers/message-handlers/agents-list-handler.d.ts +14 -0
  34. package/dist/handlers/message-handlers/agents-list-handler.d.ts.map +1 -0
  35. package/dist/handlers/message-handlers/agents-list-handler.js +25 -0
  36. package/dist/handlers/message-handlers/agents-list-handler.js.map +1 -0
  37. package/dist/handlers/message-handlers/auth-error-handler.d.ts +71 -0
  38. package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -0
  39. package/dist/handlers/message-handlers/auth-error-handler.js +30 -0
  40. package/dist/handlers/message-handlers/auth-error-handler.js.map +1 -0
  41. package/dist/handlers/message-handlers/auth-message-handler.d.ts +18 -0
  42. package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -0
  43. package/dist/handlers/message-handlers/auth-message-handler.js +60 -0
  44. package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -0
  45. package/dist/handlers/message-handlers/auth-required-handler.d.ts +76 -0
  46. package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -0
  47. package/dist/handlers/message-handlers/auth-required-handler.js +23 -0
  48. package/dist/handlers/message-handlers/auth-required-handler.js.map +1 -0
  49. package/dist/handlers/message-handlers/auth-success-handler.d.ts +18 -0
  50. package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -0
  51. package/dist/handlers/message-handlers/auth-success-handler.js +51 -0
  52. package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -0
  53. package/dist/handlers/message-handlers/base-handler.d.ts +55 -0
  54. package/dist/handlers/message-handlers/base-handler.d.ts.map +1 -0
  55. package/dist/handlers/message-handlers/base-handler.js +83 -0
  56. package/dist/handlers/message-handlers/base-handler.js.map +1 -0
  57. package/dist/handlers/message-handlers/challenge-handler.d.ts +73 -0
  58. package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -0
  59. package/dist/handlers/message-handlers/challenge-handler.js +47 -0
  60. package/dist/handlers/message-handlers/challenge-handler.js.map +1 -0
  61. package/dist/handlers/message-handlers/error-message-handler.d.ts +76 -0
  62. package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -0
  63. package/dist/handlers/message-handlers/error-message-handler.js +29 -0
  64. package/dist/handlers/message-handlers/error-message-handler.js.map +1 -0
  65. package/dist/handlers/message-handlers/index.d.ts +28 -0
  66. package/dist/handlers/message-handlers/index.d.ts.map +1 -0
  67. package/dist/handlers/message-handlers/index.js +100 -0
  68. package/dist/handlers/message-handlers/index.js.map +1 -0
  69. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +122 -0
  70. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -0
  71. package/dist/handlers/message-handlers/list-rooms-response-handler.js +30 -0
  72. package/dist/handlers/message-handlers/list-rooms-response-handler.js.map +1 -0
  73. package/dist/handlers/message-handlers/ping-pong-handler.d.ts +104 -0
  74. package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -0
  75. package/dist/handlers/message-handlers/ping-pong-handler.js +36 -0
  76. package/dist/handlers/message-handlers/ping-pong-handler.js.map +1 -0
  77. package/dist/handlers/message-handlers/regular-message-handler.d.ts +56 -0
  78. package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -0
  79. package/dist/handlers/message-handlers/regular-message-handler.js +59 -0
  80. package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -0
  81. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +81 -0
  82. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -0
  83. package/dist/handlers/message-handlers/subscribe-response-handler.js +48 -0
  84. package/dist/handlers/message-handlers/subscribe-response-handler.js.map +1 -0
  85. package/dist/handlers/message-handlers/task-response-handler.d.ts +14 -0
  86. package/dist/handlers/message-handlers/task-response-handler.d.ts.map +1 -0
  87. package/dist/handlers/message-handlers/task-response-handler.js +44 -0
  88. package/dist/handlers/message-handlers/task-response-handler.js.map +1 -0
  89. package/dist/handlers/message-handlers/types.d.ts +51 -0
  90. package/dist/handlers/message-handlers/types.d.ts.map +1 -0
  91. package/dist/handlers/message-handlers/types.js +7 -0
  92. package/dist/handlers/message-handlers/types.js.map +1 -0
  93. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +81 -0
  94. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -0
  95. package/dist/handlers/message-handlers/unsubscribe-response-handler.js +48 -0
  96. package/dist/handlers/message-handlers/unsubscribe-response-handler.js.map +1 -0
  97. package/dist/handlers/webhook-handler.d.ts +202 -0
  98. package/dist/handlers/webhook-handler.d.ts.map +1 -0
  99. package/dist/handlers/webhook-handler.js +511 -0
  100. package/dist/handlers/webhook-handler.js.map +1 -0
  101. package/dist/index.d.ts +71 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +217 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/managers/agent-registry.d.ts +173 -0
  106. package/dist/managers/agent-registry.d.ts.map +1 -0
  107. package/dist/managers/agent-registry.js +310 -0
  108. package/dist/managers/agent-registry.js.map +1 -0
  109. package/dist/managers/connection-manager.d.ts +134 -0
  110. package/dist/managers/connection-manager.d.ts.map +1 -0
  111. package/dist/managers/connection-manager.js +176 -0
  112. package/dist/managers/connection-manager.js.map +1 -0
  113. package/dist/managers/index.d.ts +9 -0
  114. package/dist/managers/index.d.ts.map +1 -0
  115. package/dist/managers/index.js +16 -0
  116. package/dist/managers/index.js.map +1 -0
  117. package/dist/managers/message-router.d.ts +112 -0
  118. package/dist/managers/message-router.d.ts.map +1 -0
  119. package/dist/managers/message-router.js +260 -0
  120. package/dist/managers/message-router.js.map +1 -0
  121. package/dist/managers/room-manager.d.ts +165 -0
  122. package/dist/managers/room-manager.d.ts.map +1 -0
  123. package/dist/managers/room-manager.js +227 -0
  124. package/dist/managers/room-manager.js.map +1 -0
  125. package/dist/teneo-sdk.d.ts +703 -0
  126. package/dist/teneo-sdk.d.ts.map +1 -0
  127. package/dist/teneo-sdk.js +907 -0
  128. package/dist/teneo-sdk.js.map +1 -0
  129. package/dist/types/config.d.ts +1047 -0
  130. package/dist/types/config.d.ts.map +1 -0
  131. package/dist/types/config.js +720 -0
  132. package/dist/types/config.js.map +1 -0
  133. package/dist/types/error-codes.d.ts +29 -0
  134. package/dist/types/error-codes.d.ts.map +1 -0
  135. package/dist/types/error-codes.js +41 -0
  136. package/dist/types/error-codes.js.map +1 -0
  137. package/dist/types/events.d.ts +616 -0
  138. package/dist/types/events.d.ts.map +1 -0
  139. package/dist/types/events.js +261 -0
  140. package/dist/types/events.js.map +1 -0
  141. package/dist/types/health.d.ts +40 -0
  142. package/dist/types/health.d.ts.map +1 -0
  143. package/dist/types/health.js +6 -0
  144. package/dist/types/health.js.map +1 -0
  145. package/dist/types/index.d.ts +10 -0
  146. package/dist/types/index.d.ts.map +1 -0
  147. package/dist/types/index.js +123 -0
  148. package/dist/types/index.js.map +1 -0
  149. package/dist/types/messages.d.ts +3734 -0
  150. package/dist/types/messages.d.ts.map +1 -0
  151. package/dist/types/messages.js +482 -0
  152. package/dist/types/messages.js.map +1 -0
  153. package/dist/types/validation.d.ts +81 -0
  154. package/dist/types/validation.d.ts.map +1 -0
  155. package/dist/types/validation.js +115 -0
  156. package/dist/types/validation.js.map +1 -0
  157. package/dist/utils/bounded-queue.d.ts +127 -0
  158. package/dist/utils/bounded-queue.d.ts.map +1 -0
  159. package/dist/utils/bounded-queue.js +181 -0
  160. package/dist/utils/bounded-queue.js.map +1 -0
  161. package/dist/utils/circuit-breaker.d.ts +141 -0
  162. package/dist/utils/circuit-breaker.d.ts.map +1 -0
  163. package/dist/utils/circuit-breaker.js +215 -0
  164. package/dist/utils/circuit-breaker.js.map +1 -0
  165. package/dist/utils/deduplication-cache.d.ts +110 -0
  166. package/dist/utils/deduplication-cache.d.ts.map +1 -0
  167. package/dist/utils/deduplication-cache.js +177 -0
  168. package/dist/utils/deduplication-cache.js.map +1 -0
  169. package/dist/utils/event-waiter.d.ts +101 -0
  170. package/dist/utils/event-waiter.d.ts.map +1 -0
  171. package/dist/utils/event-waiter.js +118 -0
  172. package/dist/utils/event-waiter.js.map +1 -0
  173. package/dist/utils/index.d.ts +51 -0
  174. package/dist/utils/index.d.ts.map +1 -0
  175. package/dist/utils/index.js +72 -0
  176. package/dist/utils/index.js.map +1 -0
  177. package/dist/utils/logger.d.ts +22 -0
  178. package/dist/utils/logger.d.ts.map +1 -0
  179. package/dist/utils/logger.js +91 -0
  180. package/dist/utils/logger.js.map +1 -0
  181. package/dist/utils/rate-limiter.d.ts +122 -0
  182. package/dist/utils/rate-limiter.d.ts.map +1 -0
  183. package/dist/utils/rate-limiter.js +190 -0
  184. package/dist/utils/rate-limiter.js.map +1 -0
  185. package/dist/utils/retry-policy.d.ts +191 -0
  186. package/dist/utils/retry-policy.d.ts.map +1 -0
  187. package/dist/utils/retry-policy.js +225 -0
  188. package/dist/utils/retry-policy.js.map +1 -0
  189. package/dist/utils/secure-private-key.d.ts +113 -0
  190. package/dist/utils/secure-private-key.d.ts.map +1 -0
  191. package/dist/utils/secure-private-key.js +188 -0
  192. package/dist/utils/secure-private-key.js.map +1 -0
  193. package/dist/utils/signature-verifier.d.ts +143 -0
  194. package/dist/utils/signature-verifier.d.ts.map +1 -0
  195. package/dist/utils/signature-verifier.js +238 -0
  196. package/dist/utils/signature-verifier.js.map +1 -0
  197. package/dist/utils/ssrf-validator.d.ts +36 -0
  198. package/dist/utils/ssrf-validator.d.ts.map +1 -0
  199. package/dist/utils/ssrf-validator.js +195 -0
  200. package/dist/utils/ssrf-validator.js.map +1 -0
  201. package/examples/.env.example +17 -0
  202. package/examples/basic-usage.ts +211 -0
  203. package/examples/production-dashboard/.env.example +153 -0
  204. package/examples/production-dashboard/package.json +39 -0
  205. package/examples/production-dashboard/public/dashboard.html +642 -0
  206. package/examples/production-dashboard/server.ts +753 -0
  207. package/examples/webhook-integration.ts +239 -0
  208. package/examples/x-influencer-battle-redesign.html +1065 -0
  209. package/examples/x-influencer-battle-server.ts +217 -0
  210. package/examples/x-influencer-battle.html +787 -0
  211. package/package.json +65 -0
  212. package/src/constants.ts +43 -0
  213. package/src/core/websocket-client.test.ts +512 -0
  214. package/src/core/websocket-client.ts +1056 -0
  215. package/src/formatters/response-formatter.test.ts +571 -0
  216. package/src/formatters/response-formatter.ts +677 -0
  217. package/src/handlers/message-handler-registry.ts +239 -0
  218. package/src/handlers/message-handlers/agent-selected-handler.ts +40 -0
  219. package/src/handlers/message-handlers/agents-list-handler.ts +26 -0
  220. package/src/handlers/message-handlers/auth-error-handler.ts +31 -0
  221. package/src/handlers/message-handlers/auth-message-handler.ts +66 -0
  222. package/src/handlers/message-handlers/auth-required-handler.ts +23 -0
  223. package/src/handlers/message-handlers/auth-success-handler.ts +57 -0
  224. package/src/handlers/message-handlers/base-handler.ts +101 -0
  225. package/src/handlers/message-handlers/challenge-handler.ts +57 -0
  226. package/src/handlers/message-handlers/error-message-handler.ts +27 -0
  227. package/src/handlers/message-handlers/index.ts +77 -0
  228. package/src/handlers/message-handlers/list-rooms-response-handler.ts +28 -0
  229. package/src/handlers/message-handlers/ping-pong-handler.ts +30 -0
  230. package/src/handlers/message-handlers/regular-message-handler.ts +65 -0
  231. package/src/handlers/message-handlers/subscribe-response-handler.ts +47 -0
  232. package/src/handlers/message-handlers/task-response-handler.ts +45 -0
  233. package/src/handlers/message-handlers/types.ts +77 -0
  234. package/src/handlers/message-handlers/unsubscribe-response-handler.ts +47 -0
  235. package/src/handlers/webhook-handler.test.ts +789 -0
  236. package/src/handlers/webhook-handler.ts +576 -0
  237. package/src/index.ts +269 -0
  238. package/src/managers/agent-registry.test.ts +466 -0
  239. package/src/managers/agent-registry.ts +347 -0
  240. package/src/managers/connection-manager.ts +195 -0
  241. package/src/managers/index.ts +9 -0
  242. package/src/managers/message-router.ts +349 -0
  243. package/src/managers/room-manager.ts +248 -0
  244. package/src/teneo-sdk.ts +1022 -0
  245. package/src/types/config.test.ts +325 -0
  246. package/src/types/config.ts +799 -0
  247. package/src/types/error-codes.ts +44 -0
  248. package/src/types/events.test.ts +302 -0
  249. package/src/types/events.ts +382 -0
  250. package/src/types/health.ts +46 -0
  251. package/src/types/index.ts +199 -0
  252. package/src/types/messages.test.ts +660 -0
  253. package/src/types/messages.ts +570 -0
  254. package/src/types/validation.ts +123 -0
  255. package/src/utils/bounded-queue.test.ts +356 -0
  256. package/src/utils/bounded-queue.ts +205 -0
  257. package/src/utils/circuit-breaker.test.ts +394 -0
  258. package/src/utils/circuit-breaker.ts +262 -0
  259. package/src/utils/deduplication-cache.test.ts +380 -0
  260. package/src/utils/deduplication-cache.ts +198 -0
  261. package/src/utils/event-waiter.test.ts +381 -0
  262. package/src/utils/event-waiter.ts +172 -0
  263. package/src/utils/index.ts +74 -0
  264. package/src/utils/logger.ts +87 -0
  265. package/src/utils/rate-limiter.test.ts +341 -0
  266. package/src/utils/rate-limiter.ts +211 -0
  267. package/src/utils/retry-policy.test.ts +558 -0
  268. package/src/utils/retry-policy.ts +272 -0
  269. package/src/utils/secure-private-key.test.ts +356 -0
  270. package/src/utils/secure-private-key.ts +205 -0
  271. package/src/utils/signature-verifier.test.ts +464 -0
  272. package/src/utils/signature-verifier.ts +298 -0
  273. package/src/utils/ssrf-validator.test.ts +372 -0
  274. package/src/utils/ssrf-validator.ts +224 -0
  275. package/tests/integration/real-server.test.ts +740 -0
  276. package/tests/integration/websocket.test.ts +381 -0
  277. package/tests/integration-setup.ts +16 -0
  278. package/tests/setup.ts +34 -0
  279. package/tsconfig.json +32 -0
  280. package/vitest.config.ts +42 -0
  281. package/vitest.integration.config.ts +23 -0
@@ -0,0 +1,1065 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>X Influencer Battle - AI Neural Interface</title>
7
+ <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
9
+
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
18
+ background: #0a0a0f;
19
+ min-height: 100vh;
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ padding: 20px;
24
+ overflow-x: hidden;
25
+ position: relative;
26
+ color: #e0e0e0;
27
+ letter-spacing: 0.3px;
28
+ }
29
+
30
+ /* Animated neural network background */
31
+ body::before {
32
+ content: '';
33
+ position: fixed;
34
+ top: 0;
35
+ left: 0;
36
+ width: 100%;
37
+ height: 100%;
38
+ background:
39
+ radial-gradient(circle at 20% 30%, rgba(138, 43, 226, 0.15) 0%, transparent 50%),
40
+ radial-gradient(circle at 80% 70%, rgba(0, 255, 255, 0.1) 0%, transparent 50%),
41
+ radial-gradient(circle at 50% 50%, rgba(255, 0, 255, 0.08) 0%, transparent 50%);
42
+ animation: neuralPulse 15s ease-in-out infinite;
43
+ pointer-events: none;
44
+ z-index: 0;
45
+ }
46
+
47
+ @keyframes neuralPulse {
48
+ 0%, 100% {
49
+ opacity: 1;
50
+ transform: scale(1) rotate(0deg);
51
+ }
52
+ 50% {
53
+ opacity: 0.8;
54
+ transform: scale(1.1) rotate(5deg);
55
+ }
56
+ }
57
+
58
+ /* Floating particles */
59
+ body::after {
60
+ content: '';
61
+ position: fixed;
62
+ top: 0;
63
+ left: 0;
64
+ width: 100%;
65
+ height: 100%;
66
+ background-image:
67
+ radial-gradient(2px 2px at 20% 30%, rgba(0, 255, 255, 0.4), transparent),
68
+ radial-gradient(2px 2px at 60% 70%, rgba(138, 43, 226, 0.4), transparent),
69
+ radial-gradient(1px 1px at 50% 50%, rgba(255, 0, 255, 0.3), transparent),
70
+ radial-gradient(1px 1px at 80% 10%, rgba(0, 255, 255, 0.3), transparent),
71
+ radial-gradient(1px 1px at 30% 80%, rgba(138, 43, 226, 0.2), transparent);
72
+ background-size: 300px 300px, 400px 400px, 200px 200px, 350px 350px, 280px 280px;
73
+ background-position: 0 0, 40px 60px, 80px 20px, 120px 100px, 160px 140px;
74
+ animation: particleFloat 25s linear infinite;
75
+ pointer-events: none;
76
+ opacity: 0.5;
77
+ z-index: 0;
78
+ }
79
+
80
+ @keyframes particleFloat {
81
+ 0% {
82
+ transform: translateY(0) translateX(0);
83
+ }
84
+ 100% {
85
+ transform: translateY(-120px) translateX(80px);
86
+ }
87
+ }
88
+
89
+ .container {
90
+ max-width: 1200px;
91
+ width: 100%;
92
+ position: relative;
93
+ z-index: 1;
94
+ }
95
+
96
+ .header {
97
+ text-align: center;
98
+ margin-bottom: 60px;
99
+ animation: fadeInDown 1s ease;
100
+ }
101
+
102
+ .header h1 {
103
+ font-size: 3.8rem;
104
+ font-weight: 800;
105
+ margin-bottom: 15px;
106
+ background: linear-gradient(135deg, #00ffff 0%, #8a2be2 50%, #ff00ff 100%);
107
+ -webkit-background-clip: text;
108
+ -webkit-text-fill-color: transparent;
109
+ background-clip: text;
110
+ text-shadow: 0 0 40px rgba(0, 255, 255, 0.3);
111
+ letter-spacing: -2px;
112
+ position: relative;
113
+ }
114
+
115
+ .header h1::after {
116
+ content: '';
117
+ position: absolute;
118
+ bottom: -10px;
119
+ left: 50%;
120
+ transform: translateX(-50%);
121
+ width: 100px;
122
+ height: 3px;
123
+ background: linear-gradient(90deg, transparent, #00ffff, transparent);
124
+ box-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
125
+ }
126
+
127
+ .header .subtitle {
128
+ color: rgba(255, 255, 255, 0.6);
129
+ font-size: 1.1rem;
130
+ font-weight: 400;
131
+ margin-top: 20px;
132
+ text-transform: uppercase;
133
+ letter-spacing: 3px;
134
+ }
135
+
136
+ /* Glassmorphism card */
137
+ .battle-card {
138
+ background: rgba(15, 15, 25, 0.7);
139
+ backdrop-filter: blur(20px);
140
+ border: 1px solid rgba(255, 255, 255, 0.1);
141
+ border-radius: 32px;
142
+ padding: 50px;
143
+ box-shadow:
144
+ 0 8px 32px rgba(0, 0, 0, 0.4),
145
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
146
+ 0 0 0 1px rgba(138, 43, 226, 0.1);
147
+ animation: fadeInUp 1s ease;
148
+ position: relative;
149
+ overflow: hidden;
150
+ }
151
+
152
+ .battle-card::before {
153
+ content: '';
154
+ position: absolute;
155
+ top: 0;
156
+ left: -100%;
157
+ width: 100%;
158
+ height: 100%;
159
+ background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.1), transparent);
160
+ animation: shimmer 3s infinite;
161
+ }
162
+
163
+ @keyframes shimmer {
164
+ 100% {
165
+ left: 100%;
166
+ }
167
+ }
168
+
169
+ .input-section {
170
+ display: grid;
171
+ grid-template-columns: 1fr auto 1fr;
172
+ gap: 40px;
173
+ align-items: center;
174
+ margin-bottom: 50px;
175
+ }
176
+
177
+ .user-input {
178
+ position: relative;
179
+ }
180
+
181
+ .user-input label {
182
+ display: block;
183
+ font-size: 0.75rem;
184
+ font-weight: 600;
185
+ color: #00ffff;
186
+ margin-bottom: 12px;
187
+ text-transform: uppercase;
188
+ letter-spacing: 2px;
189
+ text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
190
+ }
191
+
192
+ .user-input input {
193
+ width: 100%;
194
+ padding: 18px 24px;
195
+ background: rgba(10, 10, 15, 0.8);
196
+ border: 1px solid rgba(0, 255, 255, 0.3);
197
+ border-radius: 16px;
198
+ font-size: 1.1rem;
199
+ color: #fff;
200
+ transition: all 0.4s ease;
201
+ font-family: 'Inter', sans-serif;
202
+ letter-spacing: 0.5px;
203
+ box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.3);
204
+ }
205
+
206
+ .user-input input:focus {
207
+ outline: none;
208
+ border-color: #00ffff;
209
+ box-shadow:
210
+ 0 0 0 4px rgba(0, 255, 255, 0.1),
211
+ inset 0 2px 8px rgba(0, 0, 0, 0.3),
212
+ 0 0 20px rgba(0, 255, 255, 0.3);
213
+ background: rgba(10, 10, 15, 0.95);
214
+ }
215
+
216
+ .user-input input::placeholder {
217
+ color: rgba(255, 255, 255, 0.3);
218
+ }
219
+
220
+ .vs-badge {
221
+ width: 70px;
222
+ height: 70px;
223
+ background: rgba(10, 10, 15, 0.9);
224
+ backdrop-filter: blur(10px);
225
+ border: 2px solid rgba(138, 43, 226, 0.5);
226
+ border-radius: 50%;
227
+ display: flex;
228
+ align-items: center;
229
+ justify-content: center;
230
+ color: #8a2be2;
231
+ font-size: 1.6rem;
232
+ font-weight: 900;
233
+ box-shadow:
234
+ 0 0 30px rgba(138, 43, 226, 0.4),
235
+ inset 0 0 20px rgba(138, 43, 226, 0.2);
236
+ animation: vsRotate 10s linear infinite;
237
+ position: relative;
238
+ }
239
+
240
+ .vs-badge::before {
241
+ content: '';
242
+ position: absolute;
243
+ top: -5px;
244
+ left: -5px;
245
+ right: -5px;
246
+ bottom: -5px;
247
+ border-radius: 50%;
248
+ border: 1px solid rgba(138, 43, 226, 0.3);
249
+ animation: pulse 2s ease-in-out infinite;
250
+ }
251
+
252
+ @keyframes vsRotate {
253
+ 0%, 100% {
254
+ transform: rotate(0deg);
255
+ }
256
+ 50% {
257
+ transform: rotate(180deg);
258
+ }
259
+ }
260
+
261
+ .battle-btn {
262
+ width: 100%;
263
+ padding: 20px;
264
+ background: linear-gradient(135deg, #8a2be2 0%, #00ffff 100%);
265
+ color: #fff;
266
+ border: none;
267
+ border-radius: 16px;
268
+ font-size: 1.1rem;
269
+ font-weight: 700;
270
+ cursor: pointer;
271
+ transition: all 0.4s ease;
272
+ text-transform: uppercase;
273
+ letter-spacing: 2px;
274
+ box-shadow:
275
+ 0 8px 24px rgba(138, 43, 226, 0.4),
276
+ 0 0 20px rgba(0, 255, 255, 0.2);
277
+ position: relative;
278
+ overflow: hidden;
279
+ }
280
+
281
+ .battle-btn::before {
282
+ content: '';
283
+ position: absolute;
284
+ top: 0;
285
+ left: -100%;
286
+ width: 100%;
287
+ height: 100%;
288
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
289
+ transition: left 0.5s;
290
+ }
291
+
292
+ .battle-btn:hover:not(:disabled)::before {
293
+ left: 100%;
294
+ }
295
+
296
+ .battle-btn:hover:not(:disabled) {
297
+ transform: translateY(-2px);
298
+ box-shadow:
299
+ 0 12px 32px rgba(138, 43, 226, 0.5),
300
+ 0 0 40px rgba(0, 255, 255, 0.4);
301
+ }
302
+
303
+ .battle-btn:active:not(:disabled) {
304
+ transform: translateY(0);
305
+ }
306
+
307
+ .battle-btn:disabled {
308
+ opacity: 0.5;
309
+ cursor: not-allowed;
310
+ filter: grayscale(0.5);
311
+ }
312
+
313
+ .status {
314
+ text-align: center;
315
+ margin: 40px 0;
316
+ min-height: 40px;
317
+ }
318
+
319
+ .status-message {
320
+ display: inline-flex;
321
+ align-items: center;
322
+ gap: 12px;
323
+ padding: 14px 28px;
324
+ background: rgba(10, 10, 15, 0.8);
325
+ backdrop-filter: blur(10px);
326
+ border: 1px solid rgba(0, 255, 255, 0.3);
327
+ border-radius: 24px;
328
+ color: #00ffff;
329
+ font-weight: 600;
330
+ font-size: 0.95rem;
331
+ letter-spacing: 0.5px;
332
+ box-shadow: 0 0 30px rgba(0, 255, 255, 0.2);
333
+ animation: statusPulse 2s ease-in-out infinite;
334
+ }
335
+
336
+ @keyframes statusPulse {
337
+ 0%, 100% {
338
+ box-shadow: 0 0 30px rgba(0, 255, 255, 0.2);
339
+ }
340
+ 50% {
341
+ box-shadow: 0 0 40px rgba(0, 255, 255, 0.4);
342
+ }
343
+ }
344
+
345
+ .spinner {
346
+ width: 22px;
347
+ height: 22px;
348
+ border: 3px solid rgba(0, 255, 255, 0.2);
349
+ border-top-color: #00ffff;
350
+ border-radius: 50%;
351
+ animation: spin 1s linear infinite;
352
+ }
353
+
354
+ @keyframes spin {
355
+ to {
356
+ transform: rotate(360deg);
357
+ }
358
+ }
359
+
360
+ .results {
361
+ display: grid;
362
+ grid-template-columns: 1fr 1fr;
363
+ gap: 30px;
364
+ margin-top: 50px;
365
+ opacity: 0;
366
+ animation: fadeIn 0.8s ease forwards;
367
+ }
368
+
369
+ .result-card {
370
+ background: rgba(15, 15, 25, 0.6);
371
+ backdrop-filter: blur(15px);
372
+ border: 1px solid rgba(255, 255, 255, 0.1);
373
+ border-radius: 24px;
374
+ padding: 35px;
375
+ position: relative;
376
+ overflow: hidden;
377
+ transition: all 0.4s ease;
378
+ }
379
+
380
+ .result-card::before {
381
+ content: '';
382
+ position: absolute;
383
+ top: 0;
384
+ left: 0;
385
+ right: 0;
386
+ height: 4px;
387
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
388
+ }
389
+
390
+ .result-card:hover {
391
+ transform: translateY(-8px);
392
+ border-color: rgba(138, 43, 226, 0.5);
393
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
394
+ }
395
+
396
+ .result-card.winner {
397
+ background: rgba(138, 43, 226, 0.15);
398
+ border: 1px solid rgba(138, 43, 226, 0.5);
399
+ box-shadow:
400
+ 0 0 40px rgba(138, 43, 226, 0.3),
401
+ inset 0 0 40px rgba(138, 43, 226, 0.1);
402
+ }
403
+
404
+ .result-card.winner::before {
405
+ background: linear-gradient(90deg, transparent, #8a2be2, transparent);
406
+ height: 3px;
407
+ box-shadow: 0 0 20px rgba(138, 43, 226, 0.8);
408
+ }
409
+
410
+ .result-card.winner::after {
411
+ content: '👑';
412
+ position: absolute;
413
+ top: 20px;
414
+ right: 20px;
415
+ font-size: 3rem;
416
+ opacity: 0.2;
417
+ animation: crownFloat 3s ease-in-out infinite;
418
+ }
419
+
420
+ @keyframes crownFloat {
421
+ 0%, 100% {
422
+ transform: translateY(0) rotate(0deg);
423
+ }
424
+ 50% {
425
+ transform: translateY(-10px) rotate(5deg);
426
+ }
427
+ }
428
+
429
+ .result-username {
430
+ font-size: 2rem;
431
+ font-weight: 800;
432
+ margin-bottom: 25px;
433
+ color: #fff;
434
+ letter-spacing: -0.5px;
435
+ }
436
+
437
+ .result-card.winner .result-username {
438
+ background: linear-gradient(135deg, #00ffff 0%, #8a2be2 100%);
439
+ -webkit-background-clip: text;
440
+ -webkit-text-fill-color: transparent;
441
+ background-clip: text;
442
+ }
443
+
444
+ .result-stats {
445
+ display: grid;
446
+ gap: 15px;
447
+ margin-bottom: 20px;
448
+ }
449
+
450
+ .stat-item {
451
+ display: flex;
452
+ justify-content: space-between;
453
+ align-items: center;
454
+ padding: 14px 18px;
455
+ background: rgba(0, 0, 0, 0.3);
456
+ backdrop-filter: blur(10px);
457
+ border: 1px solid rgba(255, 255, 255, 0.05);
458
+ border-radius: 12px;
459
+ transition: all 0.3s ease;
460
+ }
461
+
462
+ .stat-item:hover {
463
+ background: rgba(0, 0, 0, 0.4);
464
+ border-color: rgba(0, 255, 255, 0.2);
465
+ transform: translateX(5px);
466
+ }
467
+
468
+ .stat-label {
469
+ font-weight: 500;
470
+ font-size: 0.85rem;
471
+ color: rgba(255, 255, 255, 0.6);
472
+ text-transform: uppercase;
473
+ letter-spacing: 1px;
474
+ }
475
+
476
+ .stat-value {
477
+ font-size: 1.3rem;
478
+ font-weight: 700;
479
+ color: #00ffff;
480
+ text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
481
+ }
482
+
483
+ .view-response-btn {
484
+ width: 100%;
485
+ margin-top: 15px;
486
+ padding: 12px 24px;
487
+ background: rgba(0, 0, 0, 0.4);
488
+ backdrop-filter: blur(10px);
489
+ border: 1px solid rgba(0, 255, 255, 0.3);
490
+ color: #00ffff;
491
+ border-radius: 12px;
492
+ cursor: pointer;
493
+ font-size: 0.9rem;
494
+ font-weight: 600;
495
+ font-family: 'Inter', sans-serif;
496
+ letter-spacing: 0.5px;
497
+ transition: all 0.3s ease;
498
+ }
499
+
500
+ .view-response-btn:hover {
501
+ background: rgba(0, 255, 255, 0.1);
502
+ border-color: #00ffff;
503
+ box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
504
+ transform: translateY(-2px);
505
+ }
506
+
507
+ .winner-badge {
508
+ display: inline-block;
509
+ padding: 10px 20px;
510
+ background: rgba(138, 43, 226, 0.2);
511
+ backdrop-filter: blur(10px);
512
+ border: 1px solid rgba(138, 43, 226, 0.5);
513
+ border-radius: 24px;
514
+ font-size: 0.85rem;
515
+ font-weight: 700;
516
+ margin-top: 15px;
517
+ color: #8a2be2;
518
+ text-transform: uppercase;
519
+ letter-spacing: 1px;
520
+ animation: badgeGlow 2s ease-in-out infinite;
521
+ }
522
+
523
+ @keyframes badgeGlow {
524
+ 0%, 100% {
525
+ box-shadow: 0 0 20px rgba(138, 43, 226, 0.4);
526
+ }
527
+ 50% {
528
+ box-shadow: 0 0 30px rgba(138, 43, 226, 0.6);
529
+ }
530
+ }
531
+
532
+ .error {
533
+ background: rgba(255, 0, 100, 0.1);
534
+ backdrop-filter: blur(10px);
535
+ color: #ff0066;
536
+ padding: 18px 24px;
537
+ border-radius: 16px;
538
+ margin: 20px 0;
539
+ border: 1px solid rgba(255, 0, 100, 0.3);
540
+ animation: shake 0.5s ease;
541
+ box-shadow: 0 0 20px rgba(255, 0, 100, 0.2);
542
+ }
543
+
544
+ /* Response Popup */
545
+ .response-popup {
546
+ position: fixed;
547
+ top: 0;
548
+ left: 0;
549
+ right: 0;
550
+ bottom: 0;
551
+ background: rgba(0, 0, 0, 0.85);
552
+ backdrop-filter: blur(10px);
553
+ display: none;
554
+ align-items: center;
555
+ justify-content: center;
556
+ z-index: 1000;
557
+ padding: 20px;
558
+ animation: fadeIn 0.3s ease;
559
+ }
560
+
561
+ .response-popup.active {
562
+ display: flex;
563
+ }
564
+
565
+ .popup-content {
566
+ background: rgba(15, 15, 25, 0.95);
567
+ backdrop-filter: blur(20px);
568
+ border: 1px solid rgba(0, 255, 255, 0.3);
569
+ border-radius: 24px;
570
+ max-width: 900px;
571
+ width: 100%;
572
+ max-height: 85vh;
573
+ display: flex;
574
+ flex-direction: column;
575
+ box-shadow:
576
+ 0 20px 60px rgba(0, 0, 0, 0.6),
577
+ 0 0 40px rgba(0, 255, 255, 0.2);
578
+ animation: slideUp 0.4s ease;
579
+ }
580
+
581
+ .popup-header {
582
+ padding: 25px 35px;
583
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
584
+ display: flex;
585
+ justify-content: space-between;
586
+ align-items: center;
587
+ }
588
+
589
+ .popup-header h3 {
590
+ margin: 0;
591
+ font-size: 1.4rem;
592
+ color: #00ffff;
593
+ display: flex;
594
+ align-items: center;
595
+ gap: 12px;
596
+ font-weight: 700;
597
+ letter-spacing: 0.5px;
598
+ text-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
599
+ }
600
+
601
+ .popup-close {
602
+ width: 45px;
603
+ height: 45px;
604
+ border-radius: 50%;
605
+ background: rgba(0, 0, 0, 0.4);
606
+ border: 1px solid rgba(255, 255, 255, 0.1);
607
+ font-size: 1.8rem;
608
+ cursor: pointer;
609
+ display: flex;
610
+ align-items: center;
611
+ justify-content: center;
612
+ transition: all 0.3s ease;
613
+ color: rgba(255, 255, 255, 0.6);
614
+ }
615
+
616
+ .popup-close:hover {
617
+ background: rgba(255, 0, 100, 0.2);
618
+ border-color: #ff0066;
619
+ color: #ff0066;
620
+ transform: rotate(90deg);
621
+ box-shadow: 0 0 20px rgba(255, 0, 100, 0.4);
622
+ }
623
+
624
+ .popup-body {
625
+ padding: 35px;
626
+ overflow-y: auto;
627
+ flex: 1;
628
+ }
629
+
630
+ .popup-body::-webkit-scrollbar {
631
+ width: 10px;
632
+ }
633
+
634
+ .popup-body::-webkit-scrollbar-track {
635
+ background: rgba(0, 0, 0, 0.2);
636
+ border-radius: 10px;
637
+ }
638
+
639
+ .popup-body::-webkit-scrollbar-thumb {
640
+ background: rgba(0, 255, 255, 0.3);
641
+ border-radius: 10px;
642
+ }
643
+
644
+ .popup-body::-webkit-scrollbar-thumb:hover {
645
+ background: rgba(0, 255, 255, 0.5);
646
+ }
647
+
648
+ .response-text {
649
+ font-family: 'Courier New', 'Monaco', monospace;
650
+ font-size: 0.9rem;
651
+ line-height: 1.7;
652
+ white-space: pre-wrap;
653
+ word-wrap: break-word;
654
+ background: rgba(0, 0, 0, 0.4);
655
+ padding: 25px;
656
+ border-radius: 16px;
657
+ border-left: 3px solid #00ffff;
658
+ color: rgba(255, 255, 255, 0.85);
659
+ box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.4);
660
+ }
661
+
662
+ .powered-by {
663
+ text-align: center;
664
+ margin-top: 40px;
665
+ color: rgba(255, 255, 255, 0.4);
666
+ font-size: 0.85rem;
667
+ letter-spacing: 1px;
668
+ animation: fadeIn 1.5s ease;
669
+ }
670
+
671
+ .powered-by a {
672
+ color: #00ffff;
673
+ text-decoration: none;
674
+ font-weight: 600;
675
+ transition: all 0.3s ease;
676
+ text-shadow: 0 0 10px rgba(0, 255, 255, 0.3);
677
+ }
678
+
679
+ .powered-by a:hover {
680
+ color: #8a2be2;
681
+ text-shadow: 0 0 20px rgba(138, 43, 226, 0.5);
682
+ }
683
+
684
+ /* Animations */
685
+ @keyframes fadeInDown {
686
+ from {
687
+ opacity: 0;
688
+ transform: translateY(-40px);
689
+ }
690
+ to {
691
+ opacity: 1;
692
+ transform: translateY(0);
693
+ }
694
+ }
695
+
696
+ @keyframes fadeInUp {
697
+ from {
698
+ opacity: 0;
699
+ transform: translateY(40px);
700
+ }
701
+ to {
702
+ opacity: 1;
703
+ transform: translateY(0);
704
+ }
705
+ }
706
+
707
+ @keyframes fadeIn {
708
+ to {
709
+ opacity: 1;
710
+ }
711
+ }
712
+
713
+ @keyframes slideUp {
714
+ from {
715
+ opacity: 0;
716
+ transform: translateY(50px) scale(0.95);
717
+ }
718
+ to {
719
+ opacity: 1;
720
+ transform: translateY(0) scale(1);
721
+ }
722
+ }
723
+
724
+ @keyframes shake {
725
+ 0%, 100% {
726
+ transform: translateX(0);
727
+ }
728
+ 25% {
729
+ transform: translateX(-10px);
730
+ }
731
+ 75% {
732
+ transform: translateX(10px);
733
+ }
734
+ }
735
+
736
+ @keyframes pulse {
737
+ 0%, 100% {
738
+ opacity: 1;
739
+ transform: scale(1);
740
+ }
741
+ 50% {
742
+ opacity: 0.7;
743
+ transform: scale(1.05);
744
+ }
745
+ }
746
+
747
+ @media (max-width: 768px) {
748
+ .header h1 {
749
+ font-size: 2.5rem;
750
+ }
751
+
752
+ .input-section {
753
+ grid-template-columns: 1fr;
754
+ gap: 25px;
755
+ }
756
+
757
+ .vs-badge {
758
+ order: -1;
759
+ margin: 0 auto;
760
+ }
761
+
762
+ .results {
763
+ grid-template-columns: 1fr;
764
+ }
765
+
766
+ .battle-card {
767
+ padding: 30px 25px;
768
+ }
769
+ }
770
+ </style>
771
+ </head>
772
+ <body>
773
+ <div class="container">
774
+ <div class="header">
775
+ <h1>⚡ X NEURAL BATTLE</h1>
776
+ <p class="subtitle">AI-Powered Influence Analysis</p>
777
+ </div>
778
+
779
+ <div class="battle-card">
780
+ <div class="input-section">
781
+ <div class="user-input">
782
+ <label for="user1">Challenger Alpha</label>
783
+ <input type="text" id="user1" placeholder="@username" autocomplete="off">
784
+ </div>
785
+
786
+ <div class="vs-badge">VS</div>
787
+
788
+ <div class="user-input">
789
+ <label for="user2">Challenger Beta</label>
790
+ <input type="text" id="user2" placeholder="@username" autocomplete="off">
791
+ </div>
792
+ </div>
793
+
794
+ <button class="battle-btn" id="battleBtn" onclick="startBattle()">Initialize Analysis</button>
795
+
796
+ <div class="status" id="status"></div>
797
+
798
+ <div id="errorContainer"></div>
799
+
800
+ <div id="results" style="display: none;"></div>
801
+ </div>
802
+
803
+ <div class="powered-by">
804
+ Neural Network Powered by <a href="https://teneo.pro" target="_blank">TENEO AI</a>
805
+ </div>
806
+ </div>
807
+
808
+ <!-- Response Popup -->
809
+ <div class="response-popup" id="responsePopup">
810
+ <div class="popup-content">
811
+ <div class="popup-header">
812
+ <h3 id="popupTitle">📊 Neural Response Data</h3>
813
+ <button class="popup-close" onclick="closePopup()">×</button>
814
+ </div>
815
+ <div class="popup-body">
816
+ <div class="response-text" id="popupResponseText"></div>
817
+ </div>
818
+ </div>
819
+ </div>
820
+
821
+ <script>
822
+ const WS_URL = 'ws://localhost:3000';
823
+
824
+ let ws = null;
825
+ let currentBattle = {
826
+ user1: null,
827
+ user2: null,
828
+ user1Data: null,
829
+ user2Data: null,
830
+ user1ResponseLength: 0,
831
+ user2ResponseLength: 0,
832
+ user1FullResponse: '',
833
+ user2FullResponse: ''
834
+ };
835
+
836
+ function showPopup(username, responseText) {
837
+ const popup = document.getElementById('responsePopup');
838
+ const title = document.getElementById('popupTitle');
839
+ const text = document.getElementById('popupResponseText');
840
+
841
+ title.textContent = `📊 @${username} - Neural Response Data`;
842
+ text.textContent = responseText;
843
+ popup.classList.add('active');
844
+ }
845
+
846
+ function closePopup() {
847
+ const popup = document.getElementById('responsePopup');
848
+ popup.classList.remove('active');
849
+ }
850
+
851
+ document.addEventListener('click', (e) => {
852
+ const popup = document.getElementById('responsePopup');
853
+ if (e.target === popup) {
854
+ closePopup();
855
+ }
856
+ });
857
+
858
+ document.addEventListener('keydown', (e) => {
859
+ if (e.key === 'Escape') {
860
+ closePopup();
861
+ }
862
+ });
863
+
864
+ function showStatus(message, showSpinner = false) {
865
+ const statusEl = document.getElementById('status');
866
+ statusEl.innerHTML = `
867
+ <div class="status-message">
868
+ ${showSpinner ? '<div class="spinner"></div>' : ''}
869
+ ${message}
870
+ </div>
871
+ `;
872
+ }
873
+
874
+ function hideStatus() {
875
+ document.getElementById('status').innerHTML = '';
876
+ }
877
+
878
+ function showError(message) {
879
+ const errorContainer = document.getElementById('errorContainer');
880
+ errorContainer.innerHTML = `<div class="error">⚠️ ${message}</div>`;
881
+ setTimeout(() => {
882
+ errorContainer.innerHTML = '';
883
+ }, 5000);
884
+ }
885
+
886
+ function normalizeUsername(username) {
887
+ return username.trim().replace(/^@/, '');
888
+ }
889
+
890
+ async function startBattle() {
891
+ const user1Input = document.getElementById('user1').value;
892
+ const user2Input = document.getElementById('user2').value;
893
+
894
+ if (!user1Input || !user2Input) {
895
+ showError('Both challengers required for neural analysis');
896
+ return;
897
+ }
898
+
899
+ const user1 = normalizeUsername(user1Input);
900
+ const user2 = normalizeUsername(user2Input);
901
+
902
+ if (user1 === user2) {
903
+ showError('Challengers must be unique entities');
904
+ return;
905
+ }
906
+
907
+ currentBattle = {
908
+ user1,
909
+ user2,
910
+ user1Data: null,
911
+ user2Data: null,
912
+ user1ResponseLength: 0,
913
+ user2ResponseLength: 0,
914
+ user1FullResponse: '',
915
+ user2FullResponse: ''
916
+ };
917
+
918
+ document.getElementById('results').style.display = 'none';
919
+ document.getElementById('battleBtn').disabled = true;
920
+
921
+ connectAndFetch();
922
+ }
923
+
924
+ function connectAndFetch() {
925
+ showStatus('Connecting to neural network...', true);
926
+
927
+ ws = new WebSocket(WS_URL);
928
+
929
+ ws.onopen = () => {
930
+ console.log('Connected to neural network');
931
+ fetchUser1Timeline();
932
+ };
933
+
934
+ ws.onmessage = (event) => {
935
+ const data = JSON.parse(event.data);
936
+ handleMessage(data);
937
+ };
938
+
939
+ ws.onerror = (error) => {
940
+ console.error('Neural connection error:', error);
941
+ showError('Neural network connection failed. Retry analysis.');
942
+ document.getElementById('battleBtn').disabled = false;
943
+ };
944
+
945
+ ws.onclose = () => {
946
+ console.log('Neural connection closed');
947
+ };
948
+ }
949
+
950
+ function fetchUser1Timeline() {
951
+ showStatus(`Analyzing @${currentBattle.user1} neural patterns...`, true);
952
+ ws.send(JSON.stringify({
953
+ action: 'fetch_timeline',
954
+ username: currentBattle.user1
955
+ }));
956
+ }
957
+
958
+ function fetchUser2Timeline() {
959
+ showStatus(`Analyzing @${currentBattle.user2} neural patterns...`, true);
960
+ ws.send(JSON.stringify({
961
+ action: 'fetch_timeline',
962
+ username: currentBattle.user2
963
+ }));
964
+ }
965
+
966
+ function handleMessage(data) {
967
+ console.log('Received:', data);
968
+
969
+ if (data.type === 'error') {
970
+ showError(data.message);
971
+ document.getElementById('battleBtn').disabled = false;
972
+ return;
973
+ }
974
+
975
+ if (data.type === 'timeline_response') {
976
+ const responseLength = data.content ? data.content.length : 0;
977
+ const fullResponse = data.content || 'No neural data received';
978
+
979
+ if (data.username === currentBattle.user1) {
980
+ currentBattle.user1Data = data;
981
+ currentBattle.user1ResponseLength = responseLength;
982
+ currentBattle.user1FullResponse = fullResponse;
983
+ fetchUser2Timeline();
984
+ } else if (data.username === currentBattle.user2) {
985
+ currentBattle.user2Data = data;
986
+ currentBattle.user2ResponseLength = responseLength;
987
+ currentBattle.user2FullResponse = fullResponse;
988
+ showResults();
989
+ }
990
+ }
991
+ }
992
+
993
+ function showResults() {
994
+ hideStatus();
995
+ document.getElementById('battleBtn').disabled = false;
996
+
997
+ const user1Length = currentBattle.user1ResponseLength;
998
+ const user2Length = currentBattle.user2ResponseLength;
999
+ const user1Wins = user1Length > user2Length;
1000
+
1001
+ const resultsHTML = `
1002
+ <div class="results">
1003
+ <div class="result-card ${user1Wins ? 'winner' : ''}">
1004
+ <div class="result-username">
1005
+ @${currentBattle.user1}
1006
+ </div>
1007
+ <div class="result-stats">
1008
+ <div class="stat-item">
1009
+ <span class="stat-label">Neural Score</span>
1010
+ <span class="stat-value">${user1Length.toLocaleString()}</span>
1011
+ </div>
1012
+ <div class="stat-item">
1013
+ <span class="stat-label">Data Size</span>
1014
+ <span class="stat-value">${(user1Length / 1024).toFixed(2)} KB</span>
1015
+ </div>
1016
+ </div>
1017
+ <button class="view-response-btn" onclick="showPopup('${currentBattle.user1}', currentBattle.user1FullResponse)">
1018
+ 📡 View Neural Data
1019
+ </button>
1020
+ ${user1Wins ? '<div class="winner-badge">🏆 Neural Winner</div>' : ''}
1021
+ </div>
1022
+
1023
+ <div class="result-card ${!user1Wins ? 'winner' : ''}">
1024
+ <div class="result-username">
1025
+ @${currentBattle.user2}
1026
+ </div>
1027
+ <div class="result-stats">
1028
+ <div class="stat-item">
1029
+ <span class="stat-label">Neural Score</span>
1030
+ <span class="stat-value">${user2Length.toLocaleString()}</span>
1031
+ </div>
1032
+ <div class="stat-item">
1033
+ <span class="stat-label">Data Size</span>
1034
+ <span class="stat-value">${(user2Length / 1024).toFixed(2)} KB</span>
1035
+ </div>
1036
+ </div>
1037
+ <button class="view-response-btn" onclick="showPopup('${currentBattle.user2}', currentBattle.user2FullResponse)">
1038
+ 📡 View Neural Data
1039
+ </button>
1040
+ ${!user1Wins ? '<div class="winner-badge">🏆 Neural Winner</div>' : ''}
1041
+ </div>
1042
+ </div>
1043
+ `;
1044
+
1045
+ const resultsContainer = document.getElementById('results');
1046
+ resultsContainer.innerHTML = resultsHTML;
1047
+ resultsContainer.style.display = 'block';
1048
+ }
1049
+
1050
+ ['user1', 'user2'].forEach(id => {
1051
+ document.getElementById(id).addEventListener('input', (e) => {
1052
+ if (e.target.value && !e.target.value.startsWith('@')) {
1053
+ e.target.value = '@' + e.target.value;
1054
+ }
1055
+ });
1056
+ });
1057
+
1058
+ document.addEventListener('keypress', (e) => {
1059
+ if (e.key === 'Enter' && !document.getElementById('battleBtn').disabled) {
1060
+ startBattle();
1061
+ }
1062
+ });
1063
+ </script>
1064
+ </body>
1065
+ </html>