Merge pull request #2994 from artisticMink/fix-double-quotes Fix double double quotes when copying chat message text in Firefox

ada44497dea61f47c29c5236879e42ce5127028a

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
3 files changed, +81 -25Ignore whitespace
public/script.js+3 -2
@@ -1,4 +1,4 @@
11import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter, addSafariPatch } from './scripts/RossAscends-mods.js';
22import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js';
33import {
44 generateKoboldWithStreaming,
@@ -247,6 +247,7 @@ import { AbortReason } from './scripts/util/AbortReason.js';
247247import { initSystemPrompts } from './scripts/sysprompt.js';
248248import { registerExtensionSlashCommands as initExtensionSlashCommands } from './scripts/extensions-slashcommands.js';
249249import { ToolManager } from './scripts/tool-calling.js';
250+import { applyBrowserFixes } from './scripts/browser-fixes.js';
250251
251252//exporting functions and vars for mods
252253export {
@@ -938,7 +939,7 @@ async function firstLoadInit() {
938939 throw new Error('Initialization failed');
939940 }
940941
941942 addSafariPatchapplyBrowserFixes();
942943 await getClientVersion();
943944 await readSecretState();
944945 await initLocales();
public/scripts/RossAscends-mods.js+1 -23
@@ -121,7 +121,7 @@ export function humanizeGenTime(total_gen_time) {
121121 */
122122var parsedUA = null;
123123
124124export function getParsedUA() {
125125 if (!parsedUA) {
126126 try {
127127 parsedUA = Bowser.parse(navigator.userAgent);
@@ -717,18 +717,6 @@ export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea, deboun
717717
718718// ---------------------------------------------------
719719
720-export function addSafariPatch() {
721- const userAgent = getParsedUA();
722- console.debug('User Agent', userAgent);
723- const isMobileSafari = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
724- const isDesktopSafari = userAgent?.browser?.name === 'Safari' && userAgent?.platform?.type === 'desktop';
725- const isIOS = userAgent?.os?.name === 'iOS';
726-
727- if (isIOS || isMobileSafari || isDesktopSafari) {
728- document.body.classList.add('safari');
729- }
730-}
731-
732720export function initRossMods() {
733721 // initial status check
734722 checkStatusDebounced();
@@ -741,16 +729,6 @@ export function initRossMods() {
741729 RA_autoconnect();
742730 }
743731
744- if (isMobile()) {
745- const fixFunkyPositioning = () => {
746- console.debug('[Mobile] Device viewport change detected.');
747- document.documentElement.style.position = 'fixed';
748- requestAnimationFrame(() => document.documentElement.style.position = '');
749- };
750- window.addEventListener('resize', fixFunkyPositioning);
751- window.addEventListener('orientationchange', fixFunkyPositioning);
752- }
753-
754732 $('#main_api').change(function () {
755733 var PrevAPI = main_api;
756734 setTimeout(() => RA_autoconnect(PrevAPI), 100);
public/scripts/browser-fixes.js+77 -0
@@ -0,0 +1,77 @@
1+import { getParsedUA, isMobile } from './RossAscends-mods.js';
2+
3+const isFirefox = () => /firefox/i.test(navigator.userAgent);
4+
5+function sanitizeInlineQuotationOnCopy() {
6+ // STRG+C, STRG+V on firefox leads to duplicate double quotes when inline quotation elements are copied.
7+ // To work around this, take the selection and transform <q> to <span> before calling toString().
8+ document.addEventListener('copy', function (event) {
9+ const selection = window.getSelection();
10+ if (!selection.anchorNode?.parentElement.closest('.mes_text')) {
11+ return;
12+ }
13+
14+ const range = selection.getRangeAt(0).cloneContents();
15+ const tempDOM = document.createDocumentFragment();
16+
17+ function processNode(node) {
18+ if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'q') {
19+ // Transform <q> to <span>, preserve children
20+ const span = document.createElement('span');
21+
22+ [...node.childNodes].forEach(child => {
23+ const processedChild = processNode(child);
24+ span.appendChild(processedChild);
25+ });
26+
27+ return span;
28+ } else {
29+ // Nested structures containing <q> elements are unlikely
30+ return node.cloneNode(true);
31+ }
32+ }
33+
34+ [...range.childNodes].forEach(child => {
35+ const processedChild = processNode(child);
36+ tempDOM.appendChild(processedChild);
37+ });
38+
39+ const newRange = document.createRange();
40+ newRange.selectNodeContents(tempDOM);
41+
42+ event.preventDefault();
43+ event.clipboardData.setData('text/plain', newRange.toString());
44+ });
45+}
46+
47+function addSafariPatch() {
48+ const userAgent = getParsedUA();
49+ console.debug('User Agent', userAgent);
50+ const isMobileSafari = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
51+ const isDesktopSafari = userAgent?.browser?.name === 'Safari' && userAgent?.platform?.type === 'desktop';
52+ const isIOS = userAgent?.os?.name === 'iOS';
53+
54+ if (isIOS || isMobileSafari || isDesktopSafari) {
55+ document.body.classList.add('safari');
56+ }
57+}
58+
59+function applyBrowserFixes() {
60+ if (isFirefox()) {
61+ sanitizeInlineQuotationOnCopy();
62+ }
63+
64+ if (isMobile()) {
65+ const fixFunkyPositioning = () => {
66+ console.debug('[Mobile] Device viewport change detected.');
67+ document.documentElement.style.position = 'fixed';
68+ requestAnimationFrame(() => document.documentElement.style.position = '');
69+ };
70+ window.addEventListener('resize', fixFunkyPositioning);
71+ window.addEventListener('orientationchange', fixFunkyPositioning);
72+ }
73+
74+ addSafariPatch();
75+}
76+
77+export { isFirefox, applyBrowserFixes };