From 6e2b5d5dc8aed6e30ab5cbc5b4e5da4c8a2ca3e2 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 16 Jan 2025 01:55:26 +0200 Subject: [PATCH] Fix copy actions for non-HTTPS/localhost Fixes #2352 --- public/script.js | 59 ++++++++++++++++++----------------------- public/scripts/utils.js | 20 ++++++++++++++ 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/public/script.js b/public/script.js index e8e04bbd8..14d695611 100644 --- a/public/script.js +++ b/public/script.js @@ -168,6 +168,7 @@ import { isTrueBoolean, toggleDrawer, isElementInViewport, + copyText, } from './scripts/utils.js'; import { debounce_timeout } from './scripts/constants.js'; @@ -2315,16 +2316,15 @@ export function addCopyToCodeBlocks(messageElement) { const codeBlocks = $(messageElement).find('pre code'); for (let i = 0; i < codeBlocks.length; i++) { hljs.highlightElement(codeBlocks.get(i)); - if (navigator.clipboard !== undefined) { - const copyButton = document.createElement('i'); - copyButton.classList.add('fa-solid', 'fa-copy', 'code-copy', 'interactable'); - copyButton.title = 'Copy code'; - codeBlocks.get(i).appendChild(copyButton); - copyButton.addEventListener('pointerup', function (event) { - navigator.clipboard.writeText(codeBlocks.get(i).innerText); - toastr.info(t`Copied!`, '', { timeOut: 2000 }); - }); - } + const copyButton = document.createElement('i'); + copyButton.classList.add('fa-solid', 'fa-copy', 'code-copy', 'interactable'); + copyButton.title = 'Copy code'; + codeBlocks.get(i).appendChild(copyButton); + copyButton.addEventListener('pointerup', async function () { + const text = codeBlocks.get(i).innerText; + await copyText(text); + toastr.info(t`Copied!`, '', { timeOut: 2000 }); + }); } } @@ -2724,7 +2724,7 @@ export function getStoppingStrings(isImpersonate, isContinue) { export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, quietImage = null, quietName = null, responseLength = null, force_chid = null) { console.log('got into genQuietPrompt'); const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0; - let eventHook = () => {}; + let eventHook = () => { }; try { /** @type {GenerateOptions} */ const options = { @@ -3390,7 +3390,7 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0; const isInstruct = power_user.instruct.enabled && api !== 'openai' && api !== 'novel' && !instructOverride; const isQuiet = true; - let eventHook = () => {}; + let eventHook = () => { }; if (systemPrompt) { systemPrompt = substituteParams(systemPrompt); @@ -5469,7 +5469,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) { } else { diffPrevPrompt.style.display = 'none'; } - popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('click', function () { + popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('pointerup', async function () { let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt; let rawPromptValues = rawPrompt; @@ -5477,7 +5477,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) { rawPromptValues = rawPrompt.map(x => x.content).join('\n'); } - navigator.clipboard.writeText(rawPromptValues); + await copyText(rawPromptValues); toastr.info(t`Copied!`); }); @@ -9507,7 +9507,7 @@ API Settings: ${JSON.stringify(getSettingsContents[getSettingsContents.main_api //console.log(logMessage); try { - await navigator.clipboard.writeText(logMessage); + await copyText(logMessage); toastr.info('Your ST API setup data has been copied to the clipboard.'); } catch (error) { toastr.error('Failed to copy ST Setup to clipboard:', error); @@ -10572,24 +10572,18 @@ jQuery(async function () { setTimeout(function () { $('#shadow_select_chat_popup').css('display', 'none'); }, animation_duration); }); - if (navigator.clipboard === undefined) { - // No clipboard support - $('.mes_copy').remove(); - } - else { - $(document).on('pointerup', '.mes_copy', function () { - if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) { - try { - const messageId = $(this).closest('.mes').attr('mesid'); - const text = chat[messageId]['mes']; - navigator.clipboard.writeText(text); - toastr.info('Copied!', '', { timeOut: 2000 }); - } catch (err) { - console.error('Failed to copy: ', err); - } + $(document).on('pointerup', '.mes_copy', async function () { + if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) { + try { + const messageId = $(this).closest('.mes').attr('mesid'); + const text = chat[messageId]['mes']; + await copyText(text); + toastr.info('Copied!', '', { timeOut: 2000 }); + } catch (err) { + console.error('Failed to copy: ', err); } - }); - } + } + }); $(document).on('pointerup', '.mes_prompt', async function () { let mesIdForItemization = $(this).closest('.mes').attr('mesId'); @@ -11483,4 +11477,3 @@ jQuery(async function () { initCustomSelectedSamplers(); }); - diff --git a/public/scripts/utils.js b/public/scripts/utils.js index 60b49797a..a479aee52 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -391,6 +391,26 @@ export function getStringHash(str, seed = 0) { return 4294967296 * (2097151 & h2) + (h1 >>> 0); } +/** + * Copy text to clipboard. Use navigator.clipboard.writeText if available, otherwise use document.execCommand. + * @param {string} text - The text to copy to the clipboard. + * @returns {Promise} A promise that resolves when the text has been copied to the clipboard. + */ +export function copyText(text) { + if (navigator.clipboard) { + return navigator.clipboard.writeText(text); + } + + const parent = document.querySelector('dialog[open]:last-of-type') ?? document.body; + const textArea = document.createElement('textarea'); + textArea.value = text; + parent.appendChild(textArea); + textArea.focus(); + textArea.select(); + document.execCommand('copy'); + parent.removeChild(textArea); +} + /** * Map of debounced functions to their timers. * Weak map is used to avoid memory leaks.