mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-26 07:35:44 -03:00
feat: Centralize clipboard functionality with copyToClipboard utility across components
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { showToast } from '../utils/uiHelpers.js';
|
import { showToast, copyToClipboard } from '../utils/uiHelpers.js';
|
||||||
import { state } from '../state/index.js';
|
import { state } from '../state/index.js';
|
||||||
import { showCheckpointModal } from './checkpointModal/index.js';
|
import { showCheckpointModal } from './checkpointModal/index.js';
|
||||||
import { NSFW_LEVELS } from '../utils/constants.js';
|
import { NSFW_LEVELS } from '../utils/constants.js';
|
||||||
@@ -204,21 +204,7 @@ export function createCheckpointCard(checkpoint) {
|
|||||||
const checkpointName = card.dataset.file_name;
|
const checkpointName = card.dataset.file_name;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Modern clipboard API
|
await copyToClipboard(checkpointName, 'Checkpoint name copied');
|
||||||
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');
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Copy failed:', err);
|
console.error('Copy failed:', err);
|
||||||
showToast('Copy failed', 'error');
|
showToast('Copy failed', 'error');
|
||||||
|
|||||||
@@ -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 { state } from '../state/index.js';
|
||||||
import { showLoraModal } from './loraModal/index.js';
|
import { showLoraModal } from './loraModal/index.js';
|
||||||
import { bulkManager } from '../managers/BulkManager.js';
|
import { bulkManager } from '../managers/BulkManager.js';
|
||||||
@@ -205,26 +205,7 @@ export function createLoraCard(lora) {
|
|||||||
const strength = usageTips.strength || 1;
|
const strength = usageTips.strength || 1;
|
||||||
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
const loraSyntax = `<lora:${card.dataset.file_name}:${strength}>`;
|
||||||
|
|
||||||
try {
|
await copyToClipboard(loraSyntax, 'LoRA syntax copied');
|
||||||
// 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');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Civitai button click event
|
// Civitai button click event
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Recipe Card Component
|
// Recipe Card Component
|
||||||
import { showToast } from '../utils/uiHelpers.js';
|
import { showToast, copyToClipboard } from '../utils/uiHelpers.js';
|
||||||
import { modalManager } from '../managers/ModalManager.js';
|
import { modalManager } from '../managers/ModalManager.js';
|
||||||
|
|
||||||
class RecipeCard {
|
class RecipeCard {
|
||||||
@@ -109,14 +109,11 @@ class RecipeCard {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success && data.syntax) {
|
if (data.success && data.syntax) {
|
||||||
return navigator.clipboard.writeText(data.syntax);
|
return copyToClipboard(data.syntax, 'Recipe syntax copied to clipboard');
|
||||||
} else {
|
} else {
|
||||||
throw new Error(data.error || 'No syntax returned');
|
throw new Error(data.error || 'No syntax returned');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
|
||||||
showToast('Recipe syntax copied to clipboard', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error('Failed to copy: ', err);
|
console.error('Failed to copy: ', err);
|
||||||
showToast('Failed to copy recipe syntax', 'error');
|
showToast('Failed to copy recipe syntax', 'error');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Recipe Modal Component
|
// Recipe Modal Component
|
||||||
import { showToast } from '../utils/uiHelpers.js';
|
import { showToast, copyToClipboard } from '../utils/uiHelpers.js';
|
||||||
import { state } from '../state/index.js';
|
import { state } from '../state/index.js';
|
||||||
import { setSessionItem, removeSessionItem } from '../utils/storageHelpers.js';
|
import { setSessionItem, removeSessionItem } from '../utils/storageHelpers.js';
|
||||||
|
|
||||||
@@ -747,9 +747,8 @@ class RecipeModal {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.success && data.syntax) {
|
if (data.success && data.syntax) {
|
||||||
// Copy to clipboard
|
// Use the centralized copyToClipboard utility function
|
||||||
await navigator.clipboard.writeText(data.syntax);
|
await copyToClipboard(data.syntax, 'Recipe syntax copied to clipboard');
|
||||||
showToast('Recipe syntax copied to clipboard', 'success');
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(data.error || 'No syntax returned from server');
|
throw new Error(data.error || 'No syntax returned from server');
|
||||||
}
|
}
|
||||||
@@ -761,12 +760,7 @@ class RecipeModal {
|
|||||||
|
|
||||||
// Helper method to copy text to clipboard
|
// Helper method to copy text to clipboard
|
||||||
copyToClipboard(text, successMessage) {
|
copyToClipboard(text, successMessage) {
|
||||||
navigator.clipboard.writeText(text).then(() => {
|
copyToClipboard(text, successMessage);
|
||||||
showToast(successMessage, 'success');
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('Failed to copy text: ', err);
|
|
||||||
showToast('Failed to copy text', 'error');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new method to handle downloading missing LoRAs
|
// Add new method to handle downloading missing LoRAs
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* ShowcaseView.js
|
* ShowcaseView.js
|
||||||
* Handles showcase content (images, videos) display for checkpoint modal
|
* 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 { state } from '../../state/index.js';
|
||||||
import { NSFW_LEVELS } from '../../utils/constants.js';
|
import { NSFW_LEVELS } from '../../utils/constants.js';
|
||||||
|
|
||||||
@@ -307,8 +307,7 @@ function initMetadataPanelHandlers(container) {
|
|||||||
if (!promptElement) return;
|
if (!promptElement) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(promptElement.textContent);
|
await copyToClipboard(promptElement.textContent, 'Prompt copied to clipboard');
|
||||||
showToast('Prompt copied to clipboard', 'success');
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Copy failed:', err);
|
console.error('Copy failed:', err);
|
||||||
showToast('Copy failed', 'error');
|
showToast('Copy failed', 'error');
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* RecipeTab - Handles the recipes tab in the Lora Modal
|
* 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';
|
import { setSessionItem, removeSessionItem } from '../../utils/storageHelpers.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,14 +172,11 @@ function copyRecipeSyntax(recipeId) {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success && data.syntax) {
|
if (data.success && data.syntax) {
|
||||||
return navigator.clipboard.writeText(data.syntax);
|
return copyToClipboard(data.syntax, 'Recipe syntax copied to clipboard');
|
||||||
} else {
|
} else {
|
||||||
throw new Error(data.error || 'No syntax returned');
|
throw new Error(data.error || 'No syntax returned');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
|
||||||
showToast('Recipe syntax copied to clipboard', 'success');
|
|
||||||
})
|
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error('Failed to copy: ', err);
|
console.error('Failed to copy: ', err);
|
||||||
showToast('Failed to copy recipe syntax', 'error');
|
showToast('Failed to copy recipe syntax', 'error');
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* ShowcaseView.js
|
* ShowcaseView.js
|
||||||
* 处理LoRA模型展示内容(图片、视频)的功能模块
|
* 处理LoRA模型展示内容(图片、视频)的功能模块
|
||||||
*/
|
*/
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
|
||||||
import { state } from '../../state/index.js';
|
import { state } from '../../state/index.js';
|
||||||
import { NSFW_LEVELS } from '../../utils/constants.js';
|
import { NSFW_LEVELS } from '../../utils/constants.js';
|
||||||
|
|
||||||
@@ -311,8 +311,7 @@ function initMetadataPanelHandlers(container) {
|
|||||||
if (!promptElement) return;
|
if (!promptElement) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(promptElement.textContent);
|
await copyToClipboard(promptElement.textContent, 'Prompt copied to clipboard');
|
||||||
showToast('Prompt copied to clipboard', 'success');
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Copy failed:', err);
|
console.error('Copy failed:', err);
|
||||||
showToast('Copy failed', 'error');
|
showToast('Copy failed', 'error');
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* TriggerWords.js
|
* TriggerWords.js
|
||||||
* 处理LoRA模型触发词相关的功能模块
|
* 处理LoRA模型触发词相关的功能模块
|
||||||
*/
|
*/
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
|
||||||
import { saveModelMetadata } from './ModelMetadata.js';
|
import { saveModelMetadata } from './ModelMetadata.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -336,23 +336,7 @@ async function saveTriggerWords() {
|
|||||||
*/
|
*/
|
||||||
window.copyTriggerWord = async function(word) {
|
window.copyTriggerWord = async function(word) {
|
||||||
try {
|
try {
|
||||||
// Modern clipboard API - with fallback for non-secure contexts
|
await copyToClipboard(word, 'Trigger word copied');
|
||||||
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');
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Copy failed:', err);
|
console.error('Copy failed:', err);
|
||||||
showToast('Copy failed', 'error');
|
showToast('Copy failed', 'error');
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
*
|
*
|
||||||
* 将原始的LoraModal.js拆分成多个功能模块后的主入口文件
|
* 将原始的LoraModal.js拆分成多个功能模块后的主入口文件
|
||||||
*/
|
*/
|
||||||
import { showToast } from '../../utils/uiHelpers.js';
|
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
|
||||||
import { state } from '../../state/index.js';
|
|
||||||
import { modalManager } from '../../managers/ModalManager.js';
|
import { modalManager } from '../../managers/ModalManager.js';
|
||||||
import { renderShowcaseContent, toggleShowcase, setupShowcaseScroll, scrollToTop } from './ShowcaseView.js';
|
import { renderShowcaseContent, toggleShowcase, setupShowcaseScroll, scrollToTop } from './ShowcaseView.js';
|
||||||
import { setupTabSwitching, loadModelDescription } from './ModelDescription.js';
|
import { setupTabSwitching, loadModelDescription } from './ModelDescription.js';
|
||||||
@@ -174,8 +173,7 @@ export function showLoraModal(lora) {
|
|||||||
// Copy file name function
|
// Copy file name function
|
||||||
window.copyFileName = async function(fileName) {
|
window.copyFileName = async function(fileName) {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(fileName);
|
await copyToClipboard(fileName, 'File name copied');
|
||||||
showToast('File name copied', 'success');
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Copy failed:', err);
|
console.error('Copy failed:', err);
|
||||||
showToast('Copy failed', 'error');
|
showToast('Copy failed', 'error');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { state } from '../state/index.js';
|
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';
|
import { updateCardsForBulkMode } from '../components/LoraCard.js';
|
||||||
|
|
||||||
export class BulkManager {
|
export class BulkManager {
|
||||||
@@ -205,13 +205,7 @@ export class BulkManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
await copyToClipboard(loraSyntaxes.join(', '), `Copied ${loraSyntaxes.length} LoRA syntaxes to clipboard`);
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and show the thumbnail strip of selected LoRAs
|
// Create and show the thumbnail strip of selected LoRAs
|
||||||
|
|||||||
@@ -2,6 +2,40 @@ import { state } from '../state/index.js';
|
|||||||
import { resetAndReload } from '../api/loraApi.js';
|
import { resetAndReload } from '../api/loraApi.js';
|
||||||
import { getStorageItem, setStorageItem } from './storageHelpers.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') {
|
export function showToast(message, type = 'info') {
|
||||||
const toast = document.createElement('div');
|
const toast = document.createElement('div');
|
||||||
toast.className = `toast toast-${type}`;
|
toast.className = `toast toast-${type}`;
|
||||||
@@ -108,12 +142,6 @@ export function toggleFolder(tag) {
|
|||||||
resetAndReload();
|
resetAndReload();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function copyTriggerWord(word) {
|
|
||||||
navigator.clipboard.writeText(word).then(() => {
|
|
||||||
showToast('Trigger word copied', 'success');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterByFolder(folderPath) {
|
function filterByFolder(folderPath) {
|
||||||
document.querySelectorAll('.lora-card').forEach(card => {
|
document.querySelectorAll('.lora-card').forEach(card => {
|
||||||
card.style.display = card.dataset.folder === folderPath ? '' : 'none';
|
card.style.display = card.dataset.folder === folderPath ? '' : 'none';
|
||||||
|
|||||||
Reference in New Issue
Block a user