/** * @Artstich_Example * @name easykit-node-align (ComfyUI Plugin) * @description Node2.0-based professional alignment & real-time node color picker - innovative first support: A must-have plugin for managing node layout and color schemes in ComfyUI. Features a real-time color picker, alignment, 7 preset colors, grayscale/custom modes, and one-click reverse alignment. * @author ArtsticH * @see https://registry.comfy.org/nodes/easykit-node-align * @see https://github.com/ArtsticH/ComfyUI_EasyKitHT_NodeAlignPro * @see https://gitee.com/ArtsticH/ComfyUI_EasyKitHT_NodeAlignPro * @installCommand comfy node install easykit-node-align * @installCommand git clone https://github.com/ArtsticH/ComfyUI_EasyKitHT_NodeAlignPro.git * @installCommand git clone https://gitee.com/ArtsticH/ComfyUI_EasyKitHT_NodeAlignPro.git * @created 2025-04-29 @date 2025-06-15 @lastUpdated 2026-02-09 @version v2.1.17 @license GPL-3.0 * @copyright ©2012-2026, All rights reserved. Freely open to use, modify, and distribute in accordance with the GPL-3.0 license. */ // 本插件全局默认官方语言为中文(cn)。国际友人可自行切换英文,支持'auto'以跟随浏览器/ComfyUI的语言设置 // This plugin uses Chinese (cn) as the global default language. International users can switch to English. The 'auto' setting is also supported to follow the browser or ComfyUI's language preference. // NodeAlignPro 全局国际化配置 // 在 `window.hLanguage` 对象上提供 `t(key)` 辅助函数和 `lang` 属性(支持getter/setter) (function(){ 'use strict'; const data = { NodeAlignPro_Title: {cn: 'Node Align Pro', en: 'Node Align Pro'}, // 头部/菜单/标签文字 Menu_LogoTitle: {cn: '菜单栏 LOGO', en: 'Menu Logo'}, Title_Search: {cn: '搜索节点 Github@ArtsticH...', en: 'Search nodes Github@ArtsticH...'}, Aria_ModeSwitch: {cn: '模式切换', en: 'Mode Switch'}, Aria_Menu: {cn: '菜单', en: 'Menu'}, Label_Align: {cn: '对齐:', en: 'Align:'}, Label_Distribute: {cn: '分布:', en: 'Distribute:'}, Label_Mode: {cn: '模式', en: 'Mode'}, Label_Size: {cn: '尺寸:', en: 'Size:'}, Label_Select: {cn: '选择:', en: 'Select:'}, Aria_SelectMode: {cn: '框选模式', en: 'Selection Mode'}, Aria_GroupMode: {cn: '群组模式', en: 'Group Mode'}, Aria_Separator: {cn: '分隔线', en: 'Separator'}, Aria_DragMove: {cn: '按住拖移位置', en: 'Hold to drag/move'}, ColorPicker_Title: {cn: 'hColorPicker™', en: 'hColorPicker™'}, ColorPicker_HexLabel: {cn: '十六进制:', en: 'Hex:'}, ColorPicker_RGBLabel: {cn: 'RGB:', en: 'RGB:'}, ColorPicker_NodeMode: {cn: '整体色', en: 'Whole Color'}, ColorPicker_NodeModeTip: {cn: '👆双击切换上色模式:', en: '👆Double-click to toggle color mode:'}, Panel_TestTitle: {cn: 'Node 2.0 Alignment Test', en: 'Node 2.0 Alignment Test'}, ColorPicker_HueLabel: {cn: '色相(H):', en: 'Hue (H):'}, ColorPicker_SatLabel: {cn: '饱和(S):', en: 'Sat (S):'}, ColorPicker_BriLabel: {cn: '亮度(B):', en: 'Bri (B):'}, Btn_LeftAlign: {cn: '左对齐', en: 'Left Align'}, Btn_RightAlign: {cn: '右对齐', en: 'Right Align'}, Btn_TopAlign: {cn: '顶对齐', en: 'Top Align'}, Btn_BottomAlign: {cn: '底对齐', en: 'Bottom Align'}, Btn_HCenter: {cn: '水平居中', en: 'H Center'}, Btn_VCenter: {cn: '垂直居中', en: 'V Center'}, Btn_DistH: {cn: '水平等距分布', en: 'Dist H'}, Btn_DistV: {cn: '垂直等距分布', en: 'Dist V'}, Btn_EqualWidth: {cn: '等宽', en: 'Equal Width'}, Btn_EqualHeight: {cn: '等高', en: 'Equal Height'}, Picker_ScreenPickUnsupported: {cn: '浏览器不支持屏幕取色功能,请使用最新版Chrome/Edge浏览器', en: 'Browser does not support EyeDropper API; use latest Chrome/Edge.'}, Pick_NoSelection: {cn: '未选中任何节点', en: 'No nodes selected'}, Setting_ShowOperationLog: {cn: '显示操作日志 (Show Operation Log)', en: 'Show Operation Log'}, Setting_ForceReset: {cn: '⚠强制重置NodeAlignPro插件 (Force reset NodeAlignPro plugin)', en: '⚠Force reset NodeAlignPro plugin'}, hSelKit_DragMode: {cn: '拖拽方式:', en: 'Drag Mode'}, hSelKit_UIscale: {cn: 'UI缩放:', en: 'UI Scale'}, hSelKit_WorkMode: {cn: '工作模式:', en: 'Work Mode'}, hSelKit_DisplayMode: {cn: '显示模式:', en: 'Display Mode'}, Setting_DragMode: {cn: '拖拽方式 (Drag Mode)', en: 'Drag Mode'}, Setting_UIScale: {cn: 'UI缩放 (UI Scale)', en: 'UI Scale'}, Setting_WorkMode: {cn: '工作模式 (Work Mode):可开启新版Node2.0对齐模式', en: 'Work Mode'}, hTooltip_WorkMode: { cn: '工作模式:切换插件工作模式,可开启新版Node2.0对齐模式', en: 'Work Mode: Switch plugin working mode (sync with right-click menu). Enable new Node2.0 alignment mode.' }, Setting_DisplayMode: {cn: '显示模式 (Display Mode)', en: 'Display Mode'}, Setting_AlignBtnColor: {cn: '对齐按钮颜色 (Align Button Color)', en: 'Align Button Color'}, Setting_ToolbarBgColor: {cn: '工具栏背景色 (Toolbar Background Color)', en: 'Toolbar Background Color'}, Setting_ToolbarColor_Auto: {cn: '使用ComfyUI主题配色(Use ComfyUI theme color)', en: 'Use ComfyUI theme color'}, Setting_ToolbarColor_Auto1: {cn: '若开启,将ComfyUI主题配色,下方手动设置的颜色将无效(If enabled, ComfyUI theme color will be used, and manual color setting will be ignored)', en: 'If enabled, ComfyUI theme color will be used, and manual color setting will be ignored'}, Setting_ToolbarOpacity: {cn: '工具栏透明度 (Toolbar Opacity)', en: 'Toolbar Opacity'}, Setting_NewVersionTips: {cn: '新版说明 (New Version Tips)', en: 'New Version Tips'}, Setting_ColorApplyMode: {cn: '上色模式 (Color Apply Mode)', en: 'Color Apply Mode'}, Setting_Language: {cn: 'NodeAlignPro UI语言 (Language)', en: 'NodeAlignPro UI Language'}, // 选项标签文字 Option_Lang_CN: {cn: '中文', en: '中文'}, Option_Lang_EN: {cn: 'English', en: 'English'}, hSelKit_DragSplit: {cn: '解 耦', en: 'Split'}, hSelKit_DragSplit2: {cn: '解 耦(Split)', en: 'Split'}, hSelKit_DragLink: {cn: '联 动', en: 'Link'}, hSelKit_DragLink2: {cn: '联 动(Link)', en: 'Link'}, hSelKit_APBall: {cn: 'AP球', en: 'AP Ball'}, hSelKit_StdBar: {cn: '标 准', en: 'Standard'}, hSelKit_AlignAuto: {cn: '自动', en: 'Auto'}, hSelKit_AlignAuto2: {cn: '自动(Auto)', en: 'Auto'}, hSelKit_ColorBar: {cn: '色 卡', en: 'ColorBar'}, hSelKit_ColorBar2: {cn: '色卡(ColorBar)', en: 'ColorBar'}, hSelKit_AlignBar: {cn: '传统对齐', en: 'AlignStd'}, hSelKit_AlignBar2: {cn: '传统对齐(AlignStd)', en: 'AlignStd'}, hSelKit_Node2: {cn: 'Node2.0', en: 'Node2.0'}, hSelKit_ProBar: {cn: '专 业', en: 'Pro'}, hSelKit_Always: {cn: '常驻显示', en: 'Always'}, hSelKit_Always2: {cn: '常驻显示(Always)', en: 'Always'}, hSelKit_Follow: {cn: '跟随选框', en: 'Follow+'}, hSelKit_Follow2: {cn: '跟随选框(Follow+)', en: 'Follow+'}, hNodePreview_Tips: {cn: '👆双击切换上色模式:', en: '👆Dbl-Click this: '}, Option_Color_Whole: {cn: '整体色', en: 'Whole Color'}, Option_Color_Whole2: {cn: '整体色(Whole)', en: 'Whole Color'}, Option_Color_TitleOnly: {cn: '仅标题', en: 'Title Only'}, Option_Color_TitleOnly2: {cn: '仅标题(Title)', en: 'Title Only'}, hDebug_Tips: { cn: 'v2.1.17新功能:
0. 启用自动主题色:左下角ComfyUI设置>🔥NodeAlignPro>【使用ComfyUI主题配色】
1. 启用新版Node2.0模式:右键菜单>工作模式>【Node2.0】
2. 高级对齐:Alt+对齐按钮:对齐到“反向基准”节点
3. 色卡切换:按Shift、Alt、Ctrl Alt切换不同色卡模式...
________________
4. Switch English:Right-click Menu> Language>【English】
^_^(右键菜单>新版说明隐藏本提示)', en: 'v2.1.17 New Features:
0. Auto-Theme-Color: Bottom left Settings > 🔥NodeAlignPro > 【Use ComfyUI Theme Colors】
1. Node2.0 Mode: Right-click Menu > Work Mode > 【Node2.0】
2. Advanced Alignment: Alt + Align Button: Align to "Reverse Reference" Node
3. Color Palette Switching: Press Shift, Alt, Ctrl+Alt to switch different color palette modes...
________________
4. Switch English:右键菜单>NodeAlignPro UI语言 (Language)>【English】
^_^ (Right-click Menu > NewTips to hide this tip)' }, Aria_ClearColor: {cn: '清除颜色', en: 'Clear Color'}, Aria_Pick: {cn: '取色', en: 'Pick Color'}, Aria_RandomColor: {cn: '随机颜色', en: 'Random Color'}, Aria_ScreenPick: {cn: '屏幕取色', en: 'Screen Pick'}, Aria_Prev: {cn: '上个', en: 'Previous'}, Aria_Next: {cn: '下个', en: 'Next'}, Select_SameColor: {cn: '相同颜色', en: 'Same Color'}, Select_SameName: {cn: '相同名称', en: 'Same Name'}, Select_SameSize: {cn: '相同尺寸', en: 'Same Size'}, Select_DefaultColor: {cn: '默认色', en: 'Default Color'}, Select_Colored: {cn: '已上色', en: 'Colored'}, Select_State: {cn: '选择状态', en: 'Select State'}, Tool_MagicWand: {cn: '魔棒', en: 'Magic Wand'}, Aria_Rename: {cn: '重命名', en: 'Rename'}, hMenu_ResetAll: {cn: '一键重置', en: 'Reset All'}, hMenu_BugReport: {cn: 'bug反馈', en: 'Bug2Issue'}, hMenu_Guide: {cn: '使用教程', en: 'Guide'}, hMenu_NewTips: {cn: '新版说明', en: 'New Tips'}, // 按需添加更多键值 }; const state = { lang: 'cn' }; // 从ComfyUI或浏览器检测实际使用的语言 function detectLang() { try { // 优先尝试ComfyUI应用级别的设置(如果可用) if (window.app && window.app.ui && typeof window.app.ui.settings?.getSettingValue === 'function') { const v = window.app.ui.settings.getSettingValue('language') || window.app.ui.settings.getSettingValue('lang'); if (v && typeof v === 'string') { const lv = v.toLowerCase(); if (lv.startsWith('en')) return 'en'; if (lv.startsWith('zh')) return 'cn'; } } // 回退到浏览器语言设置 const nav = (navigator && (navigator.language || navigator.userLanguage || '')).toLowerCase(); if (nav.startsWith('en')) return 'en'; if (nav.startsWith('zh')) return 'cn'; } catch (e) { /* 忽略 */ } return 'cn'; } // 简单的翻译函数:t(key) function t(key) { if (!key) return ''; const entry = data[key]; if (!entry) return key; const langToUse = (state.lang === 'auto') ? detectLang() : (state.lang || 'cn'); return entry[langToUse] || entry.cn || Object.values(entry)[0]; } // 增强的翻译函数:支持HTML标签和换行符 function tHtml(key) { if (!key) return ''; const entry = data[key]; if (!entry) return key; const langToUse = (state.lang === 'auto') ? detectLang() : (state.lang || 'cn'); let text = entry[langToUse] || entry.cn || Object.values(entry)[0]; if (!text) return ''; // 处理换行符:将\n转换为
text = text.replace(/\n/g, '
'); return text; } // 将API暴露在window对象上 window.hLanguage = { t, tHtml, // 新增支持HTML的翻译函数 data, getLang() { return state.lang; }, setLang(l) { state.lang = (l === 'auto') ? 'auto' : ((l === 'en') ? 'en' : 'cn'); return state.lang; }, detectLang, // 通过键值(data-i18n属性)翻译DOM元素文本内容的辅助函数 applyToDOM(root = document) { try { // 翻译textContent / placeholder / value属性 // 跳过仅需翻译属性(data-i18n-attr)的元素 root.querySelectorAll('[data-i18n]').forEach(el => { if (el.hasAttribute('data-i18n-attr')) return; // 保留SVG/图标内容不翻译 const key = el.getAttribute('data-i18n'); if (!key) return; // 检查是否需要HTML支持 const useHtml = el.hasAttribute('data-i18n-html'); const text = useHtml ? tHtml(key) : t(key); if (!text) return; if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') { el.placeholder = text; if (el.type === 'button' || el.type === 'submit') el.value = text; } else { if (useHtml) { // 使用innerHTML来支持HTML标签 el.innerHTML = text; } else { // 使用textContent保持原有行为 el.textContent = text; } } }); // 通过data-i18n-attr属性翻译aria-label、title等属性 root.querySelectorAll('[data-i18n][data-i18n-attr]').forEach(el => { try { const key = el.getAttribute('data-i18n'); const attrName = el.getAttribute('data-i18n-attr'); if (!key || !attrName) return; // 检查是否需要HTML支持 const useHtml = el.hasAttribute('data-i18n-html'); const text = useHtml ? tHtml(key) : t(key); if (!text) return; el.setAttribute(attrName, text); } catch (ee) { /* 忽略单个元素的翻译错误 */ } }); } catch (e) { console.warn('hLanguage.applyToDOM failed:', e); } } }; // DOM内容加载完成后自动应用翻译,更新所有带data-i18n属性的元素 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { window.hLanguage.applyToDOM(document); }); } else { setTimeout(() => window.hLanguage.applyToDOM(document), 50); } })(); export default window.hLanguage;