diff --git a/public/script.js b/public/script.js index dff96074c..f39c58dc5 100644 --- a/public/script.js +++ b/public/script.js @@ -1,4 +1,4 @@ -import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter, addSafariPatch } from './scripts/RossAscends-mods.js'; +import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter } from './scripts/RossAscends-mods.js'; import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js'; import { generateKoboldWithStreaming, @@ -247,6 +247,7 @@ import { AbortReason } from './scripts/util/AbortReason.js'; import { initSystemPrompts } from './scripts/sysprompt.js'; import { registerExtensionSlashCommands as initExtensionSlashCommands } from './scripts/extensions-slashcommands.js'; import { ToolManager } from './scripts/tool-calling.js'; +import { applyBrowserFixes } from './scripts/browser-fixes.js'; //exporting functions and vars for mods export { @@ -938,7 +939,7 @@ async function firstLoadInit() { throw new Error('Initialization failed'); } - addSafariPatch(); + applyBrowserFixes(); await getClientVersion(); await readSecretState(); await initLocales(); diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index 26a4f9fc5..ecc31df8b 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -121,7 +121,7 @@ export function humanizeGenTime(total_gen_time) { */ var parsedUA = null; -function getParsedUA() { +export function getParsedUA() { if (!parsedUA) { try { parsedUA = Bowser.parse(navigator.userAgent); @@ -717,18 +717,6 @@ export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea, deboun // --------------------------------------------------- -export function addSafariPatch() { - const userAgent = getParsedUA(); - console.debug('User Agent', userAgent); - const isMobileSafari = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); - const isDesktopSafari = userAgent?.browser?.name === 'Safari' && userAgent?.platform?.type === 'desktop'; - const isIOS = userAgent?.os?.name === 'iOS'; - - if (isIOS || isMobileSafari || isDesktopSafari) { - document.body.classList.add('safari'); - } -} - export function initRossMods() { // initial status check checkStatusDebounced(); @@ -741,16 +729,6 @@ export function initRossMods() { RA_autoconnect(); } - if (isMobile()) { - const fixFunkyPositioning = () => { - console.debug('[Mobile] Device viewport change detected.'); - document.documentElement.style.position = 'fixed'; - requestAnimationFrame(() => document.documentElement.style.position = ''); - }; - window.addEventListener('resize', fixFunkyPositioning); - window.addEventListener('orientationchange', fixFunkyPositioning); - } - $('#main_api').change(function () { var PrevAPI = main_api; setTimeout(() => RA_autoconnect(PrevAPI), 100); diff --git a/public/scripts/browser-fixes.js b/public/scripts/browser-fixes.js new file mode 100644 index 000000000..96eaa7402 --- /dev/null +++ b/public/scripts/browser-fixes.js @@ -0,0 +1,77 @@ +import { getParsedUA, isMobile } from './RossAscends-mods.js'; + +const isFirefox = () => /firefox/i.test(navigator.userAgent); + +function sanitizeInlineQuotationOnCopy() { + // STRG+C, STRG+V on firefox leads to duplicate double quotes when inline quotation elements are copied. + // To work around this, take the selection and transform to before calling toString(). + document.addEventListener('copy', function (event) { + const selection = window.getSelection(); + if (!selection.anchorNode?.parentElement.closest('.mes_text')) { + return; + } + + const range = selection.getRangeAt(0).cloneContents(); + const tempDOM = document.createDocumentFragment(); + + function processNode(node) { + if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'q') { + // Transform to , preserve children + const span = document.createElement('span'); + + [...node.childNodes].forEach(child => { + const processedChild = processNode(child); + span.appendChild(processedChild); + }); + + return span; + } else { + // Nested structures containing elements are unlikely + return node.cloneNode(true); + } + } + + [...range.childNodes].forEach(child => { + const processedChild = processNode(child); + tempDOM.appendChild(processedChild); + }); + + const newRange = document.createRange(); + newRange.selectNodeContents(tempDOM); + + event.preventDefault(); + event.clipboardData.setData('text/plain', newRange.toString()); + }); +} + +function addSafariPatch() { + const userAgent = getParsedUA(); + console.debug('User Agent', userAgent); + const isMobileSafari = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); + const isDesktopSafari = userAgent?.browser?.name === 'Safari' && userAgent?.platform?.type === 'desktop'; + const isIOS = userAgent?.os?.name === 'iOS'; + + if (isIOS || isMobileSafari || isDesktopSafari) { + document.body.classList.add('safari'); + } +} + +function applyBrowserFixes() { + if (isFirefox()) { + sanitizeInlineQuotationOnCopy(); + } + + if (isMobile()) { + const fixFunkyPositioning = () => { + console.debug('[Mobile] Device viewport change detected.'); + document.documentElement.style.position = 'fixed'; + requestAnimationFrame(() => document.documentElement.style.position = ''); + }; + window.addEventListener('resize', fixFunkyPositioning); + window.addEventListener('orientationchange', fixFunkyPositioning); + } + + addSafariPatch(); +} + +export { isFirefox, applyBrowserFixes };