From c2a1c8fd98330cea6329e2d0a5b92cb456720148 Mon Sep 17 00:00:00 2001 From: Mathium05 Date: Mon, 3 Aug 2026 19:40:33 -0500 Subject: [PATCH] firefox MD fixes --- src/webpage/markdown.ts | 63 +++++++++++++++++++++++++---------------- src/webpage/message.ts | 5 ++-- src/webpage/typeBox.ts | 14 +++++++-- 3 files changed, 54 insertions(+), 28 deletions(-) diff --git a/src/webpage/markdown.ts b/src/webpage/markdown.ts index f826ca7..a05b5fa 100644 --- a/src/webpage/markdown.ts +++ b/src/webpage/markdown.ts @@ -6,6 +6,7 @@ import {I18n} from "./i18n.js"; import {Dialog} from "./settings.js"; import {Contextmenu} from "./contextmenu.js"; import {highlight} from "./highlighter/index.js"; +import {TypeBox} from "./typeBox.js"; const linkMenu = new Contextmenu("copyLink", true); linkMenu.addButton( () => I18n.copyRegLink(), @@ -14,7 +15,7 @@ linkMenu.addButton( }, {group: "copyLink"}, ); -const isFirefox = navigator.userAgent.toLowerCase().includes("firefox"); +const isFirefox = Error.prototype.stack === ""; class MarkDown { static emoji?: typeof Emoji; txt: string[]; @@ -351,19 +352,20 @@ class MarkDown { if (stdsize) { build = build.replaceAll("\n", ""); } - if (find === count) { + if (find === count || (keep && count === 3)) { appendcurrent(); i = j; if (keep) { build += "`".repeat(find); } + if (build.endsWith("\n")) build += "\n"; if (count !== 3 && !stdsize) { const samp = document.createElement("samp"); samp.textContent = build; span.appendChild(samp); } else { const pre = document.createElement("pre"); - if (build.at(-1) === "\n") { + if (build.at(-1) === "\n" && !isFirefox) { build = build.substring(0, build.length - 1); } if (txt[i] === "\n") { @@ -974,14 +976,20 @@ class MarkDown { } appendcurrent(); const last = getCurLast(); - + function addSpacer() { + const s = document.createElement("span"); + s.style.setProperty("white-space", "pre"); + s.textContent = "​"; + s.setAttribute("real", ""); + span.append(s); + } if ( last && - last instanceof Text && - last.textContent === "\n" && - Error.prototype.stack === "" && - !isFirefox + isFirefox && + (last instanceof Text || last instanceof HTMLSpanElement) && + last.textContent === "\n" ) { + addSpacer(); span.append(current); } if ( @@ -1052,7 +1060,8 @@ class MarkDown { box.addEventListener("keydown", (_) => { if (_.isComposing) return; if (Error.prototype.stack !== "") return; - if (_.key === "Enter") { + if (_.key === "Enter" && !TypeBox.inPre() && !_.shiftKey) { + return; const selection = window.getSelection() as Selection; if (!selection) return; const range = selection.getRangeAt(0); @@ -1083,28 +1092,29 @@ class MarkDown { const content = this.rawString; if (content) { let txti = text; - if (trim) { + if (trim && TypeBox.inPre()) { txti = txti.replace(/\n$/, ""); } - const [_first, end] = content.split(txti); - console.log([txti, txt, end]); + const [first, ...ends] = content.split(txti); + const end = ends.join(txti); if (rstr) { const tw = text.split(rstr); tw.pop(); text = tw.join(""); } - const boxText = txti + txt + (end ?? ""); - box.textContent = boxText; + const boxText = first + txti + txt + (end ?? ""); + box.innerHTML = ""; + this.txt = boxText.split(""); + box.append(this.makeHTML({keep: true})); const len = txti.length + txt.length; text = boxText; - this.txt = text.split(""); this.boxupdate(len, false, 0); - console.log(this.rawString); } else { - box.textContent = txt; + this.txt = txt.split(""); + box.innerHTML = ""; + box.append(this.makeHTML({keep: true})); text = txt; - this.txt = text.split(""); this.boxupdate(txt.length, false, 0); } }; @@ -1114,7 +1124,7 @@ class MarkDown { gatherBoxContents(_.key === "Backspace"); }; box.onkeydown = (_) => { - if (isFirefox && _.key === "Enter" && !text.endsWith("\n")) { + if (isFirefox && _.key === "Enter" && (TypeBox.inPre() || _.shiftKey)) { _.preventDefault(); _.stopImmediatePropagation(); @@ -1191,6 +1201,7 @@ class MarkDown { html.childNodes[0].childNodes.length === 1 && html.childNodes[0].childNodes[0]; //console.log(box.cloneNode(true), html.cloneNode(true)); + if (isFirefox) allowLazy = false; //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])) && allowLazy) { //console.log("no replace needed"); @@ -1233,6 +1244,11 @@ class MarkDown { if (thing instanceof Text) { const text = thing.textContent; build += text; + if (element.tagName.toLowerCase() === "pre") { + if (build.endsWith("\n") && isFirefox) { + build = build.replace(/\n$/, ""); + } + } continue; } @@ -1431,13 +1447,13 @@ function saveCaretPosition( build += node.textContent; } } else { - //console.error(node,"This shouldn't happen"); + //console.error(node, "This shouldn't happen"); } } } crawlForText(context); if (baseString === "\n") { - build += baseString; + //build += baseString; } text = build; len += build.length; @@ -1447,15 +1463,14 @@ function saveCaretPosition( len = Math.min(len, txtLengthFunc(context).length); len += offset; - return function restore(backspace = false) { + return function restore(_backspace = false) { if (!selection) return; const pos = getTextNodeAtPosition(context, len, txtLengthFunc); if ( pos.node instanceof Text && pos.node.textContent === "\n" && pos.node.nextSibling && - Error.prototype.stack === "" && - !backspace + isFirefox ) { if (pos.node.nextSibling instanceof Text && pos.node.nextSibling.textContent === "\n") { pos.position = 1; diff --git a/src/webpage/message.ts b/src/webpage/message.ts index 13b6cc4..b17d0de 100644 --- a/src/webpage/message.ts +++ b/src/webpage/message.ts @@ -28,6 +28,7 @@ import {ImagesDisplay} from "./disimg"; import {ReportMenu} from "./reporting/report.js"; import {getDeveloperSettings} from "./utils/storage/devSettings.js"; import {getPreferences} from "./utils/storage/userPreferences.js"; +import {TypeBox} from "./typeBox.js"; class Message extends SnowFlake { static contextmenu = new Contextmenu("message menu"); stickers!: Sticker[]; @@ -1017,7 +1018,7 @@ class Message extends SnowFlake { area.append(md.makeHTML()); area.addEventListener("keyup", (event) => { if (this.localuser.keyup(event)) return; - if (event.key === "Enter" && !event.shiftKey) { + if (event.key === "Enter" && !event.shiftKey && !TypeBox.inPre()) { this.edit(MarkDown.gatherBoxText(area)); this.channel.editing = null; this.generateMessage(); @@ -1025,7 +1026,7 @@ class Message extends SnowFlake { }); area.addEventListener("keydown", (event) => { this.localuser.keydown(event); - if (event.key === "Enter" && !event.shiftKey) event.preventDefault(); + if (event.key === "Enter" && !event.shiftKey && !TypeBox.inPre()) event.preventDefault(); if (event.key === "Escape") { this.channel.editing = null; this.generateMessage(); diff --git a/src/webpage/typeBox.ts b/src/webpage/typeBox.ts index 49f7abe..16d1b8c 100644 --- a/src/webpage/typeBox.ts +++ b/src/webpage/typeBox.ts @@ -33,7 +33,7 @@ export class TypeBox { this.box.addEventListener("keydown", (event) => { if (event.isComposing) return; this.localuser?.keydown(event); - if (event.key === "Enter" && !event.shiftKey && window.innerWidth > 600) { + if (event.key === "Enter" && !event.shiftKey && window.innerWidth > 600 && !TypeBox.inPre()) { event.preventDefault(); event.stopImmediatePropagation(); } @@ -268,6 +268,16 @@ export class TypeBox { }; this.localuser = l; } + static inPre() { + const selection = window.getSelection(); + if (!selection) return false; + let node = selection.anchorNode; + while (node) { + if (node instanceof HTMLPreElement) return true; + node = node.parentElement; + } + return false; + } private static async handleEnter(event: KeyboardEvent): Promise { if (event.isComposing) return; if (event.key === "Escape" && (this.files.length || this.localuser?.focusChannel?.replyingto)) { @@ -296,7 +306,7 @@ export class TypeBox { } channel.typingstart(); - if (event.key === "Enter" && !event.shiftKey && window.innerWidth > 600) { + if (event.key === "Enter" && !event.shiftKey && window.innerWidth > 600 && !this.inPre()) { event.preventDefault(); await this.sendMessage(channel, content); }