diff --git a/locales/de.json b/locales/de.json index 9f83ccc1..5b64b267 100644 --- a/locales/de.json +++ b/locales/de.json @@ -399,7 +399,8 @@ "autoOrganize": "Auto-Organisierung", "metadata": "Metadaten", "sidecarStorage": "Sidecar-Speicherung", - "proxySettings": "Proxy-Einstellungen" + "proxySettings": "Proxy-Einstellungen", + "sendToWorkflow": "[TODO: Translate] Send to Workflow" }, "nav": { "general": "Allgemein", @@ -453,6 +454,18 @@ "saveFailed": "Übersprungene Pfade konnten nicht gespeichert werden: {message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "[TODO: Translate] Excluded node types", + "placeholder": "[TODO: Translate] Example: CheckpointLoaderSimple, MyCustomNode", + "help": "[TODO: Translate] ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "[TODO: Translate] Excluded node title patterns", + "placeholder": "[TODO: Translate] Example: negative, preview", + "help": "[TODO: Translate] Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "Automatische Backups", "autoEnabledHelp": "Erstellt einmal täglich einen lokalen Schnappschuss und behält die neuesten Schnappschüsse gemäß der Aufbewahrungsrichtlinie.", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embedding zum Workflow hinzugefügt", "embeddingFailed": "Fehler beim Hinzufügen des Embeddings", "promptSent": "Prompt an Workflow gesendet", - "promptFailed": "Fehler beim Senden des Prompts" + "promptFailed": "Fehler beim Senden des Prompts", + "nodeTypeExcluded": "[TODO: Translate] Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "Rezept", @@ -2332,7 +2346,8 @@ "replace": "Ersetzen", "append": "Anhängen", "selectTargetNode": "Zielknoten auswählen", - "sendToAll": "An alle senden" + "sendToAll": "An alle senden", + "excludeNodeType": "[TODO: Translate] Never suggest this node type" }, "exampleImages": { "opened": "Beispielbilder-Ordner geöffnet", diff --git a/locales/en.json b/locales/en.json index 16c3cb11..9b57e510 100644 --- a/locales/en.json +++ b/locales/en.json @@ -399,7 +399,8 @@ "autoOrganize": "Auto-organize", "metadata": "Metadata", "sidecarStorage": "Sidecar Storage", - "proxySettings": "Proxy Settings" + "proxySettings": "Proxy Settings", + "sendToWorkflow": "Send to Workflow" }, "nav": { "general": "General", @@ -453,6 +454,18 @@ "saveFailed": "Unable to save skip paths: {message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "Excluded node types", + "placeholder": "Example: CheckpointLoaderSimple, MyCustomNode", + "help": "ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "Excluded node title patterns", + "placeholder": "Example: negative, preview", + "help": "Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "Automatic backups", "autoEnabledHelp": "Create a local snapshot once per day and keep the latest snapshots according to the retention policy.", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embedding added to workflow", "embeddingFailed": "Failed to add embedding", "promptSent": "Prompt sent to workflow", - "promptFailed": "Failed to send prompt" + "promptFailed": "Failed to send prompt", + "nodeTypeExcluded": "Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "Recipe", @@ -2332,7 +2346,8 @@ "replace": "Replace", "append": "Append", "selectTargetNode": "Select target node", - "sendToAll": "Send to All" + "sendToAll": "Send to All", + "excludeNodeType": "Never suggest this node type" }, "exampleImages": { "opened": "Example images folder opened", diff --git a/locales/es.json b/locales/es.json index 730126a0..8121ce00 100644 --- a/locales/es.json +++ b/locales/es.json @@ -399,7 +399,8 @@ "autoOrganize": "Organización automática", "metadata": "Metadatos", "sidecarStorage": "Almacenamiento de archivos sidecar", - "proxySettings": "Configuración de proxy" + "proxySettings": "Configuración de proxy", + "sendToWorkflow": "[TODO: Translate] Send to Workflow" }, "nav": { "general": "General", @@ -453,6 +454,18 @@ "saveFailed": "No se pudieron guardar las rutas a omitir: {message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "[TODO: Translate] Excluded node types", + "placeholder": "[TODO: Translate] Example: CheckpointLoaderSimple, MyCustomNode", + "help": "[TODO: Translate] ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "[TODO: Translate] Excluded node title patterns", + "placeholder": "[TODO: Translate] Example: negative, preview", + "help": "[TODO: Translate] Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "Copias de seguridad automáticas", "autoEnabledHelp": "Crea una instantánea local una vez al día y conserva las más recientes según la política de retención.", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embedding añadido al workflow", "embeddingFailed": "Error al añadir el embedding", "promptSent": "Prompt enviado al workflow", - "promptFailed": "Error al enviar el prompt" + "promptFailed": "Error al enviar el prompt", + "nodeTypeExcluded": "[TODO: Translate] Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "Receta", @@ -2332,7 +2346,8 @@ "replace": "Reemplazar", "append": "Añadir", "selectTargetNode": "Seleccionar nodo de destino", - "sendToAll": "Enviar a todos" + "sendToAll": "Enviar a todos", + "excludeNodeType": "[TODO: Translate] Never suggest this node type" }, "exampleImages": { "opened": "Carpeta de imágenes de ejemplo abierta", diff --git a/locales/fr.json b/locales/fr.json index 928e4142..784c4fd3 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -399,7 +399,8 @@ "autoOrganize": "Organisation automatique", "metadata": "Métadonnées", "sidecarStorage": "Stockage des fichiers sidecar", - "proxySettings": "Paramètres du proxy" + "proxySettings": "Paramètres du proxy", + "sendToWorkflow": "[TODO: Translate] Send to Workflow" }, "nav": { "general": "Général", @@ -453,6 +454,18 @@ "saveFailed": "Impossible d'enregistrer les chemins à ignorer : {message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "[TODO: Translate] Excluded node types", + "placeholder": "[TODO: Translate] Example: CheckpointLoaderSimple, MyCustomNode", + "help": "[TODO: Translate] ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "[TODO: Translate] Excluded node title patterns", + "placeholder": "[TODO: Translate] Example: negative, preview", + "help": "[TODO: Translate] Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "Sauvegardes automatiques", "autoEnabledHelp": "Crée un instantané local une fois par jour et conserve les plus récents selon la politique de rétention.", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embedding ajouté au workflow", "embeddingFailed": "Échec de l'ajout de l'embedding", "promptSent": "Prompt envoyé au workflow", - "promptFailed": "Échec de l'envoi du prompt" + "promptFailed": "Échec de l'envoi du prompt", + "nodeTypeExcluded": "[TODO: Translate] Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "Recipe", @@ -2332,7 +2346,8 @@ "replace": "Remplacer", "append": "Ajouter", "selectTargetNode": "Sélectionner le nœud cible", - "sendToAll": "Envoyer à tous" + "sendToAll": "Envoyer à tous", + "excludeNodeType": "[TODO: Translate] Never suggest this node type" }, "exampleImages": { "opened": "Dossier d'images d'exemple ouvert", diff --git a/locales/he.json b/locales/he.json index 0cd9aee7..caec9515 100644 --- a/locales/he.json +++ b/locales/he.json @@ -399,7 +399,8 @@ "autoOrganize": "ארגון אוטומטי", "metadata": "מטא-נתונים", "sidecarStorage": "אחסון קובצי לוואי", - "proxySettings": "הגדרות פרוקסי" + "proxySettings": "הגדרות פרוקסי", + "sendToWorkflow": "[TODO: Translate] Send to Workflow" }, "nav": { "general": "כללי", @@ -453,6 +454,18 @@ "saveFailed": "לא ניתן לשמור נתיבי דילוג: {message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "[TODO: Translate] Excluded node types", + "placeholder": "[TODO: Translate] Example: CheckpointLoaderSimple, MyCustomNode", + "help": "[TODO: Translate] ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "[TODO: Translate] Excluded node title patterns", + "placeholder": "[TODO: Translate] Example: negative, preview", + "help": "[TODO: Translate] Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "גיבויים אוטומטיים", "autoEnabledHelp": "יוצר צילום מצב מקומי פעם ביום ושומר את הצילומים האחרונים לפי מדיניות השמירה.", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embedding נוסף ל-workflow", "embeddingFailed": "הוספת Embedding נכשלה", "promptSent": "פרומפט נשלחה ל-workflow", - "promptFailed": "שליחת הפרומפט נכשלה" + "promptFailed": "שליחת הפרומפט נכשלה", + "nodeTypeExcluded": "[TODO: Translate] Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "מתכון", @@ -2332,7 +2346,8 @@ "replace": "החלף", "append": "הוסף", "selectTargetNode": "בחר צומת יעד", - "sendToAll": "שלח לכל" + "sendToAll": "שלח לכל", + "excludeNodeType": "[TODO: Translate] Never suggest this node type" }, "exampleImages": { "opened": "תיקיית תמונות הדוגמה נפתחה", diff --git a/locales/ja.json b/locales/ja.json index 60c60363..7151d4ee 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -399,7 +399,8 @@ "autoOrganize": "自動整理", "metadata": "メタデータ", "sidecarStorage": "サイドカーファイルの保存", - "proxySettings": "プロキシ設定" + "proxySettings": "プロキシ設定", + "sendToWorkflow": "[TODO: Translate] Send to Workflow" }, "nav": { "general": "一般", @@ -453,6 +454,18 @@ "saveFailed": "スキップパスの保存に失敗しました:{message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "[TODO: Translate] Excluded node types", + "placeholder": "[TODO: Translate] Example: CheckpointLoaderSimple, MyCustomNode", + "help": "[TODO: Translate] ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "[TODO: Translate] Excluded node title patterns", + "placeholder": "[TODO: Translate] Example: negative, preview", + "help": "[TODO: Translate] Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "自動バックアップ", "autoEnabledHelp": "1日1回ローカルのスナップショットを作成し、保持ポリシーに従って最新のものを残します。", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embeddingをワークフローに追加しました", "embeddingFailed": "Embeddingの追加に失敗しました", "promptSent": "プロンプトをワークフローに送信しました", - "promptFailed": "プロンプトの送信に失敗しました" + "promptFailed": "プロンプトの送信に失敗しました", + "nodeTypeExcluded": "[TODO: Translate] Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "レシピ", @@ -2332,7 +2346,8 @@ "replace": "置換", "append": "追加", "selectTargetNode": "ターゲットノードを選択", - "sendToAll": "すべてに送信" + "sendToAll": "すべてに送信", + "excludeNodeType": "[TODO: Translate] Never suggest this node type" }, "exampleImages": { "opened": "例画像フォルダが開かれました", diff --git a/locales/ko.json b/locales/ko.json index 529c6bc1..467aa3b6 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -399,7 +399,8 @@ "autoOrganize": "자동 정리", "metadata": "메타데이터", "sidecarStorage": "사이드카 파일 저장", - "proxySettings": "프록시 설정" + "proxySettings": "프록시 설정", + "sendToWorkflow": "[TODO: Translate] Send to Workflow" }, "nav": { "general": "일반", @@ -453,6 +454,18 @@ "saveFailed": "건너뛰기 경로를 저장할 수 없습니다: {message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "[TODO: Translate] Excluded node types", + "placeholder": "[TODO: Translate] Example: CheckpointLoaderSimple, MyCustomNode", + "help": "[TODO: Translate] ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "[TODO: Translate] Excluded node title patterns", + "placeholder": "[TODO: Translate] Example: negative, preview", + "help": "[TODO: Translate] Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "자동 백업", "autoEnabledHelp": "하루에 한 번 로컬 스냅샷을 만들고 보존 정책에 따라 최신 스냅샷을 유지합니다.", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embedding을 워크플로에 추가했습니다", "embeddingFailed": "Embedding 추가 실패", "promptSent": "프롬프트를 워크플로에 보냈습니다", - "promptFailed": "프롬프트 보내기 실패" + "promptFailed": "프롬프트 보내기 실패", + "nodeTypeExcluded": "[TODO: Translate] Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "레시피", @@ -2332,7 +2346,8 @@ "replace": "교체", "append": "추가", "selectTargetNode": "대상 노드 선택", - "sendToAll": "모두에게 전송" + "sendToAll": "모두에게 전송", + "excludeNodeType": "[TODO: Translate] Never suggest this node type" }, "exampleImages": { "opened": "예시 이미지 폴더가 열렸습니다", diff --git a/locales/ru.json b/locales/ru.json index fae25ebc..0b6acab5 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -399,7 +399,8 @@ "autoOrganize": "Автоорганизация", "metadata": "Метаданные", "sidecarStorage": "Хранилище sidecar-файлов", - "proxySettings": "Настройки прокси" + "proxySettings": "Настройки прокси", + "sendToWorkflow": "[TODO: Translate] Send to Workflow" }, "nav": { "general": "Общее", @@ -453,6 +454,18 @@ "saveFailed": "Не удалось сохранить пути для пропуска: {message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "[TODO: Translate] Excluded node types", + "placeholder": "[TODO: Translate] Example: CheckpointLoaderSimple, MyCustomNode", + "help": "[TODO: Translate] ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "[TODO: Translate] Excluded node title patterns", + "placeholder": "[TODO: Translate] Example: negative, preview", + "help": "[TODO: Translate] Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "Автоматические резервные копии", "autoEnabledHelp": "Создаёт локальный снимок раз в день и хранит последние снимки согласно политике хранения.", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embedding добавлен в workflow", "embeddingFailed": "Не удалось добавить embedding", "promptSent": "Промпт отправлен в workflow", - "promptFailed": "Не удалось отправить промпт" + "promptFailed": "Не удалось отправить промпт", + "nodeTypeExcluded": "[TODO: Translate] Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "Рецепт", @@ -2332,7 +2346,8 @@ "replace": "Заменить", "append": "Добавить", "selectTargetNode": "Выберите целевой узел", - "sendToAll": "Отправить во все" + "sendToAll": "Отправить во все", + "excludeNodeType": "[TODO: Translate] Never suggest this node type" }, "exampleImages": { "opened": "Папка с примерами изображений открыта", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 23cd5e12..648e8c08 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -399,7 +399,8 @@ "autoOrganize": "自动整理", "metadata": "元数据", "sidecarStorage": "附属文件存储", - "proxySettings": "代理设置" + "proxySettings": "代理设置", + "sendToWorkflow": "[TODO: Translate] Send to Workflow" }, "nav": { "general": "通用", @@ -453,6 +454,18 @@ "saveFailed": "无法保存跳过路径:{message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "[TODO: Translate] Excluded node types", + "placeholder": "[TODO: Translate] Example: CheckpointLoaderSimple, MyCustomNode", + "help": "[TODO: Translate] ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "[TODO: Translate] Excluded node title patterns", + "placeholder": "[TODO: Translate] Example: negative, preview", + "help": "[TODO: Translate] Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "自动备份", "autoEnabledHelp": "每天创建一次本地快照,并按保留策略保留最新快照。", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embedding 已追加到工作流", "embeddingFailed": "添加 Embedding 失败", "promptSent": "提示词已发送到工作流", - "promptFailed": "提示词发送失败" + "promptFailed": "提示词发送失败", + "nodeTypeExcluded": "[TODO: Translate] Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "配方", @@ -2332,7 +2346,8 @@ "replace": "替换", "append": "追加", "selectTargetNode": "选择目标节点", - "sendToAll": "全部发送" + "sendToAll": "全部发送", + "excludeNodeType": "[TODO: Translate] Never suggest this node type" }, "exampleImages": { "opened": "示例图片文件夹已打开", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 1af2b0f7..f7bff8b6 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -399,7 +399,8 @@ "autoOrganize": "自動整理", "metadata": "中繼資料", "sidecarStorage": "附屬檔案儲存", - "proxySettings": "代理設定" + "proxySettings": "代理設定", + "sendToWorkflow": "[TODO: Translate] Send to Workflow" }, "nav": { "general": "通用", @@ -453,6 +454,18 @@ "saveFailed": "無法儲存跳過路徑:{message}" } }, + "sendToWorkflowExclusions": { + "nodeClasses": { + "label": "[TODO: Translate] Excluded node types", + "placeholder": "[TODO: Translate] Example: CheckpointLoaderSimple, MyCustomNode", + "help": "[TODO: Translate] ComfyUI node class names that never appear as send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + }, + "titlePatterns": { + "label": "[TODO: Translate] Excluded node title patterns", + "placeholder": "[TODO: Translate] Example: negative, preview", + "help": "[TODO: Translate] Nodes whose title contains any of these text fragments (case-insensitive) are hidden from send-to-workflow targets. Separate multiple entries with commas or semicolons. Nodes explicitly marked in ComfyUI (right-click → Mark as) are exempt." + } + }, "backup": { "autoEnabled": "自動備份", "autoEnabledHelp": "每天建立一次本地快照,並依保留政策保留最新快照。", @@ -2322,7 +2335,8 @@ "embeddingAdded": "Embedding 已附加到工作流", "embeddingFailed": "傳送 Embedding 到工作流失敗", "promptSent": "提示詞已發送到工作流", - "promptFailed": "提示詞發送失敗" + "promptFailed": "提示詞發送失敗", + "nodeTypeExcluded": "[TODO: Translate] Node type \"{type}\" excluded from send targets" }, "nodeSelector": { "recipe": "配方", @@ -2332,7 +2346,8 @@ "replace": "取代", "append": "附加", "selectTargetNode": "選擇目標節點", - "sendToAll": "全部傳送" + "sendToAll": "全部傳送", + "excludeNodeType": "[TODO: Translate] Never suggest this node type" }, "exampleImages": { "opened": "範例圖片資料夾已開啟", diff --git a/py/routes/handlers/misc_handlers.py b/py/routes/handlers/misc_handlers.py index 72465e84..81241290 100644 --- a/py/routes/handlers/misc_handlers.py +++ b/py/routes/handlers/misc_handlers.py @@ -617,6 +617,7 @@ class NodeRegistry: "widget_names": widget_names, "mode": node.get("mode"), "marker_role": node.get("marker_role"), + "exclude_send": bool(node.get("exclude_send")), } # ------------------------------------------------------------------ diff --git a/py/services/settings_manager.py b/py/services/settings_manager.py index 6749a570..ba64dee4 100644 --- a/py/services/settings_manager.py +++ b/py/services/settings_manager.py @@ -140,6 +140,10 @@ DEFAULT_SETTINGS: Dict[str, Any] = { "price_check_ttl_hours": 24, "auto_organize_exclusions": [], "metadata_refresh_skip_paths": [], + # Send-to-Workflow target filtering: candidate nodes whose comfy class or + # title matches these lists are hidden from the target selector. Nodes + # explicitly marked via right-click ("Mark as") are exempt. + "send_to_workflow_exclusions": {"node_classes": [], "title_patterns": []}, "skip_previously_downloaded_model_versions": False, "download_skip_base_models": [], # Ordered per-type rules routing downloads to a non-default root by base diff --git a/static/css/components/menu.css b/static/css/components/menu.css index 24833c52..4bb8e25a 100644 --- a/static/css/components/menu.css +++ b/static/css/components/menu.css @@ -290,6 +290,30 @@ text-align: center; } +/* Per-item "exclude this node type" action in the node selector */ +.node-item-exclude { + margin-left: auto; + flex-shrink: 0; + border: none; + background: transparent; + color: var(--text-muted); + cursor: pointer; + font-size: 12px; + padding: 2px 6px; + border-radius: 4px; + opacity: 0; + transition: opacity 0.15s, background-color 0.15s; +} + +.node-item:hover .node-item-exclude { + opacity: 1; +} + +.node-item-exclude:hover { + background: rgba(0, 0, 0, 0.25); + color: var(--text-on-accent); +} + /* Node Selector Header */ .node-selector-header { padding: 10px 15px; diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index 4a5bf565..574c8e30 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -226,6 +226,10 @@ export class SettingsManager { backendSettings?.metadata_refresh_skip_paths ?? defaults.metadata_refresh_skip_paths ); + merged.send_to_workflow_exclusions = this.normalizeSendToWorkflowExclusions( + backendSettings?.send_to_workflow_exclusions ?? defaults.send_to_workflow_exclusions + ); + merged.skip_previously_downloaded_model_versions = backendSettings?.skip_previously_downloaded_model_versions ?? defaults.skip_previously_downloaded_model_versions; @@ -278,6 +282,14 @@ export class SettingsManager { return []; } + normalizeSendToWorkflowExclusions(value) { + const source = value && typeof value === 'object' && !Array.isArray(value) ? value : {}; + return { + node_classes: this.normalizePatternList(source.node_classes), + title_patterns: this.normalizePatternList(source.title_patterns), + }; + } + getAvailableDownloadSkipBaseModels() { // Use dynamic base models if available, fallback to hardcoded const models = getMappableBaseModelsDynamic(); @@ -588,6 +600,18 @@ export class SettingsManager { }); } + for (const inputId of ['sendToWorkflowExcludedClasses', 'sendToWorkflowExcludedTitles']) { + const input = document.getElementById(inputId); + if (input) { + input.addEventListener('keydown', (event) => { + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); + this.saveSendToWorkflowExclusions(); + } + }); + } + } + const downloadSkipBaseModelsContainer = document.getElementById('downloadSkipBaseModelsContainer'); if (downloadSkipBaseModelsContainer) { downloadSkipBaseModelsContainer.addEventListener('change', (event) => { @@ -1175,6 +1199,27 @@ export class SettingsManager { metadataRefreshSkipPathsError.textContent = ''; } + const sendToWorkflowExclusions = this.normalizeSendToWorkflowExclusions( + state.global.settings.send_to_workflow_exclusions + ); + const sendToWorkflowExcludedClassesInput = document.getElementById('sendToWorkflowExcludedClasses'); + if (sendToWorkflowExcludedClassesInput) { + sendToWorkflowExcludedClassesInput.value = sendToWorkflowExclusions.node_classes.join(', '); + } + const sendToWorkflowExcludedClassesError = document.getElementById('sendToWorkflowExcludedClassesError'); + if (sendToWorkflowExcludedClassesError) { + sendToWorkflowExcludedClassesError.textContent = ''; + } + const sendToWorkflowExcludedTitlesInput = document.getElementById('sendToWorkflowExcludedTitles'); + if (sendToWorkflowExcludedTitlesInput) { + sendToWorkflowExcludedTitlesInput.value = sendToWorkflowExclusions.title_patterns.join(', '); + } + const sendToWorkflowExcludedTitlesError = document.getElementById('sendToWorkflowExcludedTitlesError'); + if (sendToWorkflowExcludedTitlesError) { + sendToWorkflowExcludedTitlesError.textContent = ''; + } + this.updateSendToWorkflowSectionVisibility(); + this.renderDownloadSkipBaseModels(); const downloadSkipBaseModelsError = document.getElementById('downloadSkipBaseModelsError'); if (downloadSkipBaseModelsError) { @@ -4269,6 +4314,57 @@ export class SettingsManager { } } + /** + * Show or hide the Send to Workflow settings section. The feature targets a + * live ComfyUI workflow, so the section is hidden in standalone mode. + */ + updateSendToWorkflowSectionVisibility() { + const subsection = document.getElementById('sendToWorkflowSubsection'); + if (subsection) { + subsection.style.display = state.global.settings.standalone_mode ? 'none' : ''; + } + } + + async saveSendToWorkflowExclusions() { + const classesInput = document.getElementById('sendToWorkflowExcludedClasses'); + const titlesInput = document.getElementById('sendToWorkflowExcludedTitles'); + if (!classesInput && !titlesInput) return; + + const normalized = { + node_classes: this.normalizePatternList(classesInput ? classesInput.value : ''), + title_patterns: this.normalizePatternList(titlesInput ? titlesInput.value : ''), + }; + + const current = this.normalizeSendToWorkflowExclusions( + state.global.settings.send_to_workflow_exclusions + ); + const unchanged = + normalized.node_classes.join('|') === current.node_classes.join('|') && + normalized.title_patterns.join('|') === current.title_patterns.join('|'); + if (unchanged) { + return; + } + + try { + await this.saveSetting('send_to_workflow_exclusions', normalized); + if (classesInput) { + classesInput.value = normalized.node_classes.join(', '); + } + if (titlesInput) { + titlesInput.value = normalized.title_patterns.join(', '); + } + + showToast( + 'toast.settings.settingsUpdated', + { setting: translate('settings.sections.sendToWorkflow') }, + 'success' + ); + } catch (error) { + console.error('Failed to save send-to-workflow exclusions:', error); + showToast('toast.settings.settingSaveFailed', { message: error.message }, 'error'); + } + } + async saveAutoOrganizeExclusions() { const input = document.getElementById('autoOrganizeExclusions'); const errorElement = document.getElementById('autoOrganizeExclusionsError'); diff --git a/static/js/state/index.js b/static/js/state/index.js index faa7c377..a21798c4 100644 --- a/static/js/state/index.js +++ b/static/js/state/index.js @@ -63,6 +63,7 @@ const DEFAULT_SETTINGS_BASE = Object.freeze({ price_check_ttl_hours: 24, auto_organize_exclusions: [], metadata_refresh_skip_paths: [], + send_to_workflow_exclusions: { node_classes: [], title_patterns: [] }, skip_previously_downloaded_model_versions: false, download_skip_base_models: [], download_root_rules: { loras: [], checkpoints: [], unet: [], embeddings: [] }, @@ -91,6 +92,7 @@ export function createDefaultSettings() { default_other_roots: {}, download_root_rules: { loras: [], checkpoints: [], unet: [], embeddings: [] }, enabled_other_sub_types: ['vae', 'upscaler', 'text_encoder'], + send_to_workflow_exclusions: { node_classes: [], title_patterns: [] }, // Standalone-only fields populated by GET /api/lm/settings; in plugin // mode the backend omits folder_paths/folder_path_schema and these // defaults apply. diff --git a/static/js/utils/uiHelpers.js b/static/js/utils/uiHelpers.js index 278f70e6..f175a08a 100644 --- a/static/js/utils/uiHelpers.js +++ b/static/js/utils/uiHelpers.js @@ -728,6 +728,48 @@ async function fetchWorkflowRegistry() { } } +/** + * Check whether a registry node is excluded from send-to-workflow targets. + * + * Precedence: + * 1. Per-node exclusion (right-click → "Exclude from Send Targets") always wins. + * 2. An explicit per-node marker ("Mark as →") exempts the node from the + * global exclusion lists — the user pointed at this exact node. + * 3. Global exclusion lists from settings: exact comfy-class matches and + * case-insensitive title substrings. + */ +function isNodeExcludedFromSendTargets(node) { + if (!node) { + return false; + } + if (node.exclude_send === true) { + return true; + } + if (node.marker_role) { + return false; + } + + const exclusions = state?.global?.settings?.send_to_workflow_exclusions; + if (!exclusions || typeof exclusions !== 'object') { + return false; + } + + const nodeClasses = Array.isArray(exclusions.node_classes) ? exclusions.node_classes : []; + const nodeClass = node.comfy_class || node.type_name; + if (nodeClass && nodeClasses.includes(nodeClass)) { + return true; + } + + const titlePatterns = Array.isArray(exclusions.title_patterns) ? exclusions.title_patterns : []; + const title = typeof node.title === 'string' ? node.title.toLowerCase() : ''; + return titlePatterns.some((pattern) => { + if (typeof pattern !== 'string' || !pattern.trim()) { + return false; + } + return title.includes(pattern.trim().toLowerCase()); + }); +} + function filterRegistryNodes(nodes = {}, predicate) { if (typeof nodes !== 'object' || nodes === null) { return {}; @@ -735,6 +777,9 @@ function filterRegistryNodes(nodes = {}, predicate) { return Object.fromEntries( Object.entries(nodes).filter(([, node]) => { + if (isNodeExcludedFromSendTargets(node)) { + return false; + } try { return predicate(node); } catch (error) { @@ -1423,6 +1468,11 @@ function showNodeSelector(nodes, options = {}) { const iconClass = NODE_TYPE_ICONS[node.type] || 'fas fa-question-circle'; const bgColor = node.bgcolor || DEFAULT_NODE_COLOR; const graphLabel = node.graph_name ? ` (${node.graph_name})` : ''; + const nodeClass = node.comfy_class || node.type_name || ''; + const excludeTitle = translate('uiHelpers.nodeSelector.excludeNodeType', {}, 'Never suggest this node type'); + const excludeButton = nodeClass + ? `` + : ''; return `