chat-widget.html
edited
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>Chat Widget</title>
7 <style>
8 * {
9 margin: 0;
10 padding: 0;
11 box-sizing: border-box;
12 }
13
14 body {
15 background: transparent;
16 font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
17 overflow: hidden;
18 }
19
20 #chat-container {
21 width: full;
22 height: fit-content;
23 display: flex;
24 flex-direction: column;
25 justify-content: flex-end;
26 background: rgba(0, 0, 0, 0.7);
27 padding: 4px;
28 font-size: 2rem;
29 }
30
31 .message {
32 padding: 2px;
33 }
34
35 .username {
36 font-weight: 600;
37 }
38
39 .message-text {
40 color: #ffffff;
41 word-wrap: break-word;
42 }
43
44 #status {
45 position: fixed;
46 top: 10px;
47 right: 10px;
48 padding: 8px 12px;
49 border-radius: 4px;
50 font-size: 16px;
51 font-weight: bold;
52 }
53
54 .connected {
55 display: none;
56 background: rgba(0, 255, 0, 0.2);
57 color: #00ff00;
58 border: 1px solid #00ff00;
59 }
60
61 .disconnected {
62 background: rgba(255, 0, 0, 0.2);
63 color: #ff0000;
64 border: 1px solid #ff0000;
65 }
66
67 .connecting {
68 background: rgba(255, 255, 0, 0.2);
69 color: #ffff00;
70 border: 1px solid #ffff00;
71 }
72 </style>
73 </head>
74 <body>
75 <div id="status" class="disconnected">Disconnected</div>
76 <div id="chat-container"></div>
77
78 <script>
79 const HANDLE = "juli.ee";
80 const WEBSOCKET_URL = "wss://stream.place/api/websocket/" + HANDLE;
81 const MAX_MESSAGES = 15;
82
83 let ws = null;
84 const chatContainer = document.getElementById("chat-container");
85 const statusElement = document.getElementById("status");
86 const messages = [];
87
88 function updateStatus(status) {
89 statusElement.className = status;
90 statusElement.textContent =
91 status.charAt(0).toUpperCase() + status.slice(1);
92 }
93
94 function addMessage(username, text, color) {
95 const messageDiv = document.createElement("div");
96 messageDiv.className = "message";
97 messageDiv.innerHTML = `
98<span class="username" style="color: rgb(${color.red}, ${color.green}, ${color.blue})">${escapeHtml(username)}</span><span class="message-text">: ${escapeHtml(text)}</span>
99 `;
100
101 chatContainer.appendChild(messageDiv);
102 messages.push(messageDiv);
103
104 // Remove old messages if exceeding max
105 if (messages.length > MAX_MESSAGES) {
106 const oldMessage = messages.shift();
107 oldMessage.remove();
108 }
109
110 // Auto-scroll
111 chatContainer.scrollTop = chatContainer.scrollHeight;
112 }
113
114 function escapeHtml(text) {
115 const div = document.createElement("div");
116 div.textContent = text;
117 return div.innerHTML;
118 }
119
120 function connect() {
121 updateStatus("connecting");
122
123 try {
124 ws = new WebSocket(WEBSOCKET_URL);
125
126 ws.onopen = () => {
127 console.log("WebSocket connected");
128 updateStatus("connected");
129 };
130
131 ws.onmessage = (event) => {
132 try {
133 const data = JSON.parse(event.data);
134
135 if (data.$type === "place.stream.chat.defs#messageView") {
136 addMessage(
137 data.author.handle,
138 data.record.text,
139 data.chatProfile.color,
140 );
141 }
142 } catch (e) {
143 console.error("Error parsing message:", e);
144 }
145 };
146
147 ws.onerror = (error) => {
148 console.error("WebSocket error:", error);
149 updateStatus("disconnected");
150 };
151
152 ws.onclose = () => {
153 console.log("WebSocket disconnected");
154 updateStatus("disconnected");
155
156 // Auto-reconnect after 3 seconds
157 setTimeout(connect, 3000);
158 };
159 } catch (e) {
160 console.error("Connection error:", e);
161 updateStatus("disconnected");
162 setTimeout(connect, 3000);
163 }
164 }
165
166 connect();
167 </script>
168 </body>
169</html>