feat: Centralize clipboard functionality with copyToClipboard utility across components

This commit is contained in:
Will Miao
2025-04-22 09:33:05 +08:00
parent b12079e0f6
commit 85f987d15c
11 changed files with 57 additions and 100 deletions

View File

@@ -1,4 +1,4 @@
import { showToast } from '../utils/uiHelpers.js';
import { showToast, copyToClipboard } from '../utils/uiHelpers.js';
import { state } from '../state/index.js';
import { showCheckpointModal } from './checkpointModal/index.js';
import { NSFW_LEVELS } from '../utils/constants.js';
@@ -204,21 +204,7 @@ export function createCheckpointCard(checkpoint) {
const checkpointName = card.dataset.file_name;
try {
// Modern clipboard API
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(checkpointName);
} else {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = checkpointName;
textarea.style.position = 'absolute';
textarea.style.left = '-99999px';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
showToast('Checkpoint name copied', 'success');
await copyToClipboard(checkpointName, 'Checkpoint name copied');
} catch (err) {
console.error('Copy failed:', err);
showToast('Copy failed', 'error');

View File

@@ -1,4 +1,4 @@
import { showToast, openCivitai } from '../utils/uiHelpers.js';
import { showToast, openCivitai, copyToClipboard } from '../utils/uiHelpers.js';
import { state } from '../state/index.js';
import { showLoraModal } from './loraModal/index.js';
import { bulkManager } from '../managers/BulkManager.js';
@@ -205,26 +205,7 @@ export function createLoraCard(lora) {
const strength = usageTips.strength || 1;
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
try {
// Modern clipboard API
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(loraSyntax);
} else {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = loraSyntax;
textarea.style.position = 'absolute';
textarea.style.left = '-99999px';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
showToast('LoRA syntax copied', 'success');
} catch (err) {
console.error('Copy failed:', err);
showToast('Copy failed', 'error');
}
await copyToClipboard(loraSyntax, 'LoRA syntax copied');
});
// Civitai button click event

View File

@@ -1,5 +1,5 @@
// Recipe Card Component
import { showToast } from '../utils/uiHelpers.js';
import { showToast, copyToClipboard } from '../utils/uiHelpers.js';
import { modalManager } from '../managers/ModalManager.js';
class RecipeCard {
@@ -109,14 +109,11 @@ class RecipeCard {
.then(response => response.json())
.then(data => {
if (data.success && data.syntax) {
return navigator.clipboard.writeText(data.syntax);
return copyToClipboard(data.syntax, 'Recipe syntax copied to clipboard');
} else {
throw new Error(data.error || 'No syntax returned');
}
})
.then(() => {
showToast('Recipe syntax copied to clipboard', 'success');
})
.catch(err => {
console.error('Failed to copy: ', err);
showToast('Failed to copy recipe syntax', 'error');
@@ -279,4 +276,4 @@ class RecipeCard {
}
}
export { RecipeCard };
export { RecipeCard };

View File

@@ -1,5 +1,5 @@
// Recipe Modal Component
import { showToast } from '../utils/uiHelpers.js';
import { showToast, copyToClipboard } from '../utils/uiHelpers.js';
import { state } from '../state/index.js';
import { setSessionItem, removeSessionItem } from '../utils/storageHelpers.js';
@@ -747,9 +747,8 @@ class RecipeModal {
const data = await response.json();
if (data.success && data.syntax) {
// Copy to clipboard
await navigator.clipboard.writeText(data.syntax);
showToast('Recipe syntax copied to clipboard', 'success');
// Use the centralized copyToClipboard utility function
await copyToClipboard(data.syntax, 'Recipe syntax copied to clipboard');
} else {
throw new Error(data.error || 'No syntax returned from server');
}
@@ -761,12 +760,7 @@ class RecipeModal {
// Helper method to copy text to clipboard
copyToClipboard(text, successMessage) {
navigator.clipboard.writeText(text).then(() => {
showToast(successMessage, 'success');
}).catch(err => {
console.error('Failed to copy text: ', err);
showToast('Failed to copy text', 'error');
});
copyToClipboard(text, successMessage);
}
// Add new method to handle downloading missing LoRAs

View File

@@ -2,7 +2,7 @@
* ShowcaseView.js
* Handles showcase content (images, videos) display for checkpoint modal
*/
import { showToast } from '../../utils/uiHelpers.js';
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
import { state } from '../../state/index.js';
import { NSFW_LEVELS } from '../../utils/constants.js';
@@ -307,8 +307,7 @@ function initMetadataPanelHandlers(container) {
if (!promptElement) return;
try {
await navigator.clipboard.writeText(promptElement.textContent);
showToast('Prompt copied to clipboard', 'success');
await copyToClipboard(promptElement.textContent, 'Prompt copied to clipboard');
} catch (err) {
console.error('Copy failed:', err);
showToast('Copy failed', 'error');

View File

@@ -1,7 +1,7 @@
/**
* RecipeTab - Handles the recipes tab in the Lora Modal
*/
import { showToast } from '../../utils/uiHelpers.js';
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
import { setSessionItem, removeSessionItem } from '../../utils/storageHelpers.js';
/**
@@ -172,14 +172,11 @@ function copyRecipeSyntax(recipeId) {
.then(response => response.json())
.then(data => {
if (data.success && data.syntax) {
return navigator.clipboard.writeText(data.syntax);
return copyToClipboard(data.syntax, 'Recipe syntax copied to clipboard');
} else {
throw new Error(data.error || 'No syntax returned');
}
})
.then(() => {
showToast('Recipe syntax copied to clipboard', 'success');
})
.catch(err => {
console.error('Failed to copy: ', err);
showToast('Failed to copy recipe syntax', 'error');

View File

@@ -2,7 +2,7 @@
* ShowcaseView.js
* 处理LoRA模型展示内容图片、视频的功能模块
*/
import { showToast } from '../../utils/uiHelpers.js';
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
import { state } from '../../state/index.js';
import { NSFW_LEVELS } from '../../utils/constants.js';
@@ -311,8 +311,7 @@ function initMetadataPanelHandlers(container) {
if (!promptElement) return;
try {
await navigator.clipboard.writeText(promptElement.textContent);
showToast('Prompt copied to clipboard', 'success');
await copyToClipboard(promptElement.textContent, 'Prompt copied to clipboard');
} catch (err) {
console.error('Copy failed:', err);
showToast('Copy failed', 'error');

View File

@@ -2,7 +2,7 @@
* TriggerWords.js
* 处理LoRA模型触发词相关的功能模块
*/
import { showToast } from '../../utils/uiHelpers.js';
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
import { saveModelMetadata } from './ModelMetadata.js';
/**
@@ -336,23 +336,7 @@ async function saveTriggerWords() {
*/
window.copyTriggerWord = async function(word) {
try {
// Modern clipboard API - with fallback for non-secure contexts
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(word);
} else {
// Fallback for older browsers or non-secure contexts
const textarea = document.createElement('textarea');
textarea.value = word;
textarea.style.position = 'absolute';
textarea.style.left = '-99999px';
document.body.appendChild(textarea);
textarea.select();
const success = document.execCommand('copy');
document.body.removeChild(textarea);
if (!success) throw new Error('Copy command failed');
}
showToast('Trigger word copied', 'success');
await copyToClipboard(word, 'Trigger word copied');
} catch (err) {
console.error('Copy failed:', err);
showToast('Copy failed', 'error');

View File

@@ -3,8 +3,7 @@
*
* 将原始的LoraModal.js拆分成多个功能模块后的主入口文件
*/
import { showToast } from '../../utils/uiHelpers.js';
import { state } from '../../state/index.js';
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
import { modalManager } from '../../managers/ModalManager.js';
import { renderShowcaseContent, toggleShowcase, setupShowcaseScroll, scrollToTop } from './ShowcaseView.js';
import { setupTabSwitching, loadModelDescription } from './ModelDescription.js';
@@ -174,8 +173,7 @@ export function showLoraModal(lora) {
// Copy file name function
window.copyFileName = async function(fileName) {
try {
await navigator.clipboard.writeText(fileName);
showToast('File name copied', 'success');
await copyToClipboard(fileName, 'File name copied');
} catch (err) {
console.error('Copy failed:', err);
showToast('Copy failed', 'error');

View File

@@ -1,5 +1,5 @@
import { state } from '../state/index.js';
import { showToast } from '../utils/uiHelpers.js';
import { showToast, copyToClipboard } from '../utils/uiHelpers.js';
import { updateCardsForBulkMode } from '../components/LoraCard.js';
export class BulkManager {
@@ -205,13 +205,7 @@ export class BulkManager {
return;
}
try {
await navigator.clipboard.writeText(loraSyntaxes.join(', '));
showToast(`Copied ${loraSyntaxes.length} LoRA syntaxes to clipboard`, 'success');
} catch (err) {
console.error('Copy failed:', err);
showToast('Copy failed', 'error');
}
await copyToClipboard(loraSyntaxes.join(', '), `Copied ${loraSyntaxes.length} LoRA syntaxes to clipboard`);
}
// Create and show the thumbnail strip of selected LoRAs

View File

@@ -2,6 +2,40 @@ import { state } from '../state/index.js';
import { resetAndReload } from '../api/loraApi.js';
import { getStorageItem, setStorageItem } from './storageHelpers.js';
/**
* Utility function to copy text to clipboard with fallback for older browsers
* @param {string} text - The text to copy to clipboard
* @param {string} successMessage - Optional success message to show in toast
* @returns {Promise<boolean>} - Promise that resolves to true if copy was successful
*/
export async function copyToClipboard(text, successMessage = 'Copied to clipboard') {
try {
// Modern clipboard API
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(text);
} else {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'absolute';
textarea.style.left = '-99999px';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
if (successMessage) {
showToast(successMessage, 'success');
}
return true;
} catch (err) {
console.error('Copy failed:', err);
showToast('Copy failed', 'error');
return false;
}
}
export function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
@@ -108,12 +142,6 @@ export function toggleFolder(tag) {
resetAndReload();
}
export function copyTriggerWord(word) {
navigator.clipboard.writeText(word).then(() => {
showToast('Trigger word copied', 'success');
});
}
function filterByFolder(folderPath) {
document.querySelectorAll('.lora-card').forEach(card => {
card.style.display = card.dataset.folder === folderPath ? '' : 'none';