diff --git a/client-web/index.html b/client-web/index.html index 0d36af5..113f6e5 100644 --- a/client-web/index.html +++ b/client-web/index.html @@ -110,7 +110,7 @@
- + diff --git a/client-web/index.js b/client-web/index.js index b7f7602..0c2de7a 100644 --- a/client-web/index.js +++ b/client-web/index.js @@ -24,10 +24,17 @@ document.addEventListener('DOMContentLoaded', function() { chatHeader.insertBefore(menuToggle, chatHeader.firstChild); } } + + document.getElementById('messageInput').addEventListener('input', function () { + this.style.height = 'auto'; + this.style.height = (this.scrollHeight) + 'px'; + }); + // Add event listeners - document.getElementById('messageInput').addEventListener('keypress', (event) => { - if (event.key === 'Enter') { + document.getElementById('messageInput').addEventListener('keydown', function(event) { + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); sendMessage(); } }); @@ -264,6 +271,218 @@ function resetLoginButton() { } } +function markdownToHtmlDiv(markdown) { + // Emoji mapping + const emojiMap = { + // 😀 Faces + smile: '😄', + happy: '😊', + grin: '😁', + laugh: '😂', + joy: 'ðŸĪĢ', + wink: '😉', + blush: '☚ïļ', + cool: '😎', + smirk: '😏', + thinking: 'ðŸĪ”', + neutral: '😐', + expressionless: '😑', + sleepy: 'ðŸ˜ī', + dizzy: 'ðŸ˜ĩ', + surprised: 'ðŸ˜ē', + scream: 'ðŸ˜ą', + cry: 'ðŸ˜Ē', + sob: '😭', + angry: '😠', + rage: 'ðŸ˜Ą', + yum: '😋', + relieved: '😌', + confused: '😕', + nerd: 'ðŸĪ“', + zany: 'ðŸĪŠ', + shush: 'ðŸĪŦ', + hush: 'ðŸĪ', + + // âĪïļ Emotions + heart: 'âĪïļ', + heartbroken: '💔', + love: '😍', + kiss: '😘', + hug: 'ðŸĪ—', + clap: '👏', + pray: '🙏', + ok: '👌', + fingerscrossed: 'ðŸĪž', + thumbsup: '👍', + thumbsdown: '👎', + eyes: '👀', + + // ðŸ”Ĩ Reactions + fire: 'ðŸ”Ĩ', + 100: 'ðŸ’Ŋ', + poop: 'ðŸ’Đ', + poo: 'ðŸ’Đ', + skull: '💀', + explosion: 'ðŸ’Ĩ', + mindblown: 'ðŸĪŊ', + party: 'ðŸĨģ', + tada: '🎉', + balloon: '🎈', + + // ðŸķ Animals + chicken: '🐔', + dog: 'ðŸķ', + cat: 'ðŸą', + fox: 'ðŸĶŠ', + panda: '🐞', + pig: '🐷', + cow: 'ðŸŪ', + rabbit: '🐰', + bear: 'ðŸŧ', + unicorn: 'ðŸĶ„', + monkey: '🐒', + dragon: '🐉', + snake: '🐍', + + // 🍔 Food & Drink + pizza: '🍕', + burger: '🍔', + fries: '🍟', + hotdog: '🌭', + taco: 'ðŸŒŪ', + ramen: '🍜', + sushi: 'ðŸĢ', + icecream: 'ðŸĻ', + cake: '🎂', + coffee: '☕', + tea: 'ðŸĩ', + beer: '🍚', + + // 🌍 Nature & Weather + sun: '☀ïļ', + moon: '🌙', + star: '⭐', + cloud: '☁ïļ', + rain: '🌧ïļ', + snow: '❄ïļ', + lightning: '⚡', + rainbow: '🌈', + tree: 'ðŸŒģ', + flower: 'ðŸŒļ', + + // âš― Activities + soccer: 'âš―', + basketball: '🏀', + football: '🏈', + baseball: 'âšū', + tennis: 'ðŸŽū', + bowling: 'ðŸŽģ', + video_game: 'ðŸŽŪ', + chess: '♟ïļ', + music: 'ðŸŽĩ', + guitar: 'ðŸŽļ', + mic: 'ðŸŽĪ', + + // ðŸ’Ą Objects & Symbols + lightbulb: 'ðŸ’Ą', + phone: 'ðŸ“ą', + laptop: 'ðŸ’ŧ', + bomb: 'ðŸ’Ģ', + money: '💰', + star2: '🌟', + warning: '⚠ïļ', + check: '✅', + x: '❌', + question: '❓', + exclamation: '❗', + infinity: 'â™ūïļ', + hourglass: 'âģ', + clock: '🕒', + + pepe: 'ðŸļ', + troll: '😈', + sus: '🧐', + amongus: 'ðŸŸĨ', + monke: 'ðŸĩ', + chad: 'ðŸĶ', + gigachad: '💊', + wojak: '😔', + feelsbadman: '😞', + feelsgoodman: '😌', + yikes: '😎', + clown: 'ðŸĪĄ', + clownworld: '🌎ðŸĪĄ', + triggered: 'ðŸ˜Ą', + pog: 'ðŸ˜ē', + kek: 'ðŸĪĢ', + based: '😎', + cringe: '😖', + dab: '🕚', + sigma: '🧠', + npc: 'ðŸĪ–', + doomer: '🌑', + zoomer: '⚡', + boomer: 'ðŸ‘ī', + sheesh: 'ðŸ˜Ī', + rickroll: '🕚ðŸŽķ', + trolled: '🧌', + rekt: '💀', + skillissue: '📉', + bro: '🙄', + cope: 'ðŸ˜Ē', + ratio: '➗', + amogus: '👁ïļðŸ‘„👁ïļ', + +}; + + // Escape HTML first + let html = markdown + .replace(/&/g, '&') + .replace(//g, '>'); + + // Code blocks (```...```) + html = html.replace(/```([^```]*)```/gs, (match, p1) => + `
${p1.trim()}
` + ); + + // Inline code (`code`) + html = html.replace(/`([^`\n]+)`/g, (match, p1) => `${p1}`); + + // Emojis :emoji_name: + html = html.replace(/:([a-z0-9_]+):/gi, (match, p1) => emojiMap[p1] || match); + + // Process overlapping formatting in order: Bold+Underline+Italic + html = parseFormatting(html); + + // Replace line breaks + html = html.replace(/\n/g, '
'); + + return html; +} + +// Parse overlapping formatting using tokens +function parseFormatting(text) { + // Define replacement tokens and their HTML + const formattingRules = [ + { regex: /\*\*\*(.*?)\*\*\*/g, html: '$1' }, // ***bold italic*** + { regex: /___(.*?)___/g, html: '$1' }, // ___underline italic___ + { regex: /\*\*(.*?)\*\*/g, html: '$1' }, // **bold** + { regex: /__(.*?)__/g, html: '$1' }, // __underline__ + { regex: /\*(.*?)\*/g, html: '$1' }, // *italic* + { regex: /_(.*?)_/g, html: '$1' }, // _italic_ + { regex: /~~(.*?)~~/g, html: '$1' } // ~~strike~~ + ]; + + // Apply formatting in order + for (const rule of formattingRules) { + text = text.replace(rule.regex, rule.html); + } + + return text; +} + + // Handle incoming messages function handleMessage(event) { if (event.data === "ping") { @@ -618,7 +837,7 @@ function addChatMessage(author, content, isHistory = false) { // Add message text const textDiv = document.createElement('div'); textDiv.className = 'message-text'; - textDiv.textContent = content; + textDiv.innerHTML = markdownToHtmlDiv(content); contentDiv.appendChild(textDiv); messageWrapper.appendChild(avatar); @@ -797,6 +1016,8 @@ async function uploadFile() { const formData = new FormData(); formData.append("file", fileInput.files[0]); formData.append("room", currentRoom); + formData.append("username", username); + formData.append("token", md5(password)); try { const response = await fetch(getUploadUrl(), { @@ -807,17 +1028,6 @@ async function uploadFile() { if (response.ok) { hideFileUpload(); - - const processedMessage = { - "type": "message", - "username": username, - "token": md5(password), - "room": currentRoom, - "content": `Sent a file` - } - if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify(processedMessage)); - } } else { alert("Failed to upload file. Please try again."); } @@ -1143,4 +1353,4 @@ if (uploadField) { this.value = ""; } }; -} \ No newline at end of file +} diff --git a/server/src/main/kotlin/Main.kt b/server/src/main/kotlin/Main.kt index 70e7d7c..47c05da 100644 --- a/server/src/main/kotlin/Main.kt +++ b/server/src/main/kotlin/Main.kt @@ -798,6 +798,20 @@ fun main(args: Array) { Files.copy(uploadedFile.content(), filePath) val room = if (ctx.formParam("room") != null) ctx.formParam("room") else "general" + var username: String = "" + var token: String = "" + + if (ctx.formParam("username") != null) { + username = ctx.formParam("username").toString() + } else { + // do error + } + + if (ctx.formParam("token") != null) { + token = ctx.formParam("token").toString() + } else { + // do error + } val processedData = JSONObject().apply { put("type", "fileStatus") @@ -808,7 +822,7 @@ fun main(args: Array) { val processedData2 = JSONObject().apply { put("type", "file") - put("username", "system") + put("username", username) put("room", room) put("content", "https://maxwellj.xyz/chookchat/uploads/$newFilename") }