This commit is contained in:
retoor 2025-09-17 22:55:31 +02:00
parent 0c6da9845c
commit a817b9b61d

View File

@ -1,5 +1,5 @@
import { app } from "./app.js"; import { app } from "./app.js";
import { NjetComponent, eventBus } from "./njet.js"; import { NjetComponent,eventBus } from "./njet.js";
import { FileUploadGrid } from "./file-upload-grid.js"; import { FileUploadGrid } from "./file-upload-grid.js";
class ChatInputComponent extends NjetComponent { class ChatInputComponent extends NjetComponent {
@ -10,15 +10,15 @@ class ChatInputComponent extends NjetComponent {
hiddenCompletions = { hiddenCompletions = {
"/starsRender": () => { "/starsRender": () => {
app.rpc.starsRender(this.channelUid, this.value.replace("/starsRender ", "")); app.rpc.starsRender(this.channelUid, this.value.replace("/starsRender ", ""))
}, },
"/leet": () => { "/leet": () => {
this.value = this.textToLeet(this.value); this.value = this.textToLeet(this.value);
this._leetSpeak = !this._leetSpeak; this._leetSpeak = !this._leetSpeak;
}, },
"/l33t": () => { "/l33t": () => {
this._leetSpeakAdvanced = !this._leetSpeakAdvanced; this._leetSpeakAdvanced = !this._leetSpeakAdvanced;
}, }
}; };
users = []; users = [];
@ -29,12 +29,11 @@ class ChatInputComponent extends NjetComponent {
lastMessagePromise = null; lastMessagePromise = null;
_leetSpeak = false; _leetSpeak = false;
_leetSpeakAdvanced = false; _leetSpeakAdvanced = false;
constructor() { constructor() {
super(); super();
this.lastUpdateEvent = new Date(); this.lastUpdateEvent = new Date();
this.textarea = document.createElement("textarea"); this.textarea = document.createElement("textarea");
this.textarea.classList.add("chat-input-textarea"); this.textarea.classList.add("chat-input-textarea");
this.value = this.getAttribute("value") || ""; this.value = this.getAttribute("value") || "";
} }
@ -48,7 +47,7 @@ class ChatInputComponent extends NjetComponent {
} }
get allAutoCompletions() { get allAutoCompletions() {
return { ...this.autoCompletions, ...this.hiddenCompletions }; return Object.assign({}, this.autoCompletions, this.hiddenCompletions);
} }
resolveAutoComplete(input) { resolveAutoComplete(input) {
@ -79,9 +78,10 @@ class ChatInputComponent extends NjetComponent {
} }
extractMentions(text) { extractMentions(text) {
return Array.from(text.matchAll(/@([a-zA-Z0-9_-]+)/g), (m) => m[1]); return Array.from(text.matchAll(/@([a-zA-Z0-9_-]+)/g), m => m[1]);
} }
matchMentionsToAuthors(mentions, authors) { matchMentionsToAuthors(mentions, authors) {
return mentions.map((mention) => { return mentions.map((mention) => {
const lowerMention = mention.toLowerCase(); const lowerMention = mention.toLowerCase();
@ -156,7 +156,7 @@ class ChatInputComponent extends NjetComponent {
return differences <= 2; return differences <= 2;
} }
matchMentions2ToAuthors(mentions, authors) { matchMentions4ToAuthors(mentions, authors) {
return mentions.map((mention) => { return mentions.map((mention) => {
let closestAuthor = null; let closestAuthor = null;
let minDistance = Infinity; let minDistance = Infinity;
@ -226,38 +226,129 @@ class ChatInputComponent extends NjetComponent {
return updatedText; return updatedText;
} }
matchMentions2ToAuthors(mentions, authors) {
return mentions.map(mention => {
let closestAuthor = null;
let minDistance = Infinity;
const lowerMention = mention.toLowerCase();
authors.forEach(author => {
const lowerAuthor = author.toLowerCase();
let distance = this.levenshteinDistance(lowerMention, lowerAuthor);
if (!this.isSubsequence(lowerMention, lowerAuthor)) {
distance += 10;
}
if (distance < minDistance) {
minDistance = distance;
closestAuthor = author;
console.info("closestAuthor",closestAuthor)
console.info("minDistance",minDistance)
}
});
if (minDistance < 5){
closestAuthor = 0;
}
return { mention, closestAuthor, distance: minDistance };
});
}
textToLeet(text) { textToLeet(text) {
const leetMap = { // L33t speak character mapping
'a': '4', 'A': '4', 'e': '3', 'E': '3', 'i': '1', 'I': '1', const leetMap = {
'o': '0', 'O': '0', 's': '5', 'S': '5', 't': '7', 'T': '7', 'a': '4',
'l': '1', 'L': '1', 'g': '9', 'G': '9', 'b': '6', 'B': '6', 'A': '4',
'z': '2', 'Z': '2' 'e': '3',
}; 'E': '3',
'i': '1',
'I': '1',
'o': '0',
'O': '0',
's': '5',
'S': '5',
't': '7',
'T': '7',
'l': '1',
'L': '1',
'g': '9',
'G': '9',
'b': '6',
'B': '6',
'z': '2',
'Z': '2'
};
// Convert text to l33t speak
return text.split('').map(char => {
return leetMap[char] || char;
}).join('');
}
return text.split('').map(char => leetMap[char] || char).join('');
}
textToLeetAdvanced(text) { // Advanced version with random character selection
const leetMap = { textToLeetAdvanced(text) {
'a': ['4', '@', '/\\'], 'A': ['4', '@', '/\\'], 'e': ['3', '€'], const leetMap = {
'E': ['3', '€'], 'i': ['1', '!', '|'], 'I': ['1', '!', '|'], 'a': ['4', '@', '/\\'],
'o': ['0', '()'], 'O': ['0', '()'], 's': ['5', '$'], 'S': ['5', '$'], 'A': ['4', '@', '/\\'],
't': ['7', '+'], 'T': ['7', '+'], 'l': ['1', '|'], 'L': ['1', '|'], 'e': ['3', '€'],
'g': ['9', '6'], 'G': ['9', '6'], 'b': ['6', '|3'], 'B': ['6', '|3'], 'E': ['3', '€'],
'z': ['2'], 'Z': ['2'], 'h': ['#', '|-|'], 'H': ['#', '|-|'], 'i': ['1', '!', '|'],
'n': ['|\\|'], 'N': ['|\\|'], 'm': ['|\\/|'], 'M': ['|\\/|'], 'I': ['1', '!', '|'],
'w': ['\\/\\/'], 'W': ['\\/\\/'], 'v': ['\\/', 'V'], 'V': ['\\/', 'V'], 'o': ['0', '()'],
'u': ['|_|'], 'U': ['|_|'], 'r': ['|2'], 'R': ['|2'], 'f': ['|='], 'O': ['0', '()'],
'F': ['|='], 'd': ['|)'], 'D': ['|)'], 'c': ['(', '['], 'C': ['(', '['], 's': ['5', '$'],
'k': ['|<'], 'K': ['|<'], 'p': ['|>'], 'P': ['|>'], 'x': ['><'], 'S': ['5', '$'],
'X': ['><'], 'y': ['`/'], 'Y': ['`/'] 't': ['7', '+'],
}; 'T': ['7', '+'],
'l': ['1', '|'],
'L': ['1', '|'],
'g': ['9', '6'],
'G': ['9', '6'],
'b': ['6', '|3'],
'B': ['6', '|3'],
'z': ['2'],
'Z': ['2'],
'h': ['#', '|-|'],
'H': ['#', '|-|'],
'n': ['|\\|'],
'N': ['|\\|'],
'm': ['|\\/|'],
'M': ['|\\/|'],
'w': ['\\/\\/'],
'W': ['\\/\\/'],
'v': ['\\/', 'V'],
'V': ['\\/', 'V'],
'u': ['|_|'],
'U': ['|_|'],
'r': ['|2'],
'R': ['|2'],
'f': ['|='],
'F': ['|='],
'd': ['|)'],
'D': ['|)'],
'c': ['(', '['],
'C': ['(', '['],
'k': ['|<'],
'K': ['|<'],
'p': ['|>'],
'P': ['|>'],
'x': ['><'],
'X': ['><'],
'y': ['`/'],
'Y': ['`/']
};
return text.split('').map(char => {
const options = leetMap[char];
if (options) {
return options[Math.floor(Math.random() * options.length)];
}
return char;
}).join('');
}
return text.split('').map(char => {
const options = leetMap[char];
return options ? options[Math.floor(Math.random() * options.length)] : char;
}).join('');
}
async connectedCallback() { async connectedCallback() {
this.user = null; this.user = null;
@ -265,7 +356,7 @@ class ChatInputComponent extends NjetComponent {
this.user = user; this.user = user;
}); });
this.liveType = this.getAttribute("live-type") === "true"; this.liveType = this.getAttribute("live-type") == "true";
this.liveTypeInterval = parseInt(this.getAttribute("live-type-interval")) || 6; this.liveTypeInterval = parseInt(this.getAttribute("live-type-interval")) || 6;
this.channelUid = this.getAttribute("channel"); this.channelUid = this.getAttribute("channel");
@ -283,14 +374,15 @@ class ChatInputComponent extends NjetComponent {
this.textarea.setAttribute("placeholder", "Type a message..."); this.textarea.setAttribute("placeholder", "Type a message...");
this.textarea.setAttribute("rows", "2"); this.textarea.setAttribute("rows", "2");
this.appendChild(this.textarea);
this.appendChild(this.textarea);
this.ttsButton = document.createElement("stt-button"); this.ttsButton = document.createElement("stt-button");
this.snekSpeaker = document.createElement("snek-speaker"); this.snekSpeaker = document.createElement("snek-speaker");
this.appendChild(this.snekSpeaker); this.appendChild(this.snekSpeaker);
this.ttsButton.addEventListener("click", () => { this.ttsButton.addEventListener("click", (e) => {
this.snekSpeaker.enable(); this.snekSpeaker.enable()
}); });
this.appendChild(this.ttsButton); this.appendChild(this.ttsButton);
@ -306,34 +398,34 @@ class ChatInputComponent extends NjetComponent {
e.preventDefault(); e.preventDefault();
this.fileUploadGrid.openFileDialog(); this.fileUploadGrid.openFileDialog();
}); });
this.subscribe("file-uploading", (e) => {
this.subscribe("file-uploading", () => {
this.fileUploadGrid.style.display = "block"; this.fileUploadGrid.style.display = "block";
this.uploadButton.style.display = "none"; this.uploadButton.style.display = "none";
this.textarea.style.display = "none"; this.textarea.style.display = "none";
}); })
this.appendChild(this.uploadButton); this.appendChild(this.uploadButton);
this.textarea.addEventListener("blur", () => { this.textarea.addEventListener("blur", () => {
this.updateFromInput(this.value, true).then(() => this.updateFromInput("")); this.updateFromInput(this.value, true).then(
this.updateFromInput("")
)
}); });
this.subscribe("file-uploads-done", (data) => { this.subscribe("file-uploads-done", (data)=>{
this.textarea.style.display = "block"; this.textarea.style.display = "block";
this.uploadButton.style.display = "block"; this.uploadButton.style.display = "block";
this.fileUploadGrid.style.display = "none"; this.fileUploadGrid.style.display = "none";
const msg = data.reduce((message, file) => { let msg =data.reduce((message, file) => {
return `${message}[${file.filename || file.name || file.remoteFile}](/channel/attachment/${file.remoteFile})`; return `${message}[${file.filename || file.name || file.remoteFile}](/channel/attachment/${file.remoteFile})`;
}, ''); }, '');
app.rpc.sendMessage(this.channelUid, msg, true); app.rpc.sendMessage(this.channelUid, msg, true);
}); });
this.textarea.addEventListener("change", (e) => {
this.value = this.textarea.value; this.textarea.addEventListener("change",(e)=>{
this.value = this.textarea.value;
this.updateFromInput(e.target.value); this.updateFromInput(e.target.value);
}); })
this.textarea.addEventListener("keyup", (e) => { this.textarea.addEventListener("keyup", (e) => {
if (e.key === "Enter" && !e.shiftKey) { if (e.key === "Enter" && !e.shiftKey) {
const message = this.replaceMentionsWithAuthors(this.value); const message = this.replaceMentionsWithAuthors(this.value);
@ -342,7 +434,7 @@ class ChatInputComponent extends NjetComponent {
if (!message) { if (!message) {
return; return;
} }
const autoCompletionHandler = this.allAutoCompletions[this.value.split(" ", 1)[0]]; let autoCompletionHandler = this.allAutoCompletions[this.value.split(" ", 1)[0]];
if (autoCompletionHandler) { if (autoCompletionHandler) {
autoCompletionHandler(); autoCompletionHandler();
this.value = ""; this.value = "";
@ -360,9 +452,10 @@ class ChatInputComponent extends NjetComponent {
this.textarea.addEventListener("keydown", (e) => { this.textarea.addEventListener("keydown", (e) => {
this.value = e.target.value; this.value = e.target.value;
let autoCompletion = null;
if (e.key === "Tab") { if (e.key === "Tab") {
e.preventDefault(); e.preventDefault();
const autoCompletion = this.resolveAutoComplete(this.value); autoCompletion = this.resolveAutoComplete(this.value);
if (autoCompletion) { if (autoCompletion) {
e.target.value = autoCompletion; e.target.value = autoCompletion;
this.value = autoCompletion; this.value = autoCompletion;
@ -379,11 +472,11 @@ class ChatInputComponent extends NjetComponent {
} }
}); });
this.addEventListener("upload", () => { this.addEventListener("upload", (e) => {
this.focus(); this.focus();
}); });
this.addEventListener("uploaded", (e) => { this.addEventListener("uploaded", function (e) {
const message = e.detail.files.reduce((message, file) => { let message = e.detail.files.reduce((message, file) => {
return `${message}[${file.name}](/channel/attachment/${file.relative_url})`; return `${message}[${file.name}](/channel/attachment/${file.relative_url})`;
}, ''); }, '');
app.rpc.sendMessage(this.channelUid, message, true); app.rpc.sendMessage(this.channelUid, message, true);
@ -419,21 +512,25 @@ class ChatInputComponent extends NjetComponent {
finalizeMessage(messageUid) { finalizeMessage(messageUid) {
let value = this.value; let value = this.value;
value = this.replaceMentionsWithAuthors(value); value = this.replaceMentionsWithAuthors(value)
if (this._leetSpeak) { if(this._leetSpeak){
value = this.textToLeet(value); value = this.textToLeet(value);
} else if (this._leetSpeakAdvanced) { }else if(this._leetSpeakAdvanced){
value = this.textToLeetAdvanced(value); value = this.textToLeetAdvanced(value);
} }
app.rpc.sendMessage(this.channelUid, value, true); app.rpc.sendMessage(this.channelUid, value , true);
this.value = ""; this.value = "";
this.messageUid = null; this.messageUid = null;
this.queuedMessage = null; this.queuedMessage = null;
this.lastMessagePromise = null; this.lastMessagePromise = null;
} }
updateFromInput(value, isFinal = false) { updateFromInput(value, isFinal = false) {
this.value = value; this.value = value;
this.flagTyping(); this.flagTyping();
if (this.liveType && value[0] !== "/") { if (this.liveType && value[0] !== "/") {