Merge pull request #3311 from SillyTavern/fix-http-copy Fix copy actions for non-HTTPS/localhost

df28c2c676d62d2e9791839d336a5aa823b210ec

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

Signed
2 files changed, +46 -33Ignore whitespace
public/script.js+26 -33
@@ -168,6 +168,7 @@ import {
168 isTrueBoolean,168 isTrueBoolean,
169 toggleDrawer,169 toggleDrawer,
170 isElementInViewport,170 isElementInViewport,
171 copyText,
171} from './scripts/utils.js';172} from './scripts/utils.js';
172import { debounce_timeout } from './scripts/constants.js';173import { debounce_timeout } from './scripts/constants.js';
173174
@@ -2315,16 +2316,15 @@ export function addCopyToCodeBlocks(messageElement) {
2315 const codeBlocks = $(messageElement).find('pre code');2316 const codeBlocks = $(messageElement).find('pre code');
2316 for (let i = 0; i < codeBlocks.length; i++) {2317 for (let i = 0; i < codeBlocks.length; i++) {
2317 hljs.highlightElement(codeBlocks.get(i));2318 hljs.highlightElement(codeBlocks.get(i));
2318 if (navigator.clipboard !== undefined) {2319 const copyButton = document.createElement('i');
2319 const copyButton = document.createElement('i');2320 copyButton.classList.add('fa-solid', 'fa-copy', 'code-copy', 'interactable');
2320 copyButton.classList.add('fa-solid', 'fa-copy', 'code-copy', 'interactable');2321 copyButton.title = 'Copy code';
2321 copyButton.title = 'Copy code';2322 codeBlocks.get(i).appendChild(copyButton);
2322 codeBlocks.get(i).appendChild(copyButton);2323 copyButton.addEventListener('pointerup', async function () {
2323 copyButton.addEventListener('pointerup', function (event) {2324 const text = codeBlocks.get(i).innerText;
2324 navigator.clipboard.writeText(codeBlocks.get(i).innerText);2325 await copyText(text);
2325 toastr.info(t`Copied!`, '', { timeOut: 2000 });2326 toastr.info(t`Copied!`, '', { timeOut: 2000 });
2326 });2327 });
2327 }
2328 }2328 }
2329}2329}
23302330
@@ -2724,7 +2724,7 @@ export function getStoppingStrings(isImpersonate, isContinue) {
2724export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, quietImage = null, quietName = null, responseLength = null, force_chid = null) {2724export async function generateQuietPrompt(quiet_prompt, quietToLoud, skipWIAN, quietImage = null, quietName = null, responseLength = null, force_chid = null) {
2725 console.log('got into genQuietPrompt');2725 console.log('got into genQuietPrompt');
2726 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;2726 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;
2727 let eventHook = () => {};2727 let eventHook = () => { };
2728 try {2728 try {
2729 /** @type {GenerateOptions} */2729 /** @type {GenerateOptions} */
2730 const options = {2730 const options = {
@@ -3390,7 +3390,7 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
3390 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;3390 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;
3391 const isInstruct = power_user.instruct.enabled && api !== 'openai' && api !== 'novel' && !instructOverride;3391 const isInstruct = power_user.instruct.enabled && api !== 'openai' && api !== 'novel' && !instructOverride;
3392 const isQuiet = true;3392 const isQuiet = true;
3393 let eventHook = () => {};3393 let eventHook = () => { };
33943394
3395 if (systemPrompt) {3395 if (systemPrompt) {
3396 systemPrompt = substituteParams(systemPrompt);3396 systemPrompt = substituteParams(systemPrompt);
@@ -5469,7 +5469,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
5469 } else {5469 } else {
5470 diffPrevPrompt.style.display = 'none';5470 diffPrevPrompt.style.display = 'none';
5471 }5471 }
5472 popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('click', function () {5472 popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('pointerup', async function () {
5473 let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt;5473 let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt;
5474 let rawPromptValues = rawPrompt;5474 let rawPromptValues = rawPrompt;
54755475
@@ -5477,7 +5477,7 @@ async function promptItemize(itemizedPrompts, requestedMesId) {
5477 rawPromptValues = rawPrompt.map(x => x.content).join('\n');5477 rawPromptValues = rawPrompt.map(x => x.content).join('\n');
5478 }5478 }
54795479
5480 navigator.clipboard.writeText(rawPromptValues);5480 await copyText(rawPromptValues);
5481 toastr.info(t`Copied!`);5481 toastr.info(t`Copied!`);
5482 });5482 });
54835483
@@ -9507,7 +9507,7 @@ API Settings: ${JSON.stringify(getSettingsContents[getSettingsContents.main_api
9507 //console.log(logMessage);9507 //console.log(logMessage);
95089508
9509 try {9509 try {
9510 await navigator.clipboard.writeText(logMessage);9510 await copyText(logMessage);
9511 toastr.info('Your ST API setup data has been copied to the clipboard.');9511 toastr.info('Your ST API setup data has been copied to the clipboard.');
9512 } catch (error) {9512 } catch (error) {
9513 toastr.error('Failed to copy ST Setup to clipboard:', error);9513 toastr.error('Failed to copy ST Setup to clipboard:', error);
@@ -10572,24 +10572,18 @@ jQuery(async function () {
10572 setTimeout(function () { $('#shadow_select_chat_popup').css('display', 'none'); }, animation_duration);10572 setTimeout(function () { $('#shadow_select_chat_popup').css('display', 'none'); }, animation_duration);
10573 });10573 });
1057410574
10575 if (navigator.clipboard === undefined) {10575 $(document).on('pointerup', '.mes_copy', async function () {
10576 // No clipboard support10576 if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) {
10577 $('.mes_copy').remove();10577 try {
10578 }10578 const messageId = $(this).closest('.mes').attr('mesid');
10579 else {10579 const text = chat[messageId]['mes'];
10580 $(document).on('pointerup', '.mes_copy', function () {10580 await copyText(text);
10581 if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) {10581 toastr.info('Copied!', '', { timeOut: 2000 });
10582 try {10582 } catch (err) {
10583 const messageId = $(this).closest('.mes').attr('mesid');10583 console.error('Failed to copy: ', err);
10584 const text = chat[messageId]['mes'];
10585 navigator.clipboard.writeText(text);
10586 toastr.info('Copied!', '', { timeOut: 2000 });
10587 } catch (err) {
10588 console.error('Failed to copy: ', err);
10589 }
10590 }10584 }
10591 });10585 }
10592 }10586 });
1059310587
10594 $(document).on('pointerup', '.mes_prompt', async function () {10588 $(document).on('pointerup', '.mes_prompt', async function () {
10595 let mesIdForItemization = $(this).closest('.mes').attr('mesId');10589 let mesIdForItemization = $(this).closest('.mes').attr('mesId');
@@ -11483,4 +11477,3 @@ jQuery(async function () {
1148311477
11484 initCustomSelectedSamplers();11478 initCustomSelectedSamplers();
11485});11479});
11486
public/scripts/utils.js+20 -0
@@ -392,6 +392,26 @@ export function getStringHash(str, seed = 0) {
392}392}
393393
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 */
399export 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 * Map of debounced functions to their timers.415 * Map of debounced functions to their timers.
396 * Weak map is used to avoid memory leaks.416 * Weak map is used to avoid memory leaks.
397 * @type {WeakMap<function, any>}417 * @type {WeakMap<function, any>}