diff --git a/src/snek/static/chat-input.js b/src/snek/static/chat-input.js index 4dfcac4..f8ce09f 100644 --- a/src/snek/static/chat-input.js +++ b/src/snek/static/chat-input.js @@ -45,14 +45,98 @@ class ChatInputComponent extends HTMLElement { focus() { this.textarea.focus(); + } + getAuthors(){ + const uniqueAuthors = new Set() + document.querySelectorAll('div.author').forEach(el=> + { + uniqueAuthors.add(el.innerText.trim()) + }) + + return Array.from(uniqueAuthors) +} + extractMentions(text) { + const regex = /@([a-zA-Z0-9_]+)/g; + const mentions = []; + let match; + + while ((match = regex.exec(text)) !== null) { + mentions.push(match[1]); } + return mentions; +} + matchMentionsToAuthors(mentions, authors) { + return mentions.map(mention => { + let closestAuthor = null; + let minDistance = Infinity; + + authors.forEach(author => { + const distance = this.levenshteinDistance(mention.toLowerCase(), author.toLowerCase()); + if (distance < minDistance) { + minDistance = distance; + closestAuthor = author; + } + }); + + return { mention, closestAuthor, distance: minDistance }; + }); +} +levenshteinDistance(a, b) { + const matrix = []; + + // Initialize the first row and column + for (let i = 0; i <= b.length; i++) { + matrix[i] = [i]; + } + for (let j = 0; j <= a.length; j++) { + matrix[0][j] = j; + } + + // Fill in the matrix + for (let i = 1; i <= b.length; i++) { + for (let j = 1; j <= a.length; j++) { + if (b.charAt(i - 1) === a.charAt(j - 1)) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min( + matrix[i - 1][j] + 1, // Deletion + matrix[i][j - 1] + 1, // Insertion + matrix[i - 1][j - 1] + 1 // Substitution + ); + } + } + } + + return matrix[b.length][a.length]; +} + + + + replaceMentionsWithAuthors(text) { + const authors = this.getAuthors(); + const mentions = this.extractMentions(text); + + const matches = this.matchMentionsToAuthors(mentions, authors); + console.info(matches) + let updatedText = text; + matches.forEach(({ mention, closestAuthor }) => { + const mentionRegex = new RegExp(`@${mention}`, 'g'); + console.info(closestAuthor) + updatedText = updatedText.replace(mentionRegex, `@${closestAuthor}`); + }); + + return updatedText; +} + + async connectedCallback() { this.user = null app.rpc.getUser(null).then((user) => { this.user=user }) + const me = this; this.liveType = this.getAttribute("live-type") === "true"; this.liveTypeInterval = parseInt(this.getAttribute("live-type-interval")) || 3; @@ -102,7 +186,7 @@ class ChatInputComponent extends HTMLElement { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); - const message = e.target.value; + const message = me.replaceMentionsWithAuthors(this.value); this.messageUid = null; this.value = ""; this.previousValue = ""; @@ -192,6 +276,7 @@ class ChatInputComponent extends HTMLElement { app.rpc && typeof app.rpc.updateMessageText === "function" ) { + this.value = this.replaceMentionsWithAuthors(this.value); app.rpc.updateMessageText(this.messageUid, this.value); } }