(html)
const express = require("express");
const http = require("http");
const { Server } = require("socket.io");
const app = express();
const server = http.createServer(app);
const io = new Server(server);
app.use(express.static("public"));
io.on("connection", (socket) => {
// announce join
socket.on("join", (name) => {
socket.data.name = name || "Anonymous";
socket.broadcast.emit("system", `${socket.data.name} joined the chat`);
});
// receive chat message and broadcast
socket.on("chat", (msg) => {
const payload = {
name: socket.data.name || "Anonymous",
text: msg,
time: new Date().toISOString()
};
io.emit("chat", payload);
});
socket.on("disconnect", () => {
if (socket.data.name) {
socket.broadcast.emit("system", `${socket.data.name} left the chat`);
}
});
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => console.log(`Server listening on http://localhost:${PORT}`));
*{box-sizing:border-box}
body{font-family:Arial,Helvetica,sans-serif;margin:0;background:#f4f7fb;height:100vh}
.container{display:flex;height:100%}
.sidebar{width:220px;padding:16px;background:#2f3640;color:#fff}
.sidebar h2{margin-top:0}
.sidebar input{width:100%;padding:8px;margin-top:8px;border-radius:4px;border:0}
.sidebar button{width:100%;padding:8px;margin-top:8px;border:none;border-radius:4px;background:#00a8ff;color:#fff;cursor:pointer}
.chat{flex:1;display:flex;flex-direction:column}
.messages{flex:1;padding:16px;overflow:auto}
.msg{background:#fff;padding:8px 12px;border-radius:8px;margin-bottom:8px;max-width:75%}
.msg.me{background:#c8e6c9;margin-left:auto}
.sys{color:#666;font-style:italic;margin-bottom:8px}
.composer{display:flex;padding:12px;background:#eee}
.composer input{flex:1;padding:10px;border-radius:6px;border:1px solid #ccc}
.composer button{margin-left:8px;padding:10px 16px;border-radius:6px;border:none;background:#2ecc71;color:#fff;cursor:pointer}
const express = require("express");
const http = require("http");
const { Server } = require("socket.io");
const app = express();
const server = http.createServer(app);
const io = new Server(server, { cors: { origin: "*" } });
app.use(express.static("public"));
// In-memory store: { roomName: { users: {socketId: name}, history: [msg,...] } }
const ROOMS = {};
const MAX_HISTORY = 100;
function ensureRoom(room) {
if (!ROOMS[room]) ROOMS[room] = { users: {}, history: [] };
}
io.on("connection", (socket) => {
socket.data.name = "Anonymous";
socket.on("join", ({ name, room }) => {
name = (name || "Anonymous").slice(0, 24);
room = (room || "lobby").slice(0, 32);
socket.data.name = name;
socket.data.room = room;
socket.join(room);
ensureRoom(room);
ROOMS[room].users[socket.id] = name;
// Send room info + history to joining client
socket.emit("room_joined", { room, history: ROOMS[room].history, users: Object.values(ROOMS[room].users) });
// Notify others in room
socket.to(room).emit("system", `${name} joined ${room}`);
io.in(room).emit("users", Object.values(ROOMS[room].users));
});
socket.on("chat", (text) => {
const room = socket.data.room || "lobby";
const payload = {
id: Date.now().toString(36) + "-" + Math.random().toString(36).slice(2,8),
name: socket.data.name || "Anonymous",
const socket = io();
const form = document.getElementById("form");
const input = document.getElementById("input");
const messages = document.getElementById("messages");
const joinBtn = document.getElementById("joinBtn");
const nameInput = document.getElementById("nameInput");
const roomInput = document.getElementById("roomInput");
const usersEl = document.getElementById("users");
const pmSelect = document.getElementById("pmSelect");
const pmInput = document.getElementById("pmInput");
const pmSend = document.getElementById("pmSend");
const roomNameEl = document.getElementById("roomName");
const typingEl = document.getElementById("typing");
let myName = "";
let joined = false;
let typingTimer = null;
function appendMessage(html, cls) {
const el = document.createElement("div");
el.className = cls || "msg";
el.innerHTML = html;
messages.appendChild(el);
messages.scrollTop = messages.scrollHeight;
}
function escapeHtml(s = "") {
return s.replace(/[&<>"']/g, (c) => ({ "&":"&","<":"<",">":">",'"':""","'":"'" })[c]);
}
joinBtn.addEventListener("click", () => {
const name = (nameInput.value || "Anonymous").trim();
const room = (roomInput.value || "lobby").trim();
myName = name;
socket.emit("join", { name, room });
joined = true;
nameInput.disabled = true;
roomInput.disabled = true;
joinBtn.disabled = true;
roomNameEl.textContent = `Room: ${room}`;
});
form.addEventListener("submit", (e) => {
.users{max-height:180px;overflow:auto;padding:8px;background:#fff;border-radius:6px}
.user{padding:6px;border-bottom:1px solid #eee}
.room-name{padding:8px 12px;background:#fff;border-bottom:1px solid #ddd}
.typing{padding:6px 12px;color:#666;font-style:italic}
.msg.pm{background:#ffe0b2}
.time{font-size:0.8em;color:#888;margin-left:8px}