Fix copy actions for non-HTTPS/localhost Fixes #2352
| @@ -168,6 +168,7 @@ import { | ||
| 168 | 168 | isTrueBoolean, |
| 169 | 169 | toggleDrawer, |
| 170 | 170 | isElementInViewport, |
| 171 | + copyText, | |
| 171 | 172 | } from './scripts/utils.js'; |
| 172 | 173 | import { debounce_timeout } from './scripts/constants.js'; |
| 173 | 174 | |
| @@ -2315,18 +2316,17 @@ export function addCopyToCodeBlocks(messageElement) { | ||
| 2315 | 2316 | const codeBlocks = $(messageElement).find('pre code'); |
| 2316 | 2317 | for (let i = 0; i < codeBlocks.length; i++) { |
| 2317 | 2318 | hljs.highlightElement(codeBlocks.get(i)); |
| 2318 | - if (navigator.clipboard !== undefined) { | |
| 2319 | 2319 | const copyButton = document.createElement('i'); |
| 2320 | 2320 | copyButton.classList.add('fa-solid', 'fa-copy', 'code-copy', 'interactable'); |
| 2321 | 2321 | copyButton.title = 'Copy code'; |
| 2322 | 2322 | codeBlocks.get(i).appendChild(copyButton); |
| 2323 | 2323 | copyButton.addEventListener('pointerup', async function (event) { |
| 2324 | 2324 | navigator.clipboard.writeText( const text = codeBlocks.get(i).innerText); |
| 2325 | + await copyText(text); | |
| 2325 | 2326 | toastr.info(t`Copied!`, '', { timeOut: 2000 }); |
| 2326 | 2327 | }); |
| 2327 | 2328 | } |
| 2328 | 2329 | } |
| 2329 | -} | |
| 2330 | 2330 | |
| 2331 | 2331 | |
| 2332 | 2332 | /** |
| @@ -5469,7 +5469,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) { | ||
| 5469 | 5469 | } else { |
| 5470 | 5470 | diffPrevPrompt.style.display = 'none'; |
| 5471 | 5471 | } |
| 5472 | 5472 | popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('clickpointerup', async function () { |
| 5473 | 5473 | let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt; |
| 5474 | 5474 | let rawPromptValues = rawPrompt; |
| 5475 | 5475 | |
| @@ -5477,7 +5477,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) { | ||
| 5477 | 5477 | rawPromptValues = rawPrompt.map(x => x.content).join('\n'); |
| 5478 | 5478 | } |
| 5479 | 5479 | |
| 5480 | 5480 | navigator.clipboard.writeTextawait copyText(rawPromptValues); |
| 5481 | 5481 | toastr.info(t`Copied!`); |
| 5482 | 5482 | }); |
| 5483 | 5483 | |
| @@ -9507,7 +9507,7 @@ API Settings: ${JSON.stringify(getSettingsContents[getSettingsContents.main_api | ||
| 9507 | 9507 | //console.log(logMessage); |
| 9508 | 9508 | |
| 9509 | 9509 | try { |
| 9510 | 9510 | await navigator.clipboard.writeTextcopyText(logMessage); |
| 9511 | 9511 | toastr.info('Your ST API setup data has been copied to the clipboard.'); |
| 9512 | 9512 | } catch (error) { |
| 9513 | 9513 | toastr.error('Failed to copy ST Setup to clipboard:', error); |
| @@ -10572,24 +10572,18 @@ jQuery(async function () { | ||
| 10572 | 10572 | setTimeout(function () { $('#shadow_select_chat_popup').css('display', 'none'); }, animation_duration); |
| 10573 | 10573 | }); |
| 10574 | 10574 | |
| 10575 | - if (navigator.clipboard === undefined) { | |
| 10575 | + $(document).on('pointerup', '.mes_copy', async function () { | |
| 10576 | - // No clipboard support | |
| 10577 | - $('.mes_copy').remove(); | |
| 10578 | - } | |
| 10579 | - else { | |
| 10580 | - $(document).on('pointerup', '.mes_copy', function () { | |
| 10581 | 10576 | if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) { |
| 10582 | 10577 | try { |
| 10583 | 10578 | const messageId = $(this).closest('.mes').attr('mesid'); |
| 10584 | 10579 | const text = chat[messageId]['mes']; |
| 10585 | - navigator.clipboard.writeText(text); | |
| 10580 | + await copyText(text); | |
| 10586 | 10581 | toastr.info('Copied!', '', { timeOut: 2000 }); |
| 10587 | 10582 | } catch (err) { |
| 10588 | 10583 | console.error('Failed to copy: ', err); |
| 10589 | 10584 | } |
| 10590 | 10585 | } |
| 10591 | 10586 | }); |
| 10592 | - } | |
| 10593 | 10587 | |
| 10594 | 10588 | $(document).on('pointerup', '.mes_prompt', async function () { |
| 10595 | 10589 | let mesIdForItemization = $(this).closest('.mes').attr('mesId'); |
| @@ -11483,4 +11477,3 @@ jQuery(async function () { | ||
| 11483 | 11477 | |
| 11484 | 11478 | initCustomSelectedSamplers(); |
| 11485 | 11479 | }); |
| 11486 | - | |
| @@ -392,6 +392,26 @@ export function getStringHash(str, seed = 0) { | ||
| 392 | 392 | } |
| 393 | 393 | |
| 394 | 394 | /** |
| 395 | + * Copy text to clipboard. Use navigator.clipboard.writeText if available, otherwise use document.execCommand. | |
| 396 | + * @param {string} text - The text to copy to the clipboard. | |
| 397 | + * @returns {Promise<void>} A promise that resolves when the text has been copied to the clipboard. | |
| 398 | + */ | |
| 399 | +export function copyText(text) { | |
| 400 | + if (navigator.clipboard) { | |
| 401 | + return navigator.clipboard.writeText(text); | |
| 402 | + } | |
| 403 | + | |
| 404 | + const parent = document.querySelector('dialog[open]:last-of-type') ?? document.body; | |
| 405 | + const textArea = document.createElement('textarea'); | |
| 406 | + textArea.value = text; | |
| 407 | + parent.appendChild(textArea); | |
| 408 | + textArea.focus(); | |
| 409 | + textArea.select(); | |
| 410 | + document.execCommand('copy'); | |
| 411 | + parent.removeChild(textArea); | |
| 412 | +} | |
| 413 | + | |
| 414 | +/** | |
| 395 | 415 | * Map of debounced functions to their timers. |
| 396 | 416 | * Weak map is used to avoid memory leaks. |
| 397 | 417 | * @type {WeakMap<function, any>} |