emoji button and a lot of bug fixes

This commit is contained in:
MathMan05 2025-04-05 20:55:18 -05:00
parent a33755f6ae
commit fbd7c38f44
6 changed files with 182 additions and 45 deletions

View file

@ -134,8 +134,16 @@ class Emoji {
});
const menu = document.createElement("div");
menu.classList.add("flexttb", "emojiPicker");
menu.style.top = y + "px";
menu.style.left = x + "px";
if (y > 0) {
menu.style.top = y + "px";
} else {
menu.style.bottom = y * -1 + "px";
}
if (x > 0) {
menu.style.left = x + "px";
} else {
menu.style.right = x * -1 + "px";
}
const topBar = document.createElement("div");
topBar.classList.add("flexltr", "emojiHeading");

View file

@ -85,10 +85,10 @@
<div id="channelw" class="flexltr">
<div id="loadingdiv"></div>
</div>
<div id="ghostMessages"></div>
<div style="position: relative">
<div id="searchOptions" class="flexttb searchOptions"></div>
</div>
<div id="ghostMessages"></div>
<div id="pasteimage" class="flexltr"></div>
<div id="replybox" class="hideReplyBox"></div>
<div id="typediv">
@ -96,6 +96,7 @@
<div class="outerTypeBox">
<span class="svg-upload svgicon" id="upload"></span>
<div id="typebox" contenteditable="true"></div>
<span class="svgicon svg-emoji" id="emojiTB"></span>
</div>
</div>
<div id="typing" class="hidden flexltr">

View file

@ -267,4 +267,11 @@ import {I18n} from "./i18n.js";
}
};
};
const emojiTB = document.getElementById("emojiTB") as HTMLElement;
emojiTB.onmousedown = (e) => {
e.preventDefault();
e.stopImmediatePropagation();
thisUser.TBEmojiMenu(emojiTB.getBoundingClientRect());
};
emojiTB.onclick = (e) => e.stopImmediatePropagation();
})();

View file

@ -20,7 +20,7 @@ import {
} from "./jsontypes.js";
import {Member} from "./member.js";
import {Dialog, Form, FormError, Options, Settings} from "./settings.js";
import {getTextNodeAtPosition, MarkDown} from "./markdown.js";
import {getTextNodeAtPosition, MarkDown, saveCaretPosition} from "./markdown.js";
import {Bot} from "./bot.js";
import {Role} from "./role.js";
import {VoiceFactory} from "./voice.js";
@ -33,7 +33,9 @@ import {Rights} from "./rights.js";
import {Contextmenu} from "./contextmenu.js";
const wsCodesRetry = new Set([4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009]);
interface CustomHTMLDivElement extends HTMLDivElement {
markdown: MarkDown;
}
class Localuser {
badges = new Map<
string,
@ -2229,10 +2231,6 @@ class Localuser {
}
readonly autofillregex = Object.freeze(/[@#:]([a-z0-9 ]*)$/i);
mdBox() {
interface CustomHTMLDivElement extends HTMLDivElement {
markdown: MarkDown;
}
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
const typeMd = typebox.markdown;
typeMd.owner = this;
@ -2240,18 +2238,57 @@ class Localuser {
this.search(document.getElementById("searchOptions") as HTMLDivElement, typeMd, str, pre);
};
}
MDReplace(replacewith: string, original: string, typebox: MarkDown) {
async TBEmojiMenu(rect: DOMRect) {
const typebox = document.getElementById("typebox") as CustomHTMLDivElement;
const p = saveCaretPosition(typebox);
if (!p) return;
const original = MarkDown.getText();
console.log(original);
const emoji = await Emoji.emojiPicker(
-10 + rect.left - window.screen.width,
-5 + rect.top - window.screen.height,
this,
);
p();
const md = typebox.markdown;
this.MDReplace(
emoji.id
? `<${emoji.animated ? "a" : ""}:${emoji.name}:${emoji.id}>`
: (emoji.emoji as string),
original,
md,
null,
);
//*/
}
MDReplace(
replacewith: string,
original: string,
typebox: MarkDown,
start: RegExp | null = this.autofillregex,
) {
let raw = typebox.rawString;
raw = raw.split(original)[1];
if (raw === undefined) return;
raw = original.replace(this.autofillregex, "") + replacewith + raw;
let empty = raw.length === 0;
raw = original !== "" ? raw.split(original)[1] : raw;
if (raw === undefined && !empty) return;
if (empty) {
raw = "";
}
raw = (start ? original.replace(start, "") : original) + replacewith + raw;
console.log(raw);
console.log(replacewith);
console.log(original);
typebox.txt = raw.split("");
const match = original.match(this.autofillregex);
console.log(typebox.rawString);
const match = start ? original.match(start) : true;
if (match) {
typebox.boxupdate(replacewith.length - match[0].length);
console.log(match);
typebox.boxupdate(
replacewith.length - (match === true ? 0 : match[0].length),
false,
original.length,
);
}
}
MDSearchOptions(
@ -2339,8 +2376,7 @@ class Localuser {
break;
case "Enter":
case "Tab":
//@ts-ignore
cur.onclick();
cur.click();
break;
}
return true;
@ -2437,6 +2473,7 @@ class Localuser {
await Member.new(thing, this.lookingguild as Guild);
}
}
if (!typebox.rawString.startsWith(original)) return;
this.MDFineMentionGen(name, original, box, typebox);
}
});
@ -2475,6 +2512,8 @@ class Localuser {
this.MDSearchOptions([], "", box, md);
}
break;
default:
return;
}
return;
}

