Merge pull request #2994 from artisticMink/fix-double-quotes Fix double double quotes when copying chat message text in Firefox
Signed| @@ -1,4 +1,4 @@ | |||
| 1 | import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter, addSafariPatch } from './scripts/RossAscends-mods.js'; | 1 | import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter } from './scripts/RossAscends-mods.js'; |
| 2 | import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js'; | 2 | import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js'; |
| 3 | import { | 3 | import { |
| 4 | generateKoboldWithStreaming, | 4 | generateKoboldWithStreaming, |
| @@ -247,6 +247,7 @@ import { AbortReason } from './scripts/util/AbortReason.js'; | |||
| 247 | import { initSystemPrompts } from './scripts/sysprompt.js'; | 247 | import { initSystemPrompts } from './scripts/sysprompt.js'; |
| 248 | import { registerExtensionSlashCommands as initExtensionSlashCommands } from './scripts/extensions-slashcommands.js'; | 248 | import { registerExtensionSlashCommands as initExtensionSlashCommands } from './scripts/extensions-slashcommands.js'; |
| 249 | import { ToolManager } from './scripts/tool-calling.js'; | 249 | import { ToolManager } from './scripts/tool-calling.js'; |
| 250 | import { applyBrowserFixes } from './scripts/browser-fixes.js'; | ||
| 250 | 251 | ||
| 251 | //exporting functions and vars for mods | 252 | //exporting functions and vars for mods |
| 252 | export { | 253 | export { |
| @@ -938,7 +939,7 @@ async function firstLoadInit() { | |||
| 938 | throw new Error('Initialization failed'); | 939 | throw new Error('Initialization failed'); |
| 939 | } | 940 | } |
| 940 | 941 | ||
| 941 | addSafariPatch(); | 942 | applyBrowserFixes(); |
| 942 | await getClientVersion(); | 943 | await getClientVersion(); |
| 943 | await readSecretState(); | 944 | await readSecretState(); |
| 944 | await initLocales(); | 945 | await initLocales(); |
| @@ -121,7 +121,7 @@ export function humanizeGenTime(total_gen_time) { | |||
| 121 | */ | 121 | */ |
| 122 | var parsedUA = null; | 122 | var parsedUA = null; |
| 123 | 123 | ||
| 124 | function getParsedUA() { | 124 | export function getParsedUA() { |
| 125 | if (!parsedUA) { | 125 | if (!parsedUA) { |
| 126 | try { | 126 | try { |
| 127 | parsedUA = Bowser.parse(navigator.userAgent); | 127 | parsedUA = Bowser.parse(navigator.userAgent); |
| @@ -717,18 +717,6 @@ export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea, deboun | |||
| 717 | 717 | ||
| 718 | // --------------------------------------------------- | 718 | // --------------------------------------------------- |
| 719 | 719 | ||
| 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 | |||
| 732 | export function initRossMods() { | 720 | export function initRossMods() { |
| 733 | // initial status check | 721 | // initial status check |
| 734 | checkStatusDebounced(); | 722 | checkStatusDebounced(); |
| @@ -741,16 +729,6 @@ export function initRossMods() { | |||
| 741 | RA_autoconnect(); | 729 | RA_autoconnect(); |
| 742 | } | 730 | } |
| 743 | 731 | ||
| 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 | |||
| 754 | $('#main_api').change(function () { | 732 | $('#main_api').change(function () { |
| 755 | var PrevAPI = main_api; | 733 | var PrevAPI = main_api; |
| 756 | setTimeout(() => RA_autoconnect(PrevAPI), 100); | 734 | setTimeout(() => RA_autoconnect(PrevAPI), 100); |
| @@ -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 }; | ||