feat(embedding): send embedding to workflow + fix copy button format

- Fix copy button on embedding cards to copy 'embedding:folder/name' format
- Add send-embedding-to-workflow for Prompt (LoraManager), Text (LoraManager),
  and CLIPTextEncode nodes, appending embedding code to text content
- Extend workflow registry to register text-capable nodes by comfyClass
  (not generic widget name 'text') to avoid false matches
- Add mode parameter to update_node_widget API/event for append support
- Fix single/bulk context menus: single shows plain 'Send to Workflow',
  bulk collapses submenu into direct action for embeddings (append-only)
This commit is contained in:
Will Miao
2026-06-11 22:41:42 +08:00
parent 84e9fe2dfb
commit d87863b423
9 changed files with 201 additions and 20 deletions

View File

@@ -1,5 +1,5 @@
import { state, getCurrentPageState } from '../state/index.js';
import { showToast, copyToClipboard, sendLoraToWorkflow, buildLoraSyntax, getNSFWLevelName } from '../utils/uiHelpers.js';
import { showToast, copyToClipboard, sendLoraToWorkflow, sendEmbeddingToWorkflow, buildLoraSyntax, getNSFWLevelName } from '../utils/uiHelpers.js';
import { updateCardsForBulkMode } from '../components/shared/ModelCard.js';
import { modalManager } from './ModalManager.js';
import { getModelApiClient, resetAndReload } from '../api/modelApiFactory.js';
@@ -47,7 +47,7 @@ export class BulkManager {
},
[MODEL_TYPES.EMBEDDING]: {
addTags: true,
sendToWorkflow: false,
sendToWorkflow: true,
copyAll: false,
refreshAll: true,
checkUpdates: true,
@@ -504,13 +504,17 @@ export class BulkManager {
}
async sendAllModelsToWorkflow(replaceMode = false) {
if (state.currentPageType !== MODEL_TYPES.LORA) {
showToast('toast.loras.sendOnlyForLoras', {}, 'warning');
if (state.selectedModels.size === 0) {
showToast('toast.models.noModelsSelected', {}, 'warning');
return;
}
if (state.selectedModels.size === 0) {
showToast('toast.loras.noLorasSelected', {}, 'warning');
if (state.currentPageType === MODEL_TYPES.EMBEDDING) {
return this._sendAllEmbeddingsToWorkflow();
}
if (state.currentPageType !== MODEL_TYPES.LORA) {
showToast('toast.loras.sendOnlyForLoras', {}, 'warning');
return;
}
@@ -542,6 +546,28 @@ export class BulkManager {
await sendLoraToWorkflow(loraSyntaxes.join(', '), replaceMode, 'lora');
}
async _sendAllEmbeddingsToWorkflow() {
const embeddingCodes = [];
for (const filepath of state.selectedModels) {
const escapedPath = CSS.escape(filepath);
const card = document.querySelector(`.model-card[data-filepath="${escapedPath}"]`);
if (card) {
const folder = card.dataset.folder || '';
const name = card.dataset.file_name || '';
const code = folder ? `embedding:${folder}/${name}` : `embedding:${name}`;
embeddingCodes.push(code);
}
}
if (embeddingCodes.length === 0) {
showToast('No valid embedding data found', {}, 'warning');
return;
}
const joinedCode = embeddingCodes.join(', ');
await sendEmbeddingToWorkflow(joinedCode, false);
}
showBulkDeleteModal() {
if (state.selectedModels.size === 0) {
showToast('toast.models.noModelsSelected', {}, 'warning');