View file

@ -46,6 +46,9 @@ class MarkDown {
get textContent() {
return this.makeHTML().textContent;
}
static getText() {
return text;
}
makeHTML({keep = this.keep, stdsize = this.stdsize} = {}) {
return this.markdown(this.txt, {keep, stdsize});
}
@ -67,6 +70,9 @@ class MarkDown {
current = document.createElement("span");
}
}
function getCurLast(): Element | undefined {
return span.children[span.children.length - 1];
}
for (let i = 0; i < txt.length; i++) {
if (txt[i] === "\n" || i === 0) {
const first = i === 0;
@ -143,6 +149,10 @@ class MarkDown {
}
if (txt[i] === "\n") {
if (!stdsize) {
const last = getCurLast();
if (last instanceof HTMLElement && last.contentEditable === "false") {
span.append(document.createElement("span"));
}
appendcurrent();
span.append(document.createElement("br"));
}
@ -460,6 +470,13 @@ class MarkDown {
if (txt[j] === ">") {
appendcurrent();
const last = getCurLast();
if (
last instanceof HTMLBRElement ||
(last instanceof HTMLElement && last.contentEditable === "false")
) {
span.append(document.createElement("span"));
}
const mention = document.createElement("span");
mention.classList.add("mentionMD");
mention.contentEditable = "false";
@ -680,6 +697,15 @@ class MarkDown {
current.textContent += txt[i];
}
appendcurrent();
const last = getCurLast();
if (
last &&
last instanceof HTMLElement &&
last.contentEditable &&
!(last instanceof HTMLBRElement)
) {
span.append(current);
}
return span;
}
static unspoil(e: any): void {
@ -696,7 +722,8 @@ class MarkDown {
};
let prevcontent = "";
box.onkeyup = (_) => {
const content = MarkDown.gatherBoxText(box);
let content = MarkDown.gatherBoxText(box);
if (content === "\n") content = "";
if (content !== prevcontent) {
prevcontent = content;
this.txt = content.split("");
@ -725,14 +752,19 @@ class MarkDown {
) {
this.customBox = [stringToHTML, HTMLToString];
}
boxupdate(offset = 0) {
boxupdate(offset = 0, allowLazy = true, computedLength: void | number = undefined) {
const box = this.box.deref();
if (!box) return;
let restore: undefined | (() => void);
if (this.customBox) {
restore = saveCaretPosition(box, offset, this.customBox[1]);
restore = saveCaretPosition(box, offset, this.customBox[1], computedLength);
} else {
restore = saveCaretPosition(box, offset);
restore = saveCaretPosition(
box,
offset,
MarkDown.gatherBoxText.bind(MarkDown),
computedLength,
);
}
if (this.customBox) {
@ -748,10 +780,13 @@ class MarkDown {
html.childNodes[0].childNodes[0];
//console.log(box.cloneNode(true), html.cloneNode(true));
//TODO this may be slow, may want to check in on this in the future if it is
if (!box.hasChildNodes() || html.isEqualNode(Array.from(box.childNodes)[0])) {
if ((!box.hasChildNodes() || html.isEqualNode(Array.from(box.childNodes)[0])) && allowLazy) {
//console.log("no replace needed");
} else {
if (!(box.childNodes.length === 1 && box.childNodes[0] instanceof Text && condition)) {
if (
!(box.childNodes.length === 1 && box.childNodes[0] instanceof Text && condition) ||
!allowLazy
) {
box.innerHTML = "";
box.append(html);
} else {
@ -762,13 +797,6 @@ class MarkDown {
}
if (restore) {
restore();
if (this.customBox) {
const test = saveCaretPosition(box, 0, this.customBox[1]);
if (test) test();
} else {
const test = saveCaretPosition(box);
if (test) test();
}
}
this.onUpdate(text, formatted);
}
@ -864,6 +892,7 @@ function saveCaretPosition(
context: HTMLElement,
offset = 0,
txtLengthFunc = MarkDown.gatherBoxText.bind(MarkDown),
computedLength: void | number = undefined,
) {
const selection = window.getSelection() as Selection;
if (!selection) return;
@ -873,26 +902,39 @@ function saveCaretPosition(
let base = selection.anchorNode as Node;
range.setStart(base, 0);
let baseString: string;
if (!(base instanceof Text)) {
let i = 0;
const index = selection.focusOffset;
//@ts-ignore
for (const thing of base.childNodes) {
if (i === index) {
base = thing;
break;
}
i++;
let i = 0;
const index = selection.focusOffset;
for (const thing of Array.from(base.childNodes)) {
if (i === index) {
base = thing;
break;
}
i++;
}
console.log(base);
const prev = base.previousSibling;
let len = 0;
if ((!prev || prev instanceof HTMLBRElement) && base instanceof HTMLBRElement) {
len--;
}
if (
!(base instanceof Text) &&
!(
base instanceof HTMLSpanElement &&
base.className === "" &&
base.children.length == 0 &&
!(base instanceof HTMLBRElement)
)
) {
if (base instanceof HTMLElement) {
baseString = txtLengthFunc(base);
} else {
baseString = base.textContent as string;
baseString = base.textContent || "";
}
} else {
baseString = selection.toString();
}
range.setStart(context, 0);
let build = "";
@ -937,8 +979,14 @@ function saveCaretPosition(
build += baseString;
}
text = build;
let len = build.length + offset;
len += build.length;
if (computedLength !== undefined) {
len = computedLength;
}
len = Math.min(len, txtLengthFunc(context).length);
console.log(len, base);
len += offset;
console.log(len);
return function restore() {
if (!selection) return;
const pos = getTextNodeAtPosition(context, len, txtLengthFunc);
@ -960,6 +1008,12 @@ function getTextNodeAtPosition(
node: Node;
position: number;
} {
if (index === 0) {
return {
node: root,
position: 0,
};
}
if (root instanceof Text) {
return {
node: root,
@ -976,6 +1030,7 @@ function getTextNodeAtPosition(
position: -1,
};
}
let lastElm: Node = root;
for (const node of root.childNodes as unknown as Node[]) {
lastElm = node;
@ -983,10 +1038,31 @@ function getTextNodeAtPosition(
if (node instanceof HTMLElement) {
len = txtLengthFunc(node).length;
} else {
len = (node.textContent as string).length;
len = (node.textContent || "").length;
}
if (len <= index && (len < index || len !== 0)) {
index -= len;
if (index === 0) {
let nodey = node;
let bad = false;
while (nodey.childNodes.length) {
nodey = Array.from(nodey.childNodes).at(-1) as ChildNode;
if (nodey instanceof HTMLElement && nodey.contentEditable === "false") {
bad = true;
break;
}
}
if (
!(nodey instanceof HTMLBRElement) &&
(!(nodey instanceof HTMLElement) || nodey.contentEditable === "false") &&
!bad
) {
return {
node: nodey,
position: (nodey.textContent || "").length,
};
}
}
} else {
const returny = getTextNodeAtPosition(node, index);
if (returny.position === -1) {

View file

@ -432,6 +432,11 @@ textarea {
mask-repeat: no-repeat;
aspect-ratio: 1/1;
}
#emojiTB{
width:.2in;
height:.2in;
cursor: pointer;
}
.selectarrow {
position: absolute;
top: 10px;
@ -1143,6 +1148,7 @@ span.instanceStatus {
flex-grow: 1;
width: 100%;
margin-left: 0.06in;
white-space: pre;
}
.outerTypeBox {
max-height: 50svh;
@ -1499,7 +1505,7 @@ span .quote:last-of-type .quoteline {
cursor: pointer;
}
.mentionMD::after {
content: "";
/* content: ""; */
}
.mentionMD:hover {
background: var(--mention);