feat: add 'Send to ComfyUI' button to ModelModal and RecipeModal

- Add send button to ModelModal header for all model types (LoRA, Checkpoint, Embedding)
- Add send button to RecipeModal header for sending entire recipes
- Style buttons to match existing modal action buttons
- Add translations for all supported languages
This commit is contained in:
Will Miao
2026-03-29 20:35:08 +08:00
parent a4cb51e96c
commit 267082c712
15 changed files with 262 additions and 14 deletions

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "Auf Civitai anzeigen", "viewOnCivitai": "Auf Civitai anzeigen",
"viewOnCivitaiText": "Auf Civitai anzeigen", "viewOnCivitaiText": "Auf Civitai anzeigen",
"viewCreatorProfile": "Ersteller-Profil anzeigen", "viewCreatorProfile": "Ersteller-Profil anzeigen",
"openFileLocation": "Dateispeicherort öffnen" "openFileLocation": "Dateispeicherort öffnen",
"sendToWorkflow": "An ComfyUI senden",
"sendToWorkflowText": "An ComfyUI senden"
}, },
"openFileLocation": { "openFileLocation": {
"success": "Dateispeicherort erfolgreich geöffnet", "success": "Dateispeicherort erfolgreich geöffnet",
@@ -1077,6 +1079,9 @@
"copied": "Pfad in die Zwischenablage kopiert: {{path}}", "copied": "Pfad in die Zwischenablage kopiert: {{path}}",
"clipboardFallback": "Pfad: {{path}}" "clipboardFallback": "Pfad: {{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "Kann nicht an ComfyUI senden: Kein Dateipfad verfügbar"
},
"metadata": { "metadata": {
"version": "Version", "version": "Version",
"fileName": "Dateiname", "fileName": "Dateiname",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "Rezept-Tags erfolgreich aktualisiert", "tagsUpdated": "Rezept-Tags erfolgreich aktualisiert",
"sourceUrlUpdated": "Quell-URL erfolgreich aktualisiert", "sourceUrlUpdated": "Quell-URL erfolgreich aktualisiert",
"noRecipeId": "Keine Rezept-ID verfügbar", "noRecipeId": "Keine Rezept-ID verfügbar",
"sendToWorkflowFailed": "Fehler beim Senden des Rezepts an den Workflow: {message}",
"copyFailed": "Fehler beim Kopieren der Rezept-Syntax: {message}", "copyFailed": "Fehler beim Kopieren der Rezept-Syntax: {message}",
"noMissingLoras": "Keine fehlenden LoRAs zum Herunterladen", "noMissingLoras": "Keine fehlenden LoRAs zum Herunterladen",
"missingLorasInfoFailed": "Fehler beim Abrufen der Informationen für fehlende LoRAs", "missingLorasInfoFailed": "Fehler beim Abrufen der Informationen für fehlende LoRAs",

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "View on Civitai", "viewOnCivitai": "View on Civitai",
"viewOnCivitaiText": "View on Civitai", "viewOnCivitaiText": "View on Civitai",
"viewCreatorProfile": "View Creator Profile", "viewCreatorProfile": "View Creator Profile",
"openFileLocation": "Open File Location" "openFileLocation": "Open File Location",
"sendToWorkflow": "Send to ComfyUI",
"sendToWorkflowText": "Send to ComfyUI"
}, },
"openFileLocation": { "openFileLocation": {
"success": "File location opened successfully", "success": "File location opened successfully",
@@ -1077,6 +1079,9 @@
"copied": "Path copied to clipboard: {{path}}", "copied": "Path copied to clipboard: {{path}}",
"clipboardFallback": "Path: {{path}}" "clipboardFallback": "Path: {{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "Unable to send to ComfyUI: No file path available"
},
"metadata": { "metadata": {
"version": "Version", "version": "Version",
"fileName": "File Name", "fileName": "File Name",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "Recipe tags updated successfully", "tagsUpdated": "Recipe tags updated successfully",
"sourceUrlUpdated": "Source URL updated successfully", "sourceUrlUpdated": "Source URL updated successfully",
"noRecipeId": "No recipe ID available", "noRecipeId": "No recipe ID available",
"sendToWorkflowFailed": "Failed to send recipe to workflow: {message}",
"copyFailed": "Error copying recipe syntax: {message}", "copyFailed": "Error copying recipe syntax: {message}",
"noMissingLoras": "No missing LoRAs to download", "noMissingLoras": "No missing LoRAs to download",
"missingLorasInfoFailed": "Failed to get information for missing LoRAs", "missingLorasInfoFailed": "Failed to get information for missing LoRAs",

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "Ver en Civitai", "viewOnCivitai": "Ver en Civitai",
"viewOnCivitaiText": "Ver en Civitai", "viewOnCivitaiText": "Ver en Civitai",
"viewCreatorProfile": "Ver perfil del creador", "viewCreatorProfile": "Ver perfil del creador",
"openFileLocation": "Abrir ubicación del archivo" "openFileLocation": "Abrir ubicación del archivo",
"sendToWorkflow": "Enviar a ComfyUI",
"sendToWorkflowText": "Enviar a ComfyUI"
}, },
"openFileLocation": { "openFileLocation": {
"success": "Ubicación del archivo abierta exitosamente", "success": "Ubicación del archivo abierta exitosamente",
@@ -1077,6 +1079,9 @@
"copied": "Ruta copiada al portapapeles: {{path}}", "copied": "Ruta copiada al portapapeles: {{path}}",
"clipboardFallback": "Ruta: {{path}}" "clipboardFallback": "Ruta: {{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "No se puede enviar a ComfyUI: no hay ruta de archivo disponible"
},
"metadata": { "metadata": {
"version": "Versión", "version": "Versión",
"fileName": "Nombre de archivo", "fileName": "Nombre de archivo",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "Etiquetas de receta actualizadas exitosamente", "tagsUpdated": "Etiquetas de receta actualizadas exitosamente",
"sourceUrlUpdated": "URL de origen actualizada exitosamente", "sourceUrlUpdated": "URL de origen actualizada exitosamente",
"noRecipeId": "No hay ID de receta disponible", "noRecipeId": "No hay ID de receta disponible",
"sendToWorkflowFailed": "Error al enviar la receta al flujo de trabajo: {message}",
"copyFailed": "Error copiando sintaxis de receta: {message}", "copyFailed": "Error copiando sintaxis de receta: {message}",
"noMissingLoras": "No hay LoRAs faltantes para descargar", "noMissingLoras": "No hay LoRAs faltantes para descargar",
"missingLorasInfoFailed": "Error al obtener información de LoRAs faltantes", "missingLorasInfoFailed": "Error al obtener información de LoRAs faltantes",

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "Voir sur Civitai", "viewOnCivitai": "Voir sur Civitai",
"viewOnCivitaiText": "Voir sur Civitai", "viewOnCivitaiText": "Voir sur Civitai",
"viewCreatorProfile": "Voir le profil du créateur", "viewCreatorProfile": "Voir le profil du créateur",
"openFileLocation": "Ouvrir l'emplacement du fichier" "openFileLocation": "Ouvrir l'emplacement du fichier",
"sendToWorkflow": "Envoyer vers ComfyUI",
"sendToWorkflowText": "Envoyer vers ComfyUI"
}, },
"openFileLocation": { "openFileLocation": {
"success": "Emplacement du fichier ouvert avec succès", "success": "Emplacement du fichier ouvert avec succès",
@@ -1077,6 +1079,9 @@
"copied": "Chemin copié dans le presse-papiers: {{path}}", "copied": "Chemin copié dans le presse-papiers: {{path}}",
"clipboardFallback": "Chemin: {{path}}" "clipboardFallback": "Chemin: {{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "Impossible d'envoyer vers ComfyUI : aucun chemin de fichier disponible"
},
"metadata": { "metadata": {
"version": "Version", "version": "Version",
"fileName": "Nom de fichier", "fileName": "Nom de fichier",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "Tags de la recipe mis à jour avec succès", "tagsUpdated": "Tags de la recipe mis à jour avec succès",
"sourceUrlUpdated": "URL source mise à jour avec succès", "sourceUrlUpdated": "URL source mise à jour avec succès",
"noRecipeId": "Aucun ID de recipe disponible", "noRecipeId": "Aucun ID de recipe disponible",
"sendToWorkflowFailed": "Échec de l'envoi de la recette vers le workflow : {message}",
"copyFailed": "Erreur lors de la copie de la syntaxe de la recipe : {message}", "copyFailed": "Erreur lors de la copie de la syntaxe de la recipe : {message}",
"noMissingLoras": "Aucun LoRA manquant à télécharger", "noMissingLoras": "Aucun LoRA manquant à télécharger",
"missingLorasInfoFailed": "Échec de l'obtention des informations pour les LoRAs manquants", "missingLorasInfoFailed": "Échec de l'obtention des informations pour les LoRAs manquants",

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "הצג ב-Civitai", "viewOnCivitai": "הצג ב-Civitai",
"viewOnCivitaiText": "הצג ב-Civitai", "viewOnCivitaiText": "הצג ב-Civitai",
"viewCreatorProfile": "הצג פרופיל יוצר", "viewCreatorProfile": "הצג פרופיל יוצר",
"openFileLocation": "פתח מיקום קובץ" "openFileLocation": "פתח מיקום קובץ",
"sendToWorkflow": "שלח ל-ComfyUI",
"sendToWorkflowText": "שלח ל-ComfyUI"
}, },
"openFileLocation": { "openFileLocation": {
"success": "מיקום הקובץ נפתח בהצלחה", "success": "מיקום הקובץ נפתח בהצלחה",
@@ -1077,6 +1079,9 @@
"copied": "הנתיב הועתק ללוח העריכה: {{path}}", "copied": "הנתיב הועתק ללוח העריכה: {{path}}",
"clipboardFallback": "נתיב: {{path}}" "clipboardFallback": "נתיב: {{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "לא ניתן לשלוח ל-ComfyUI: אין נתיב קובץ זמין"
},
"metadata": { "metadata": {
"version": "גרסה", "version": "גרסה",
"fileName": "שם קובץ", "fileName": "שם קובץ",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "תגיות המתכון עודכנו בהצלחה", "tagsUpdated": "תגיות המתכון עודכנו בהצלחה",
"sourceUrlUpdated": "כתובת ה-URL המקורית עודכנה בהצלחה", "sourceUrlUpdated": "כתובת ה-URL המקורית עודכנה בהצלחה",
"noRecipeId": "אין מזהה מתכון זמין", "noRecipeId": "אין מזהה מתכון זמין",
"sendToWorkflowFailed": "נכשל שליחת המתכון ל-workflow: {message}",
"copyFailed": "שגיאה בהעתקת תחביר המתכון: {message}", "copyFailed": "שגיאה בהעתקת תחביר המתכון: {message}",
"noMissingLoras": "אין LoRAs חסרים להורדה", "noMissingLoras": "אין LoRAs חסרים להורדה",
"missingLorasInfoFailed": "קבלת מידע עבור LoRAs חסרים נכשלה", "missingLorasInfoFailed": "קבלת מידע עבור LoRAs חסרים נכשלה",

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "Civitaiで表示", "viewOnCivitai": "Civitaiで表示",
"viewOnCivitaiText": "Civitaiで表示", "viewOnCivitaiText": "Civitaiで表示",
"viewCreatorProfile": "作成者プロフィールを表示", "viewCreatorProfile": "作成者プロフィールを表示",
"openFileLocation": "ファイルの場所を開く" "openFileLocation": "ファイルの場所を開く",
"sendToWorkflow": "ComfyUI に送信",
"sendToWorkflowText": "ComfyUI に送信"
}, },
"openFileLocation": { "openFileLocation": {
"success": "ファイルの場所を正常に開きました", "success": "ファイルの場所を正常に開きました",
@@ -1077,6 +1079,9 @@
"copied": "パスをクリップボードにコピーしました: {{path}}", "copied": "パスをクリップボードにコピーしました: {{path}}",
"clipboardFallback": "パス: {{path}}" "clipboardFallback": "パス: {{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "ComfyUI に送信できません:ファイルパスがありません"
},
"metadata": { "metadata": {
"version": "バージョン", "version": "バージョン",
"fileName": "ファイル名", "fileName": "ファイル名",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "レシピタグが正常に更新されました", "tagsUpdated": "レシピタグが正常に更新されました",
"sourceUrlUpdated": "ソースURLが正常に更新されました", "sourceUrlUpdated": "ソースURLが正常に更新されました",
"noRecipeId": "レシピIDが利用できません", "noRecipeId": "レシピIDが利用できません",
"sendToWorkflowFailed": "ワークフローへのレシピ送信に失敗しました:{message}",
"copyFailed": "レシピ構文のコピーエラー:{message}", "copyFailed": "レシピ構文のコピーエラー:{message}",
"noMissingLoras": "ダウンロードする不足LoRAがありません", "noMissingLoras": "ダウンロードする不足LoRAがありません",
"missingLorasInfoFailed": "不足LoRAの情報取得に失敗しました", "missingLorasInfoFailed": "不足LoRAの情報取得に失敗しました",

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "Civitai에서 보기", "viewOnCivitai": "Civitai에서 보기",
"viewOnCivitaiText": "Civitai에서 보기", "viewOnCivitaiText": "Civitai에서 보기",
"viewCreatorProfile": "제작자 프로필 보기", "viewCreatorProfile": "제작자 프로필 보기",
"openFileLocation": "파일 위치 열기" "openFileLocation": "파일 위치 열기",
"sendToWorkflow": "ComfyUI로 보내기",
"sendToWorkflowText": "ComfyUI로 보내기"
}, },
"openFileLocation": { "openFileLocation": {
"success": "파일 위치가 성공적으로 열렸습니다", "success": "파일 위치가 성공적으로 열렸습니다",
@@ -1077,6 +1079,9 @@
"copied": "경로가 클립보드에 복사되었습니다: {{path}}", "copied": "경로가 클립보드에 복사되었습니다: {{path}}",
"clipboardFallback": "경로: {{path}}" "clipboardFallback": "경로: {{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "ComfyUI로 보낼 수 없습니다: 파일 경로가 없습니다"
},
"metadata": { "metadata": {
"version": "버전", "version": "버전",
"fileName": "파일명", "fileName": "파일명",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "레시피 태그가 성공적으로 업데이트되었습니다", "tagsUpdated": "레시피 태그가 성공적으로 업데이트되었습니다",
"sourceUrlUpdated": "소스 URL이 성공적으로 업데이트되었습니다", "sourceUrlUpdated": "소스 URL이 성공적으로 업데이트되었습니다",
"noRecipeId": "사용 가능한 레시피 ID가 없습니다", "noRecipeId": "사용 가능한 레시피 ID가 없습니다",
"sendToWorkflowFailed": "워크플로우에 레시피 보내기 실패: {message}",
"copyFailed": "레시피 문법 복사 오류: {message}", "copyFailed": "레시피 문법 복사 오류: {message}",
"noMissingLoras": "다운로드할 누락된 LoRA가 없습니다", "noMissingLoras": "다운로드할 누락된 LoRA가 없습니다",
"missingLorasInfoFailed": "누락된 LoRA 정보를 가져오는데 실패했습니다", "missingLorasInfoFailed": "누락된 LoRA 정보를 가져오는데 실패했습니다",

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "Посмотреть на Civitai", "viewOnCivitai": "Посмотреть на Civitai",
"viewOnCivitaiText": "Посмотреть на Civitai", "viewOnCivitaiText": "Посмотреть на Civitai",
"viewCreatorProfile": "Посмотреть профиль создателя", "viewCreatorProfile": "Посмотреть профиль создателя",
"openFileLocation": "Открыть расположение файла" "openFileLocation": "Открыть расположение файла",
"sendToWorkflow": "Отправить в ComfyUI",
"sendToWorkflowText": "Отправить в ComfyUI"
}, },
"openFileLocation": { "openFileLocation": {
"success": "Расположение файла успешно открыто", "success": "Расположение файла успешно открыто",
@@ -1077,6 +1079,9 @@
"copied": "Путь скопирован в буфер обмена: {{path}}", "copied": "Путь скопирован в буфер обмена: {{path}}",
"clipboardFallback": "Путь: {{path}}" "clipboardFallback": "Путь: {{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "Невозможно отправить в ComfyUI: путь к файлу недоступен"
},
"metadata": { "metadata": {
"version": "Версия", "version": "Версия",
"fileName": "Имя файла", "fileName": "Имя файла",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "Теги рецепта успешно обновлены", "tagsUpdated": "Теги рецепта успешно обновлены",
"sourceUrlUpdated": "Исходный URL успешно обновлен", "sourceUrlUpdated": "Исходный URL успешно обновлен",
"noRecipeId": "ID рецепта недоступен", "noRecipeId": "ID рецепта недоступен",
"sendToWorkflowFailed": "Не удалось отправить рецепт в рабочий процесс: {message}",
"copyFailed": "Ошибка копирования синтаксиса рецепта: {message}", "copyFailed": "Ошибка копирования синтаксиса рецепта: {message}",
"noMissingLoras": "Нет отсутствующих LoRAs для загрузки", "noMissingLoras": "Нет отсутствующих LoRAs для загрузки",
"missingLorasInfoFailed": "Не удалось получить информацию для отсутствующих LoRAs", "missingLorasInfoFailed": "Не удалось получить информацию для отсутствующих LoRAs",

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "在 Civitai 查看", "viewOnCivitai": "在 Civitai 查看",
"viewOnCivitaiText": "在 Civitai 查看", "viewOnCivitaiText": "在 Civitai 查看",
"viewCreatorProfile": "查看创作者主页", "viewCreatorProfile": "查看创作者主页",
"openFileLocation": "打开文件位置" "openFileLocation": "打开文件位置",
"sendToWorkflow": "发送到 ComfyUI",
"sendToWorkflowText": "发送到 ComfyUI"
}, },
"openFileLocation": { "openFileLocation": {
"success": "文件位置已成功打开", "success": "文件位置已成功打开",
@@ -1077,6 +1079,9 @@
"copied": "路径已复制到剪贴板:{{path}}", "copied": "路径已复制到剪贴板:{{path}}",
"clipboardFallback": "路径:{{path}}" "clipboardFallback": "路径:{{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "无法发送到 ComfyUI没有可用的文件路径"
},
"metadata": { "metadata": {
"version": "版本", "version": "版本",
"fileName": "文件名", "fileName": "文件名",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "配方标签更新成功", "tagsUpdated": "配方标签更新成功",
"sourceUrlUpdated": "来源 URL 更新成功", "sourceUrlUpdated": "来源 URL 更新成功",
"noRecipeId": "无配方 ID", "noRecipeId": "无配方 ID",
"sendToWorkflowFailed": "发送配方到工作流失败:{message}",
"copyFailed": "复制配方语法出错:{message}", "copyFailed": "复制配方语法出错:{message}",
"noMissingLoras": "没有缺失的 LoRA 可下载", "noMissingLoras": "没有缺失的 LoRA 可下载",
"missingLorasInfoFailed": "获取缺失 LoRA 信息失败", "missingLorasInfoFailed": "获取缺失 LoRA 信息失败",

View File

@@ -1069,7 +1069,9 @@
"viewOnCivitai": "在 Civitai 查看", "viewOnCivitai": "在 Civitai 查看",
"viewOnCivitaiText": "在 Civitai 查看", "viewOnCivitaiText": "在 Civitai 查看",
"viewCreatorProfile": "查看創作者個人檔案", "viewCreatorProfile": "查看創作者個人檔案",
"openFileLocation": "開啟檔案位置" "openFileLocation": "開啟檔案位置",
"sendToWorkflow": "傳送到 ComfyUI",
"sendToWorkflowText": "傳送到 ComfyUI"
}, },
"openFileLocation": { "openFileLocation": {
"success": "檔案位置已成功開啟", "success": "檔案位置已成功開啟",
@@ -1077,6 +1079,9 @@
"copied": "路徑已複製到剪貼簿:{{path}}", "copied": "路徑已複製到剪貼簿:{{path}}",
"clipboardFallback": "路徑:{{path}}" "clipboardFallback": "路徑:{{path}}"
}, },
"sendToWorkflow": {
"noFilePath": "無法傳送到 ComfyUI沒有可用的檔案路徑"
},
"metadata": { "metadata": {
"version": "版本", "version": "版本",
"fileName": "檔案名稱", "fileName": "檔案名稱",
@@ -1506,6 +1511,7 @@
"tagsUpdated": "配方標籤已更新", "tagsUpdated": "配方標籤已更新",
"sourceUrlUpdated": "來源網址已更新", "sourceUrlUpdated": "來源網址已更新",
"noRecipeId": "無配方 ID", "noRecipeId": "無配方 ID",
"sendToWorkflowFailed": "傳送配方到工作流失敗:{message}",
"copyFailed": "複製配方語法錯誤:{message}", "copyFailed": "複製配方語法錯誤:{message}",
"noMissingLoras": "無缺少的 LoRA 可下載", "noMissingLoras": "無缺少的 LoRA 可下載",
"missingLorasInfoFailed": "取得缺少 LoRA 資訊失敗", "missingLorasInfoFailed": "取得缺少 LoRA 資訊失敗",

View File

@@ -835,7 +835,8 @@
} }
[data-theme="dark"] .creator-info, [data-theme="dark"] .creator-info,
[data-theme="dark"] .civitai-view { [data-theme="dark"] .civitai-view,
[data-theme="dark"] .modal-send-btn {
background: rgba(255, 255, 255, 0.03); background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--lora-border); border: 1px solid var(--lora-border);
} }
@@ -875,7 +876,8 @@
/* Add hover effect for creator info */ /* Add hover effect for creator info */
.creator-info:hover, .creator-info:hover,
.civitai-view:hover { .civitai-view:hover,
.modal-send-btn:hover {
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1); background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
border-color: var(--lora-accent); border-color: var(--lora-accent);
transform: translateY(-1px); transform: translateY(-1px);
@@ -910,3 +912,42 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
/* Send to ComfyUI Button */
.modal-send-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: rgba(0, 0, 0, 0.03);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: var(--border-radius-sm);
color: var(--text-color);
cursor: pointer;
font-weight: 500;
font-size: 0.9em;
transition: all 0.2s;
}
[data-theme="dark"] .modal-send-btn {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--lora-border);
}
.modal-send-btn:hover {
background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1);
border-color: var(--lora-accent);
transform: translateY(-1px);
}
.modal-send-btn:active {
transform: translateY(0);
}
.modal-send-btn i {
font-size: 14px;
}
.modal-send-btn span {
white-space: nowrap;
}

View File

@@ -565,6 +565,26 @@
color: var(--lora-accent); color: var(--lora-accent);
} }
.send-recipe-btn {
background: none;
border: none;
color: var(--text-color);
opacity: 0.7;
cursor: pointer;
padding: 4px 8px;
border-radius: var(--border-radius-xs);
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.send-recipe-btn:hover {
opacity: 1;
background: var(--lora-surface);
color: var(--lora-accent);
}
#recipeLorasCount { #recipeLorasCount {
font-size: 0.9em; font-size: 0.9em;
color: var(--text-color); color: var(--text-color);

View File

@@ -1,5 +1,5 @@
// Recipe Modal Component // Recipe Modal Component
import { showToast, copyToClipboard, sendModelPathToWorkflow, openCivitaiByMetadata } from '../utils/uiHelpers.js'; import { showToast, copyToClipboard, sendLoraToWorkflow, sendModelPathToWorkflow, openCivitaiByMetadata } from '../utils/uiHelpers.js';
import { translate } from '../utils/i18nHelpers.js'; import { translate } from '../utils/i18nHelpers.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';
@@ -778,6 +778,7 @@ class RecipeModal {
const copyPromptBtn = document.getElementById('copyPromptBtn'); const copyPromptBtn = document.getElementById('copyPromptBtn');
const copyNegativePromptBtn = document.getElementById('copyNegativePromptBtn'); const copyNegativePromptBtn = document.getElementById('copyNegativePromptBtn');
const copyRecipeSyntaxBtn = document.getElementById('copyRecipeSyntaxBtn'); const copyRecipeSyntaxBtn = document.getElementById('copyRecipeSyntaxBtn');
const sendRecipeBtn = document.getElementById('sendRecipeBtn');
if (copyPromptBtn) { if (copyPromptBtn) {
copyPromptBtn.addEventListener('click', () => { copyPromptBtn.addEventListener('click', () => {
@@ -799,6 +800,13 @@ class RecipeModal {
this.fetchAndCopyRecipeSyntax(); this.fetchAndCopyRecipeSyntax();
}); });
} }
if (sendRecipeBtn) {
sendRecipeBtn.addEventListener('click', () => {
// Send recipe to ComfyUI workflow
this.sendRecipeToWorkflow();
});
}
} }
// Fetch recipe syntax from backend and copy to clipboard // Fetch recipe syntax from backend and copy to clipboard
@@ -835,6 +843,35 @@ class RecipeModal {
copyToClipboard(text, successMessage); copyToClipboard(text, successMessage);
} }
// Send recipe to ComfyUI workflow
async sendRecipeToWorkflow() {
if (!this.recipeId) {
showToast('toast.recipes.noRecipeId', {}, 'error');
return;
}
try {
// Fetch recipe syntax from backend
const response = await fetch(`/api/lm/recipe/${this.recipeId}/syntax`);
if (!response.ok) {
throw new Error(`Failed to get recipe syntax: ${response.statusText}`);
}
const data = await response.json();
if (data.success && data.syntax) {
// Send the recipe syntax to ComfyUI workflow
await sendLoraToWorkflow(data.syntax, false, 'recipe');
} else {
throw new Error(data.error || 'No syntax returned from server');
}
} catch (error) {
console.error('Error sending recipe to workflow:', error);
showToast('toast.recipes.sendToWorkflowFailed', { message: error.message }, 'error');
}
}
// Add new method to handle downloading missing LoRAs // Add new method to handle downloading missing LoRAs
async showDownloadMissingLorasModal() { async showDownloadMissingLorasModal() {
console.log("currentRecipe", this.currentRecipe); console.log("currentRecipe", this.currentRecipe);

View File

@@ -1,5 +1,6 @@
import { showToast, openCivitai } from '../../utils/uiHelpers.js'; import { showToast, openCivitai, sendLoraToWorkflow, sendModelPathToWorkflow, buildLoraSyntax } from '../../utils/uiHelpers.js';
import { modalManager } from '../../managers/ModalManager.js'; import { modalManager } from '../../managers/ModalManager.js';
import { MODEL_TYPES } from '../../api/apiConfig.js';
import { import {
toggleShowcase, toggleShowcase,
setupShowcaseScroll, setupShowcaseScroll,
@@ -294,6 +295,17 @@ export async function showModelModal(model, modelType) {
].join('\n') ].join('\n')
: ''; : '';
const headerActionItems = []; const headerActionItems = [];
// Add send to ComfyUI button for all model types
const sendToWorkflowTitle = translate('modals.model.actions.sendToWorkflow', {}, 'Send to ComfyUI');
const sendToWorkflowButton = `
<button class="modal-send-btn" data-action="send-to-workflow" data-model-type="${modelType}" title="${sendToWorkflowTitle}">
<i class="fas fa-paper-plane"></i>
<span>${translate('modals.model.actions.sendToWorkflowText', {}, 'Send to ComfyUI')}</span>
</button>
`.trim();
headerActionItems.push(indentMarkup(sendToWorkflowButton, 20));
if (creatorActionsMarkup) { if (creatorActionsMarkup) {
headerActionItems.push(creatorActionsMarkup); headerActionItems.push(creatorActionsMarkup);
} }
@@ -615,6 +627,14 @@ export async function showModelModal(model, modelType) {
const activeModalElement = document.getElementById(modalId); const activeModalElement = document.getElementById(modalId);
if (activeModalElement) { if (activeModalElement) {
activeModalElement.dataset.filePath = modelWithFullData.file_path || ''; activeModalElement.dataset.filePath = modelWithFullData.file_path || '';
// Store usage_tips for LoRA models
if (modelType === 'loras' && modelWithFullData.usage_tips) {
activeModalElement.dataset.usageTips = modelWithFullData.usage_tips;
}
// Store sub_type for checkpoint models
if (modelType === 'checkpoints' && modelWithFullData.sub_type) {
activeModalElement.dataset.subType = modelWithFullData.sub_type;
}
} }
updateVersionsTabBadge(updateAvailabilityState.hasUpdateAvailable); updateVersionsTabBadge(updateAvailabilityState.hasUpdateAvailable);
const versionsTabController = initVersionsTab({ const versionsTabController = initVersionsTab({
@@ -747,6 +767,9 @@ function setupEventHandlers(filePath, modelType) {
case 'nav-next': case 'nav-next':
handleDirectionalNavigation('next', modelType); handleDirectionalNavigation('next', modelType);
break; break;
case 'send-to-workflow':
handleSendToWorkflow(target, modelType);
break;
} }
} }
@@ -1026,6 +1049,70 @@ async function openFileLocation(filePath) {
} }
} }
async function handleSendToWorkflow(target, modelType) {
const filePath = getModalFilePath();
if (!filePath) {
showToast('modals.model.sendToWorkflow.noFilePath', {}, 'error');
return;
}
// Get the current model data from the modal
const modalElement = document.getElementById('modelModal');
const currentFileName = modalElement?.querySelector('#file-name')?.textContent || '';
if (modelType === 'loras') {
// For LoRA: Build syntax from usage tips and send
const usageTipsData = modalElement?.dataset?.usageTips;
const usageTips = usageTipsData ? JSON.parse(usageTipsData) : {};
const loraSyntax = buildLoraSyntax(currentFileName, usageTips);
await sendLoraToWorkflow(loraSyntax, false, 'lora');
} else if (modelType === 'checkpoints') {
// For Checkpoint: Send model path
const subtype = (modalElement?.dataset?.subType || 'checkpoint').toLowerCase();
const isDiffusionModel = subtype === 'diffusion_model';
const widgetName = isDiffusionModel ? 'unet_name' : 'ckpt_name';
const actionTypeText = translate(
isDiffusionModel ? 'uiHelpers.nodeSelector.diffusionModel' : 'uiHelpers.nodeSelector.checkpoint',
{},
isDiffusionModel ? 'Diffusion Model' : 'Checkpoint'
);
const successMessage = translate(
isDiffusionModel ? 'uiHelpers.workflow.diffusionModelUpdated' : 'uiHelpers.workflow.checkpointUpdated',
{},
isDiffusionModel ? 'Diffusion model updated in workflow' : 'Checkpoint updated in workflow'
);
const failureMessage = translate(
isDiffusionModel ? 'uiHelpers.workflow.diffusionModelFailed' : 'uiHelpers.workflow.checkpointFailed',
{},
isDiffusionModel ? 'Failed to update diffusion model node' : 'Failed to update checkpoint node'
);
const missingNodesMessage = translate(
'uiHelpers.workflow.noMatchingNodes',
{},
'No compatible nodes available in the current workflow'
);
const missingTargetMessage = translate(
'uiHelpers.workflow.noTargetNodeSelected',
{},
'No target node selected'
);
await sendModelPathToWorkflow(filePath, {
widgetName,
collectionType: MODEL_TYPES.CHECKPOINT,
actionTypeText,
successMessage,
failureMessage,
missingNodesMessage,
missingTargetMessage,
});
} else if (modelType === 'embeddings') {
// For Embedding: Send as LoRA syntax (embedding name only)
const embeddingSyntax = `<embed:${currentFileName}:1>`;
await sendLoraToWorkflow(embeddingSyntax, false, 'embedding');
}
}
// Export the model modal API // Export the model modal API
const modelModal = { const modelModal = {
show: showModelModal, show: showModelModal,

View File

@@ -65,6 +65,9 @@
<button class="copy-btn" id="copyRecipeSyntaxBtn" title="Copy Recipe Syntax"> <button class="copy-btn" id="copyRecipeSyntaxBtn" title="Copy Recipe Syntax">
<i class="fas fa-copy"></i> <i class="fas fa-copy"></i>
</button> </button>
<button class="action-btn send-recipe-btn" id="sendRecipeBtn" title="Send Recipe to ComfyUI">
<i class="fas fa-paper-plane"></i>
</button>
</div> </div>
</div> </div>
<div class="recipe-resources-list"> <div class="recipe-resources-list">