v2.0.3: 🔥add i18n Language Kit (For english user)

This commit is contained in:
ArtsticH
2026-01-25 02:58:48 +08:00
parent e00a1136f9
commit 7e8bb0abbd
3 changed files with 430 additions and 126 deletions
+191
View File
@@ -0,0 +1,191 @@
/**
* @Artstich_Example
* @name ComfyUI_EasyKitHT_NodeAlignPro (ComfyUI Plugin)
* @description ComfyUI_EasyKitHT_NodeAlignPro is a lightweight ComfyUI node alignment and node coloring tool for refactoring and rewriting the UI based on the open-source projects Comfyui-Align and Comfyui-Nodealigner.
* @author ArtsticH
* @see https://registry.comfy.org/zh/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 @version v2.0.3 @lastUpdated 2026-01-24 @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.
*/
// NodeAlignPro 全局国际化配置
// 在 `window.hLanguage` 对象上提供 `t(key)` 辅助函数和 `lang` 属性(支持getter/setter)
(function(){
'use strict';
const data = {
// 格式:{ cn: '中文文本', en: 'English Text' }
// 保持单行格式以便于快速编辑
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: '显示操作日志', en: 'Show Operation Log'},
Setting_ForceReset: {cn: '⚠强制重置NodeAlignPro插件', en: '⚠Force reset NodeAlignPro plugin'},
Setting_DragMode: {cn: '拖拽方式', en: 'Drag Mode'},
Setting_UIScale: {cn: 'UI缩放', en: 'UI Scale'},
Setting_WorkMode: {cn: '工作模式', en: 'Work Mode'},
Setting_DisplayMode: {cn: '显示模式', en: 'Display Mode'},
Setting_AlignBtnColor: {cn: '对齐按钮颜色', en: 'Align Button Color'},
Setting_ToolbarBgColor: {cn: '工具栏背景色', en: 'Toolbar Background Color'},
Setting_ToolbarOpacity: {cn: '工具栏透明度', en: 'Toolbar Opacity'},
Setting_NewVersionTips: {cn: '新版说明', en: 'New Version Tips'},
Setting_ColorApplyMode: {cn: '上色模式', en: 'Color Apply Mode'},
Setting_Language: {cn: '语言', en: 'Language'},
// 选项标签文字
Option_Lang_CN: {cn: '中文', en: '中文'},
Option_Lang_EN: {cn: 'English', en: 'English'},
Option_Lang_Auto: {cn: '自动(Auto)', en: 'Auto'},
Option_Drag_Split: {cn: '解 耦', en: 'Split'},
Menu_Option_APBall: {cn: 'AP球', en: 'AP Ball'},
Menu_Option_Color: {cn: '色 卡', en: 'ColorBar'},
Menu_Option_Std: {cn: '标 准', en: 'Standard'},
Menu_Option_Pro: {cn: '专 业', en: 'Pro'},
Option_Drag_Link: {cn: '联 动', en: 'Link'},
Option_Work_Align: {cn: '-对 齐-', en: '-Align-'},
Option_Display_Always: {cn: '常驻显示', en: 'Always'},
Option_Display_Follow: {cn: '跟随选框', en: 'Follow+'},
hNodePreview_Tips: {cn: '👆双击切换上色模式:', en: '👆Dbl-Click this: '},
Option_Color_Whole: {cn: '整体色', en: 'Whole Color'},
Option_Color_TitleOnly: {cn: '仅标题', en: 'Title Only'},
Debug_Tips: {cn: 'v2.0.3_rc新版功能:按Shift、Alt、Ctrl Alt切换不同色卡模式...\nAlt+对齐按钮:对齐到“反向基准”节点^_^(右键菜单>【新版说明】隐藏本提示)', en: 'v2.0.3_rc: Use Shift/Alt/Ctrl+Alt to switch Alignment Mode...\nAlt+Align button: align to reversed reference node ^_^ (HideThis: Right-click > New Tips)'},
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'},
Menu_ResetAll: {cn: '一键重置', en: 'Reset All'},
Menu_BugReport: {cn: 'bug反馈', en: 'Bug2Issue'},
Menu_Guide: {cn: '使用教程', en: 'Guide'},
Menu_NewVersion: {cn: '新版说明', en: 'New Tips'},
// 按需添加更多键值
};
// 本插件全局默认光放语言为中文(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.
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];
}
// 将API暴露在window对象上
window.hLanguage = {
t,
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;
const text = 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 {
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;
const text = 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);
}
})();
+110 -77
View File
@@ -461,7 +461,7 @@
setMode(mode) { this.mode = mode; this.saveMode(); return this.mode; }
toggleMode() { this.mode = this.mode === 0 ? 1 : 0; this.saveMode(); return this.mode; }
getMode() { return this.mode; }
getModeText() { return this.mode === 0 ? '仅标题' : '整体色'; }
getModeText() { try { if (window.hLanguage && typeof window.hLanguage.t === 'function') return window.hLanguage.t(this.mode === 0 ? 'Option_Color_TitleOnly' : 'Option_Color_Whole'); } catch (e) {} return this.mode === 0 ? '仅标题' : '整体色'; }
saveMode() { localStorage.setItem('NodeAlignPro_ColorApplyMode', this.mode.toString()); }
loadMode() { const saved = localStorage.getItem('NodeAlignPro_ColorApplyMode'); this.mode = saved !== null ? parseInt(saved) : 1; }
}
@@ -552,9 +552,9 @@
function __hCreateHTML() {
const container = document.createElement('div'); container.id = 'hNodeAlignKit';
container.innerHTML = `
<div class="hDebugInfo" id="debugInfo">v2.0.3_rc新版功能:按Shift、Alt、Ctrl Alt切换不同色卡模式...</br>Alt+对齐按钮:对齐到“反向基准”节点^_^(右键菜单>【新版说明】隐藏本提示)</div>
<div class="hDebugInfo" id="debugInfo" data-i18n="Debug_Tips">v2.0.3_rc新版功能:按Shift、Alt、Ctrl Alt切换不同色卡模式...</br>Alt+对齐按钮:对齐到“反向基准”节点^_^(右键菜单>【新版说明】隐藏本提示)</div>
<div id="h0__hApBar0_apBall">
<button id="hBarLOGO" class="hBtn"><div class="hIcon" id="hBtnY_barLOGO_ApBall" aria-label="LOGO_NodeAlignPro"></div></button></div>
<button id="hBarLOGO" class="hBtn"><div class="hIcon" id="hBtnY_barLOGO_ApBall" data-i18n="Menu_LogoTitle" data-i18n-attr="aria-label"></div></button></div>
<div id="h1__hApBar1_Color">
<button id="hColor1_Red" class="hBtnC hBtnC_7c" data-color-type="default"></button>
<button id="hColor2_Orange" class="hBtnC hBtnC_7c" data-color-type="default"></button>
@@ -563,20 +563,20 @@
<button id="hColor5_Cyan" class="hBtnC hBtnC_7c" data-color-type="default"></button>
<button id="hColor6_Blue" class="hBtnC hBtnC_7c" data-color-type="default"></button>
<button id="hColor7_Purple" class="hBtnC hBtnC_7c" data-color-type="default"></button>
<button id="hClear" class="hBtnC"><div class="hIcon" id="hColorA_Clear" aria-label="清除颜色"></div></button>
<button id="hPick" class="hBtnC"><div class="hIcon" id="hColorB_Pick" aria-label="取色"></div></button>
<button id="hRandom" class="hBtnC"><div class="hIcon" id="hColorC_Random" aria-label="随机颜色"></div></button>
<button id="hZoom" class="hBtnC" title="屏幕取色" style="background-color: rgb(var(--hC_BW1_Black)); border: 1px solid rgb(var(--hC_BW5_LightGray));"><div class="hIcon" id="hColorF_Zoom" aria-label="屏幕取色"></div></button>
<button id="hClear" class="hBtnC"><div class="hIcon" id="hColorA_Clear" data-i18n="Aria_ClearColor" data-i18n-attr="aria-label"></div></button>
<button id="hPick" class="hBtnC"><div class="hIcon" id="hColorB_Pick" data-i18n="Aria_Pick" data-i18n-attr="aria-label"></div></button>
<button id="hRandom" class="hBtnC"><div class="hIcon" id="hColorC_Random" data-i18n="Aria_RandomColor" data-i18n-attr="aria-label"></div></button>
<button id="hZoom" class="hBtnC" title="屏幕取色" style="background-color: rgb(var(--hC_BW1_Black)); border: 1px solid rgb(var(--hC_BW5_LightGray));"><div class="hIcon" id="hColorF_Zoom" data-i18n="Aria_ScreenPick" data-i18n-attr="aria-label"></div></button>
<button id="hColorD_Add" class="hBtnC" style="display: none;"></button>
<button id="hColorE_Love" class="hBtnC" style="display: none;"></button></div>
<div class="Artstich_hColorPicker" id="Artstich_hColorPicker" style="display:none;">
<div class="hColorPicker__hCPr">
<div class="hColorPicker__ValueCopy">
<div class="hCPr__header" id="hCPr__header">
<div id="hBtnY_barLOGO_Color" class="hIcon__LOGO" aria-label="Artstich_hColorPicker"></div>
<div id="hBtnY_barLOGO_Color" class="hIcon__LOGO" data-i18n="ColorPicker_Title" data-i18n-attr="aria-label"></div>
<h2>hColorPicker™</h2></div>
<div class="hCPr__valueG">
<div class="hCPr__valueLabel">十六进制:</div>
<div class="hCPr__valueLabel" data-i18n="ColorPicker_HexLabel">十六进制:</div>
<input type="text" class="value-input" id="hexInput2" value="37377D">
<button class="copy-btn" data-target="hexInput2"> <div class="hCPr__copyIcon"> <div class="hCPr__copyIcon-back"></div> <div class="hCPr__copyIcon-front"></div></div></button></div>
<div class="hCPr__valueG">
@@ -603,7 +603,7 @@
<text class="hPreview__Node-Txt" x="156.28" y="50.41">图像</text>
<circle class="hPreview__Node-Dot hPreview__Node-Output2" cx="189.38" cy="45.62" />
<text class="hPreview__Node-Txt" x="23.03" y="68.91"><tspan letter-spacing="-.02em">vae</tspan></text>
<circle class="hPreview__Node-Dot hPreview__Node-Input2" cx="14.63" cy="64.12" /></svg> <div class="hCPr__hTips"> <div id="hCPr__nodePreviewTips">👆双击切换上色模式:</div> <span class="hCPr__nodeMode" id="hCPr__nodeMode">整体色</span></div></div>
<circle class="hPreview__Node-Dot hPreview__Node-Input2" cx="14.63" cy="64.12" /></svg> <div class="hCPr__hTips"> <div id="hCPr__nodePreviewTips" data-i18n="hNodePreview_Tips">👆双击切换上色模式:</div> <span class="hCPr__nodeMode" id="hCPr__nodeMode">整体色</span></div></div>
<div class="hCPr__hsbBarKit"> <div class="hCPr__hsbBar_sliderG"> <div class="hCPr__hsbBar_sliderLabel">色相(H):</div> <div class="hCPr__hsbBar_hsbBarG">
<div class="slider" id="hCPr__HUE_sliderControl"><div class="hCPr__sliderClip" id="hCPr__HUE_sliderClip"> <div class="hCPr__sliderFill hCPr__hueSliderFill" id="hCPr__HUE_fill"></div></div><div class="slider-touch-area" id="hueTouchArea"></div><div class="slider-handle" id="hueHandle"></div></div>
<input type="text" class="hCPr__sliderValue" id="hCPr__HUE_input" value="240"></div></div> <div class="hCPr__hsbBar_sliderG"> <div class="hCPr__hsbBar_sliderLabel">饱和(S):</div> <div class="hCPr__hsbBar_hsbBarG">
@@ -614,102 +614,102 @@
<div id="h2__hNodeAlignPro" class="hNodeAlignPro">
<div id="hNAP-Title">
<button id="hNAP-Title__LOGO">
<div id="hBtnY_barLOGO_Title" class="hIcon" aria-label="菜单栏LOGO"></div></button>
<div id="hBtnY_barLOGO_Title" class="hIcon" data-i18n="Menu_LogoTitle" data-i18n-attr="aria-label"></div></button>
<div id="hNAP-Title__Content">
<div id="hNAP-Title__Content-Text">Node Align Pro</div>
<button id="hNAP-Title__Content-ModeSwitch"><div id="hBtnV_modeSwitch" class="hIcon" aria-label="模式切换"></div></button>
<button id="hNAP-Title__Context">搜索节点 Github@ArtsticH...</button>
<button id="hNAP-Title__MenuA"><div id="hBtnV_barMenuA" aria-label="菜单"></div></button></div></div>
<div id="hNAP-Title__Content-Text" data-i18n="NodeAlignPro_Title">Node Align Pro</div>
<button id="hNAP-Title__Content-ModeSwitch"><div id="hBtnV_modeSwitch" class="hIcon" data-i18n="Aria_ModeSwitch" data-i18n-attr="aria-label"></div></button>
<button id="hNAP-Title__Context" data-i18n="Title_Search">搜索节点 Github@ArtsticH...</button>
<button id="hNAP-Title__MenuA"><div id="hBtnV_barMenuA" data-i18n="Aria_Menu" data-i18n-attr="aria-label"></div></button></div></div>
<div id="hApBar2__Align">
<button id="hNAP-Title__LOGOAlign" class="hBtn"><div class="hIcon hIcon__LOGO" id="hBtnY_barLOGO" aria-label="预留给" AP球""></div></button>
<div id="hBarDivider01" class="hBarDivider" aria-label="分隔线"></div>
<div id="hAlignBtn_Group1" class="hAlignBtn_Group1"><div class="hAlign-label">对齐:</div>
<button id="hAlignLeft" class="hBtn"><div class="hIcon" id="hBtnA_alignLeft" aria-label="左对齐" aria-label="左对齐"></div></button>
<button id="hAlignCenterV" class="hBtn"><div class="hIcon" id="hBtnB_alignCenterV" aria-label="垂直居中"></div></button>
<button id="hAlignRight" class="hBtn"><div class="hIcon" id="hBtnC_alignRight" aria-label="右对齐"></div></button>
<div id="hBarDivider02" class="hBarDivider" aria-label="分隔线"></div></div>
<button id="hNAP-Title__LOGOAlign" class="hBtn"><div class="hIcon hIcon__LOGO" id="hBtnY_barLOGO" data-i18n="Menu_LogoTitle" data-i18n-attr="aria-label"></div></button>
<div id="hBarDivider01" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div>
<div id="hAlignBtn_Group1" class="hAlignBtn_Group1"><div class="hAlign-label" data-i18n="Label_Align">对齐:</div>
<button id="hAlignLeft" class="hBtn"><div class="hIcon" id="hBtnA_alignLeft" data-i18n="Btn_LeftAlign" data-i18n-attr="aria-label"></div></button>
<button id="hAlignCenterV" class="hBtn"><div class="hIcon" id="hBtnB_alignCenterV" data-i18n="Btn_VCenter" data-i18n-attr="aria-label"></div></button>
<button id="hAlignRight" class="hBtn"><div class="hIcon" id="hBtnC_alignRight" data-i18n="Btn_RightAlign" data-i18n-attr="aria-label"></div></button>
<div id="hBarDivider02" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div></div>
<div id="hAlignBtn_Group2" class="hAlignBtn_Group2">
<button id="hAlignTop" class="hBtn"><div class="hIcon" id="hBtnD_alignTop" aria-label="顶部对齐"></div></button>
<button id="hAlignCenterH" class="hBtn"><div class="hIcon" id="hBtnE_alignCenterH" aria-label="水平居中"></div></button>
<button id="hAlignBottom" class="hBtn"><div class="hIcon" id="hBtnF_alignButton" aria-label="底部对齐"></div></button>
<div id="hBarDivider03" class="hBarDivider" aria-label="分隔线"></div></div>
<div class="hAlignBtn_Group3" class="hAlignBtn_Group3"><div class="hAlign-label2">模式</div>
<button id="hSelectMode" class="hBtn"><div class="hIcon" id="hBtnK_hSelectMode" aria-label="框选模式"></div></button>
<button id="hGroupMode" class="hBtn"><div class="hIcon" id="hBtnL_hGroupMode" aria-label="群组模式"></div></button>
<div id="hBarDivider04" class="hBarDivider" aria-label="分隔线"></div></div>
<div id="hAlignBtn_Group4" class="hAlignBtn_Group4"><div class="hAlign-label">分布:</div>
<button id="hAlignTop" class="hBtn"><div class="hIcon" id="hBtnD_alignTop" data-i18n="Btn_TopAlign" data-i18n-attr="aria-label"></div></button>
<button id="hAlignCenterH" class="hBtn"><div class="hIcon" id="hBtnE_alignCenterH" data-i18n="Btn_HCenter" data-i18n-attr="aria-label"></div></button>
<button id="hAlignBottom" class="hBtn"><div class="hIcon" id="hBtnF_alignButton" data-i18n="Btn_BottomAlign" data-i18n-attr="aria-label"></div></button>
<div id="hBarDivider03" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div></div>
<div class="hAlignBtn_Group3" class="hAlignBtn_Group3"><div class="hAlign-label2" data-i18n="Label_Mode">模式</div>
<button id="hSelectMode" class="hBtn"><div class="hIcon" id="hBtnK_hSelectMode" data-i18n="Aria_SelectMode" data-i18n-attr="aria-label"></div></button>
<button id="hGroupMode" class="hBtn"><div class="hIcon" id="hBtnL_hGroupMode" data-i18n="Aria_GroupMode" data-i18n-attr="aria-label"></div></button>
<div id="hBarDivider04" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div></div>
<div id="hAlignBtn_Group4" class="hAlignBtn_Group4"><div class="hAlign-label" data-i18n="Label_Distribute">分布:</div>
<div id="hCMP-SwitchInput__hDistEven" class="hCMP-SwitchInput">
<div class="hCMP-Switch">
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchUp" class="hCMP-Switch__Btn-SVG" aria-label="上个"></div></button>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchDown" class="hCMP-Switch__Btn-SVG" aria-label="下个"></div></button></div>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchUp" class="hCMP-Switch__Btn-SVG" data-i18n="Aria_Prev" data-i18n-attr="aria-label"></div></button>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchDown" class="hCMP-Switch__Btn-SVG" data-i18n="Aria_Next" data-i18n-attr="aria-label"></div></button></div>
<input type="text" class="hInput" value="128px"></div>
<button id="hDistEvenH" class="hBtn"><div id="hBtnG_distEvenH" class="hIcon" aria-label="水平分布"></div></button>
<button id="hDistEvenV" class="hBtn"><div id="hBtnH_distEvenV" class="hIcon" aria-label="垂直分布"></div></button>
<div id="hBarDivider05" class="hBarDivider" aria-label="分隔线"></div></div>
<div id="hAlignBtn_Group5" class="hAlignBtn_Group5"><div class="hAlign-label">尺寸:</div>
<button id="hDistEvenH" class="hBtn"><div id="hBtnG_distEvenH" class="hIcon" data-i18n="Btn_DistH" data-i18n-attr="aria-label"></div></button>
<button id="hDistEvenV" class="hBtn"><div id="hBtnH_distEvenV" class="hIcon" data-i18n="Btn_DistV" data-i18n-attr="aria-label"></div></button>
<div id="hBarDivider05" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div></div>
<div id="hAlignBtn_Group5" class="hAlignBtn_Group5"><div class="hAlign-label" data-i18n="Label_Size">尺寸:</div>
<div id="hCMP-SwitchInput__hEqual" class="hCMP-SwitchInput">
<div class="hCMP-Switch">
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchUp_1" class="hCMP-Switch__Btn-SVG" aria-label="上个"></div></button>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchDown_1" class="hCMP-Switch__Btn-SVG" aria-label="下个"></div></button></div>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchUp_1" class="hCMP-Switch__Btn-SVG" data-i18n="Aria_Prev" data-i18n-attr="aria-label"></div></button>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchDown_1" class="hCMP-Switch__Btn-SVG" data-i18n="Aria_Next" data-i18n-attr="aria-label"></div></button></div>
<input type="text" class="hInput" value="128px"></div>
<button id="hEqualWidth" class="hBtn"><div id="hBtnI_equalWidth" class="hIcon" aria-label="等宽"></div></button>
<button id="hEqualHeight" class="hBtn"><div id="hBtnJ_equalHeight" class="hIcon" aria-label="等高"></div></button>
<div id="hBarDivider06" class="hBarDivider" aria-label="分隔线"></div></div>
<button id="hBarMove" class="hBtn"><div class="hIcon" id="hBtnV_barMove" aria-label="按住拖移位置"></div></button></div>
<button id="hEqualWidth" class="hBtn"><div id="hBtnI_equalWidth" class="hIcon" data-i18n="Btn_EqualWidth" data-i18n-attr="aria-label"></div></button>
<button id="hEqualHeight" class="hBtn"><div id="hBtnJ_equalHeight" class="hIcon" data-i18n="Btn_EqualHeight" data-i18n-attr="aria-label"></div></button>
<div id="hBarDivider06" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div></div>
<button id="hBarMove" class="hBtn"><div class="hIcon" id="hBtnV_barMove" data-i18n="Aria_DragMove" data-i18n-attr="aria-label"></div></button></div>
<div id="hApBar4__ProH" hApBar2__Align>
<div id="hSelectBtn_Group1" class="hSelectBtn_Group"><div class="hAlign-label">选择:</div>
<button id="hSelectTool1" class="hBtn"><div class="hIcon" id="hBtnR_selectTool1" aria-label="相同颜色"></div></button>
<button id="hSelectTool2" class="hBtn"><div class="hIcon" id="hBtnR_selectTool2" aria-label="相同名称"></div></button>
<button id="hSelectTool3" class="hBtn"><div class="hIcon" id="hBtnR_selectTool3" aria-label="相同尺寸"></div></button></div>
<div id="hBarDivider07" class="hBarDivider" aria-label="分隔线"></div>
<div id="hSelectBtn_Group1" class="hSelectBtn_Group"><div class="hAlign-label" data-i18n="Label_Select">选择:</div>
<button id="hSelectTool1" class="hBtn"><div class="hIcon" id="hBtnR_selectTool1" data-i18n="Select_SameColor" data-i18n-attr="aria-label"></div></button>
<button id="hSelectTool2" class="hBtn"><div class="hIcon" id="hBtnR_selectTool2" data-i18n="Select_SameName" data-i18n-attr="aria-label"></div></button>
<button id="hSelectTool3" class="hBtn"><div class="hIcon" id="hBtnR_selectTool3" data-i18n="Select_SameSize" data-i18n-attr="aria-label"></div></button></div>
<div id="hBarDivider07" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div>
<div id="hSelectBtn_Group2" class="hSelectBtn_Group">
<button id="hSelectTool4" class="hBtn"><div class="hIcon" id="hBtnR_selectTool4" aria-label="默认色"></div></button>
<button id="hSelectTool5" class="hBtn"><div class="hIcon" id="hBtnR_selectTool5" aria-label="已上色"></div></button></div>
<div id="hBarDivider08" class="hBarDivider" aria-label="分隔线"></div>
<button id="hSelectTool4" class="hBtn"><div class="hIcon" id="hBtnR_selectTool4" data-i18n="Select_DefaultColor" data-i18n-attr="aria-label"></div></button>
<button id="hSelectTool5" class="hBtn"><div class="hIcon" id="hBtnR_selectTool5" data-i18n="Select_Colored" data-i18n-attr="aria-label"></div></button></div>
<div id="hBarDivider08" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div>
<div id="hSelectBtn_Group3" class="hSelectBtn_Group">
<button id="hSelectTool0" class="hBtn"><div class="hIcon" id="hBtnR_selectTool0" aria-label="选择状态"></div></button>
<button id="hMagicTool" class="hBtn"><div class="hIcon" id="hBtnT_magicTool" aria-label="魔棒"></div></button></div>
<div id="hBarDivider09" class="hBarDivider" aria-label="分隔线"></div>
<button id="hSelectTool0" class="hBtn"><div class="hIcon" id="hBtnR_selectTool0" aria-label="选择状态"></div></button>
<button id="hSelectTool0" class="hBtn"><div class="hIcon" id="hBtnR_selectTool0" data-i18n="Select_State" data-i18n-attr="aria-label"></div></button>
<button id="hMagicTool" class="hBtn"><div class="hIcon" id="hBtnT_magicTool" data-i18n="Tool_MagicWand" data-i18n-attr="aria-label"></div></button></div>
<div id="hBarDivider09" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div>
<button id="hSelectTool0" class="hBtn"><div class="hIcon" id="hBtnR_selectTool0" data-i18n="Select_State" data-i18n-attr="aria-label"></div></button>
<button class="hMenu-btn" id="hBtn-DispMode__hAlways">常驻</button></div>
<div id="hApBar4__ProH__Rename" hApBar2__Align>
<div class="hAlign-label">命名:</div>
<div class="hAlign-label__RenameAB">前缀</div><div id="hCMP-SwitchInput__RenameA" class="hCMP-SwitchInput">
<div class="hCMP-Switch">
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchUp_2" class="hCMP-Switch__Btn-SVG" aria-label="上个"></div></button>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchDown_2" class="hCMP-Switch__Btn-SVG" aria-label="下个"></div></button></div>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchUp_2" class="hCMP-Switch__Btn-SVG" data-i18n="Aria_Prev" data-i18n-attr="aria-label"></div></button>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchDown_2" class="hCMP-Switch__Btn-SVG" data-i18n="Aria_Next" data-i18n-attr="aria-label"></div></button></div>
<input type="text" class="hInput" value="ArtsticH"></div>
<div class="hAlign-label__RenameAB">后缀</div><div id="hCMP-SwitchInput__RenameB" class="hCMP-SwitchInput">
<div class="hCMP-Switch">
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchUp_3" class="hCMP-Switch__Btn-SVG" aria-label="上个"></div></button>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchDown_3" class="hCMP-Switch__Btn-SVG" aria-label="下个"></div></button></div>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchUp_3" class="hCMP-Switch__Btn-SVG" data-i18n="Aria_Prev" data-i18n-attr="aria-label"></div></button>
<button class="hCMP-Switch__Btn"> <div id="hBtnV_switchDown_3" class="hCMP-Switch__Btn-SVG" data-i18n="Aria_Next" data-i18n-attr="aria-label"></div></button></div>
<input type="text" class="hInput" value="t#time"></div>
<div id="hBarDivider10" class="hBarDivider" aria-label="分隔线"></div>
<button id="hRenameTool" class="hBtn"><div class="hIcon" id="hBtnS_renameTool" aria-label="重命名"></div></button>
<button id="hRenameTool" class="hBtn"><div class="hIcon" id="hBtnS_renameToolA" aria-label="重命名A"></div></button></div></div>
<div id="hBarDivider10" class="hBarDivider" data-i18n="Aria_Separator" data-i18n-attr="aria-label"></div>
<button id="hRenameTool" class="hBtn"><div class="hIcon" id="hBtnS_renameTool" data-i18n="Aria_Rename" data-i18n-attr="aria-label"></div></button>
<button id="hRenameTool" class="hBtn"><div class="hIcon" id="hBtnS_renameToolA" data-i18n="Aria_Rename" data-i18n-attr="aria-label"></div></button></div></div>
<div id="h6__hMenu" style="display: none;">
<div class="hCMP__hSelKit">
<label class="hSelKit-label">拖拽方式</label><div class="hCMP-hSel">
<div class="hMenu-btn" data-target="hCMP-hSel__drag-options">解 耦</div>
<label class="hSelKit-label" data-i18n="Setting_DragMode">拖拽方式</label><div class="hCMP-hSel">
<div class="hMenu-btn" data-target="hCMP-hSel__drag-options" data-i18n="Option_Drag_Split">解 耦</div>
<div class="hCMP-hSel__options" id="hCMP-hSel__drag-options">
<div class="hCMP-hSel__option" data-value="hDragMode1_Split">解 耦</div><div class="hCMP-hSel__option selected" data-value="hDragMode0_Link">联 动</div></div></div></div>
<div class="hCMP-hSel__option" data-value="hDragMode1_Split" data-i18n="Option_Drag_Split">解 耦</div><div class="hCMP-hSel__option selected" data-value="hDragMode0_Link" data-i18n="Option_Drag_Link">联 动</div></div></div></div>
<div class="hCMP__hSelKit">
<label class="hSelKit-label">UI缩放</label><div class="hCMP-hSel">
<div class="hMenu-btn" data-target="hCMP-hSel__scale-options">1x</div>
<div class="hCMP-hSel__options" id="hCMP-hSel__scale-options">
<div class="hCMP-hSel__option" data-value="hUIScale_0_5x">0.5x</div><div class="hCMP-hSel__option" data-value="hUIScale_0_75x">0.75x</div><div class="hCMP-hSel__option selected" data-value="hUIScale_1x">1x</div><div class="hCMP-hSel__option" data-value="hUIScale_1_25x">1.25x</div><div class="hCMP-hSel__option" data-value="hUIScale_1_5x">1.5x</div><div class="hCMP-hSel__option" data-value="hUIScale_2x">2x</div></div></div></div>
<div class="hCMP__hSelKit">
<label class="hSelKit-label">工作模式</label><div class="hCMP-hSel">
<div class="hMenu-btn" data-target="hCMP-hSel__mode-options">对 齐</div>
<label class="hSelKit-label" data-i18n="Setting_WorkMode">工作模式</label><div class="hCMP-hSel">
<div class="hMenu-btn" data-target="hCMP-hSel__mode-options" data-i18n="Option_Work_Align">对 齐</div>
<div class="hCMP-hSel__options" id="hCMP-hSel__mode-options">
<div class="hCMP-hSel__option" data-value="hApBar0_apBall" style="opacity: 0.3; cursor: not-allowed;">AP球</div><div class="hCMP-hSel__option" data-value="hApBar1_Color" style="opacity: 0.3; cursor: not-allowed;">色 卡</div><div class="hCMP-hSel__option selected" data-value="hApBar2_Align">-对 齐-</div><div class="hCMP-hSel__option" data-value="hApBar3_StdH" style="opacity: 0.3; cursor: not-allowed;">标 准</div><div class="hCMP-hSel__option" data-value="hApBar4_ProH" style="opacity: 0.3; cursor: not-allowed;">专 业</div></div></div></div>
<div class="hCMP-hSel__option" data-value="hApBar0_apBall" style="opacity: 0.3; cursor: not-allowed;" data-i18n="Menu_Option_APBall">AP球</div><div class="hCMP-hSel__option" data-value="hApBar1_Color" style="opacity: 0.3; cursor: not-allowed;" data-i18n="Menu_Option_Color">色 卡</div><div class="hCMP-hSel__option selected" data-value="hApBar2_Align" data-i18n="Option_Work_Align">-对 齐-</div><div class="hCMP-hSel__option" data-value="hApBar3_StdH" style="opacity: 0.3; cursor: not-allowed;" data-i18n="Menu_Option_Std">标 准</div><div class="hCMP-hSel__option" data-value="hApBar4_ProH" style="opacity: 0.3; cursor: not-allowed;" data-i18n="Menu_Option_Pro">专 业</div></div></div></div>
<div class="hCMP__hSelKit">
<label class="hSelKit-label">显示模式</label><div class="hCMP-hSel"><div class="hMenu-btn" data-target="hCMP-hSel__display-options">常驻显示</div><div class="hCMP-hSel__options" id="hCMP-hSel__display-options"><div class="hCMP-hSel__option selected" data-value="hDispMode0_Always">常驻显示</div><div class="hCMP-hSel__option" data-value="hDispMode1_Follow">跟随选框</div></div></div></div>
<label class="hSelKit-label" data-i18n="Setting_DisplayMode">显示模式</label><div class="hCMP-hSel"><div class="hMenu-btn" data-target="hCMP-hSel__display-options" data-i18n="Option_Display_Always">常驻显示</div><div class="hCMP-hSel__options" id="hCMP-hSel__display-options"><div class="hCMP-hSel__option selected" data-value="hDispMode0_Always" data-i18n="Option_Display_Always">常驻显示</div><div class="hCMP-hSel__option" data-value="hDispMode1_Follow" data-i18n="Option_Display_Follow">跟随选框</div></div></div></div>
<div>
<button class="hMenu-btn hMenu-btnReset" id="hReset">一键重置</button>
<button class="hMenu-btn" id="hBugReport">bug反馈</button>
<button class="hMenu-btn" id="hGuide">使用教程</button>
<button class="hMenu-btn" id="hBack">新版说明</button></div></div>
<button class="hMenu-btn hMenu-btnReset" id="hReset" data-i18n="Menu_ResetAll">一键重置</button>
<button class="hMenu-btn" id="hBugReport" data-i18n="Menu_BugReport">bug反馈</button>
<button class="hMenu-btn" id="hGuide" data-i18n="Menu_Guide">使用教程</button>
<button class="hMenu-btn" id="hBack" data-i18n="Menu_NewVersion">新版说明</button></div></div>
<input type="color" id="hiddenColorPicker" style="display: none;">
`;
return container;
@@ -1923,7 +1923,7 @@
document.getElementById('hReset').addEventListener('click', __hReset__hNAP_State);
const openLinkAndHideMenu = (url) => { window.open(url, '_blank'); window.__hMgr_MenuHide && window.__hMgr_MenuHide.hideMenu(); };
document.getElementById('hBugReport').addEventListener('click', () => openLinkAndHideMenu('https://github.com/ArtsticH/ComfyUI_EasyKitHT_NodeAlignPro/issues'));
document.getElementById('hGuide').addEventListener('click', () => openLinkAndHideMenu('https://github.com/ArtsticH/ComfyUI_EasyKitHT_NodeAlignPro'));
document.getElementById('hGuide').addEventListener('click', () => openLinkAndHideMenu('https://github.com/ArtsticH/ComfyUI_EasyKitHT_NodeAlignPro#readme'));
const backBtn = document.getElementById('hBack');
if (backBtn) backBtn.addEventListener('click', () => { const debugInfo = document.querySelector('.hDebugInfo'); if (debugInfo) { debugInfo.style.display = debugInfo.style.display === 'none' ? 'block' : 'none'; } window.__hMgr_MenuHide && window.__hMgr_MenuHide.hideMenu(); })
}
@@ -2029,7 +2029,17 @@
hide() { const container = document.getElementById('hNodeAlignKit'); container && (container.style.display = 'none'); }
updateMenuButtonText() {
const menuBtn = document.querySelector('[data-target="hCMP-hSel__display-options"]'); menuBtn && (menuBtn.textContent = this.isPermanent ? '常驻显示' : '跟随选框'); const optionsContainer = document.getElementById('hCMP-hSel__display-options');
const menuBtn = document.querySelector('[data-target="hCMP-hSel__display-options"]');
try {
if (window.hLanguage && typeof window.hLanguage.t === 'function') {
const alwaysText = window.hLanguage.t('Option_Display_Always') || '常驻显示';
const followText = window.hLanguage.t('Option_Display_Follow') || '跟随选框';
menuBtn && (menuBtn.textContent = this.isPermanent ? alwaysText : followText);
} else {
menuBtn && (menuBtn.textContent = this.isPermanent ? '常驻显示' : '跟随选框');
}
} catch (e) { menuBtn && (menuBtn.textContent = this.isPermanent ? '常驻显示' : '跟随选框'); }
const optionsContainer = document.getElementById('hCMP-hSel__display-options');
optionsContainer && optionsContainer.querySelectorAll('.hCMP-hSel__option').forEach(opt => { opt.classList.remove('selected'); (this.isPermanent && opt.getAttribute('data-value') === 'hDispMode0_Always') || (!this.isPermanent && opt.getAttribute('data-value') === 'hDispMode1_Follow') && opt.classList.add('selected'); });
}
@@ -2056,6 +2066,29 @@
// 【== 初始化流程 ==】
const __hInit_hNAP = () => {
window.__hMgr_Log = new __hMgr_Log(); hLog.info('初始化ComfyUI_EasyKitHT_NodeAlignPro插件完毕, 等待DOM加载...'); const container = __hCreateHTML(); document.body.appendChild(container);
try {
window.hLanguage && window.hLanguage.applyToDOM && window.hLanguage.applyToDOM(container);
// 对没有 data-i18n 属性的元素进行额外的定向翻译
if (window.hLanguage) {
const HL = window.hLanguage;
const titleEl = container.querySelector('#hNAP-Title__Content-Text'); titleEl && (titleEl.textContent = HL.t('NodeAlignPro_Title'));
const cpHeader = container.querySelector('#hCPr__header h2'); cpHeader && (cpHeader.textContent = HL.t('ColorPicker_Title'));
const hexInput = container.querySelector('#hexInput2'); if (hexInput && hexInput.previousElementSibling) hexInput.previousElementSibling.textContent = HL.t('ColorPicker_HexLabel');
const rgbLabel = container.querySelector('.hCPr__rgbLabel'); rgbLabel && (rgbLabel.textContent = HL.t('ColorPicker_RGBLabel'));
const nodePreviewTip = container.querySelector('#hCPr__nodePreviewTips'); nodePreviewTip && (nodePreviewTip.textContent = HL.t('ColorPicker_NodeModeTip'));
const nodeMode = container.querySelector('#hCPr__nodeMode'); nodeMode && (nodeMode.textContent = HL.t('ColorPicker_NodeMode'));
try {
// 颜色选择器滑块标签(按顺序)
const sliders = Array.from(container.querySelectorAll('.hCPr__hsbBar_sliderLabel'));
if (sliders.length >= 3) {
sliders[0].textContent = HL.t('ColorPicker_HueLabel');
sliders[1].textContent = HL.t('ColorPicker_SatLabel');
sliders[2].textContent = HL.t('ColorPicker_BriLabel');
}
} catch (ee) { /* 忽略 */ }
try { const logoAp = container.querySelector('#hBtnY_barLOGO'); if (logoAp) logoAp.setAttribute('aria-label', HL.t('Menu_LogoTitle')); } catch (ee) {}
}
} catch (e) { console.warn('hLanguage.applyToDOM 失败:', e); }
setTimeout(() => {
hLog.debug('NodeAlignPro核心组件初始化完毕! 请等待其它插件加载...</br>🔥v2.0.3_rc新版教程文档请点击:右键菜单>【使用教程】查看...');
window.containerController = new __hController_hNAPKit(container), window.__hMgr_PopEl__Position = new __hMgr_PopEl__Position(), window.__hMgr_PopEl__Position.init(container), window.__hMgr_MenuHide = new __hMgr_MenuHide(); __hInit_AllIcons(), __hInit_MainInterface(), __hInit_hMenu__Dropdown(); window.__hColor_Module = new __hColor_Module(); __hInit_ColorPicker(); window.NodeAlignProSettingsManager = new __hMgr_Settings(); // 初始化设置管理器
@@ -2169,11 +2202,11 @@
// 更新取色器显示
const nodeModeText = document.getElementById('hCPr__nodeMode');
if (nodeModeText) {
nodeModeText.textContent = mode === 0 ? '仅标题' : '整体色';
try { nodeModeText.textContent = (window.hLanguage && typeof window.hLanguage.t === 'function') ? window.hLanguage.t(mode === 0 ? 'Option_Color_TitleOnly' : 'Option_Color_Whole') : (mode === 0 ? '仅标题' : '整体色'); } catch (e) { nodeModeText.textContent = mode === 0 ? '仅标题' : '整体色'; }
nodeModeText.style.backgroundColor = mode === 0 ? 'rgb(var(--hC_BW3_DeepGray))' : 'rgb(var(--hC_CPr0__PurpleStd))';
}
hLog.info('--@hSetting', `上色模式已设置为: ${mode === 0 ? '仅标题' : '整体色'}`);
try { hLog.info('--@hSetting', `上色模式已设置为: ${(window.hLanguage && typeof window.hLanguage.t === 'function') ? window.hLanguage.t(mode === 0 ? 'Option_Color_TitleOnly' : 'Option_Color_Whole') : (mode === 0 ? '仅标题' : '整体色')}`); } catch (e) { hLog.info('--@hSetting', `上色模式已设置为: ${mode === 0 ? '仅标题' : '整体色'}`); }
}
}
+129 -49
View File
@@ -1,19 +1,56 @@
/**
* @Artstich_Example
* @name ComfyUI_EasyKitHT_NodeAlignPro (ComfyUI Plugin)
* @description ComfyUI_EasyKitHT_NodeAlignPro is a lightweight ComfyUI node alignment and node coloring tool for refactoring and rewriting the UI based on the open-source projects Comfyui-Align and Comfyui-Nodealigner.
* @author ArtsticH
* @see https://registry.comfy.org/zh/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 @version v2.0.3 @lastUpdated 2026-01-24 @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.
*/
import { app } from "../../scripts/app.js";
// 简单的国际化助手,当hLanguage未就绪时回退到提供的中文文本
function h_i18n(key, fallback) {
try { return window.hLanguage && typeof window.hLanguage.t === 'function' ? window.hLanguage.t(key) : (fallback || key); } catch (e) { return fallback || key; }
}
// 辅助函数:安全地调用存在的方法,否则存储待处理的值
function __hNodeAlignPro_safeCall(target, methodName, keyForPending, value) {
try {
if (target && typeof target[methodName] === 'function') {
target[methodName](value);
return true;
}
} catch (e) { console.warn(`[NodeAlignPro 设置模块] 调用 ${methodName} 失败:`, e); }
// 存储待处理值供主模块稍后获取
try {
window.__hNodeAlignPro_pendingSettings = window.__hNodeAlignPro_pendingSettings || {};
window.__hNodeAlignPro_pendingSettings[keyForPending] = value;
console.info(`[NodeAlignPro 设置模块] 挂起设置 ${keyForPending}=${value},等待主模块处理`);
} catch (e) { /* 忽略 */ }
return false;
}
const NodeAlignProSettings = [
{
id: "hNodeAlignPro.ShowOperationLog", name: "显示操作日志", type: "boolean",
id: "hNodeAlignPro.ShowOperationLog", name: h_i18n('Setting_ShowOperationLog','显示操作日志'), type: "boolean",
defaultValue: false,
category: ["🔥 NodeAlignPro", "Z开发人员选项", "显示操作日志"],
tooltip: "开启后,插件操作日志将输出到页面左上角,方便进阶用户调试",
category: ["🔥 NodeAlignPro", "Z开发人员选项", h_i18n('Setting_ShowOperationLog','显示操作日志')],
tooltip: h_i18n('Setting_ShowOperationLog','开启后,插件操作日志将输出到页面左上角,方便进阶用户调试'),
onChange: (value) => { try { if (window.NodeAlignProSettingsManager) { window.NodeAlignProSettingsManager.setShowOperationLog(value); } } catch (error) { console.error('设置操作日志显示失败:', error); } }
},
{
id: "hNodeAlignPro.hReset", name: "⚠强制重置NodeAlignPro插件", type: "boolean",
id: "hNodeAlignPro.hReset", name: h_i18n('Setting_ForceReset','⚠强制重置NodeAlignPro插件'), type: "boolean",
defaultValue: false,
category: ["🔥 NodeAlignPro", "Z开发人员选项", "⚠强制重置NodeAlignPro插件"],
tooltip: "⚠此操作会强制刷新页面,请务必先保存工作流! 开启后会强制重建NodeAlignPro插件,仅在插件异常时使用! ",
category: ["🔥 NodeAlignPro", "Z开发人员选项", h_i18n('Setting_ForceReset','⚠强制重置NodeAlignPro插件')],
tooltip: h_i18n('Setting_ForceReset','⚠此操作会强制刷新页面,请务必先保存工作流! 开启后会强制重建NodeAlignPro插件,仅在插件异常时使用! '),
onChange: (value) => {
if (value) try {
if (typeof __hReset__hNAP_State === 'function') __hReset__hNAP_State(); else resetNodeAlignProManually(); // 1. 调用核心文件中的重置函数。如果核心重置函数不存在,执行手动重置
@@ -21,7 +58,7 @@ const NodeAlignProSettings = [
setTimeout(() => { // 4. 刷新页面以完全重置插件
const isResetEnabled = app.ui?.settings?.getSettingValue?.("hNodeAlignPro.hReset"); // 检查开关是否处于关闭状态
if (isResetEnabled === true) location.reload(); // 只有当开关处于开启状态时才刷新
else { if (hLog) hLog.info('--@hSetting', '重置操作已完成,开关已自动关闭,无需刷新页面'); console.log('重置操作已完成,开关已自动关闭,无需刷新页面'); } // 如果开关已关闭,则不刷新,仅记录日志
else { if (hLog) hLog.info('--@hSetting', '🔥NodeAlignPro已重置!直接可用,无需重复刷新页面'); console.log('🔥NodeAlignPro已重置!直接可用,无需重复刷新页面'); } // 如果开关已关闭,则不刷新,仅记录日志
}, 500);
if (hLog) hLog.info('--@hSetting', '插件已通过设置菜单强制重置,页面将重新加载...');
} catch (error) { console.error('重置插件失败:', error); if (hLog) hLog.error('--@hSetting', '重置失败:', error); }
@@ -31,15 +68,28 @@ const NodeAlignProSettings = [
// { id: "hNodeAlignPro.button_test", name: "测试", type: "input", defaultValue: "测试文本", category: ["🔥 NodeAlignPro", "NodeAlignPro预置颜色", "测试"], onChange: (newVal) => { } },
{
id: "hNodeAlignPro.linkMode", name: "拖拽方式", type: "combo",
options: [{ value: "hDragMode1_Split", text: "解 耦(默认)" }, { value: "hDragMode0_Link", text: "联 动" }],
id: "hNodeAlignPro.linkMode", name: h_i18n('Setting_DragMode','拖拽方式'), type: "combo",
options: [{ value: "hDragMode1_Split", text: h_i18n('Option_Drag_Split','解 耦') }, { value: "hDragMode0_Link", text: h_i18n('Option_Drag_Link','联 动') }],
defaultValue: "hDragMode1_Split",
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", "拖拽方式"],
tooltip: "切换是否联动[运行/Action]按钮到插件面板(与插件右键菜单设置同步)",
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", h_i18n('Setting_DragMode','拖拽方式')],
tooltip: h_i18n('Setting_DragMode','切换是否联动[运行/Action]按钮到插件面板(与插件右键菜单设置同步)'),
onChange: (value) => {
try {
const mode = value === "hDragMode0_Link" ? 1 : 0; // 直接调用全局的处理函数。或者通过设置管理器。或者直接调用联动管理器
(typeof __hMenu_Selection === 'function' && __hMenu_Selection(value)) || (window.NodeAlignProSettingsManager && window.NodeAlignProSettingsManager.setLinkMode(mode)) || (window.__hMgr_ACbar && window.__hMgr_ACbar.setLinkMode(mode)) || console.error('无法找到拖拽方式处理函数');
const mode = value === "hDragMode0_Link" ? 1 : 0;
// 首先尝试显式的全局处理函数
if (typeof __hMenu_Selection === 'function') {
try { __hMenu_Selection(value); return; } catch (e) { console.warn('调用 __hMenu_Selection 失败:', e); }
}
// 如果可用,尝试设置管理器方法
if (window.NodeAlignProSettingsManager && typeof window.NodeAlignProSettingsManager.setLinkMode === 'function') {
try { window.NodeAlignProSettingsManager.setLinkMode(mode); return; } catch (e) { console.warn('调用 NodeAlignProSettingsManager.setLinkMode 失败:', e); }
}
// 尝试ACbar管理器
if (window.__hMgr_ACbar && typeof window.__hMgr_ACbar.setLinkMode === 'function') {
try { window.__hMgr_ACbar.setLinkMode(mode); return; } catch (e) { console.warn('调用 __hMgr_ACbar.setLinkMode 失败:', e); }
}
// 回退:存储待处理值供主模块稍后获取
__hNodeAlignPro_safeCall(null, null, 'linkMode', mode);
} catch (error) { console.error('设置拖拽方式失败:', error); }
}
},
@@ -50,7 +100,7 @@ const NodeAlignProSettings = [
defaultValue: "hUIScale_1x",
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", "UI缩放"],
tooltip: "调整插件UI缩放比例(与插件右键菜单设置同步)",
onChange: (value) => { try { if (window.NodeAlignProSettingsManager) { window.NodeAlignProSettingsManager.setUIScale(value); } } catch (error) { console.error('设置UI缩放失败:', error); } }
onChange: (value) => { try { if (window.NodeAlignProSettingsManager && typeof window.NodeAlignProSettingsManager.setUIScale === 'function') { window.NodeAlignProSettingsManager.setUIScale(value); } else __hNodeAlignPro_safeCall(null, null, 'uiScale', value); } catch (error) { console.error('设置UI缩放失败:', error); } }
},
/* {
id: "hNodeAlignPro.UIScale_v2", name: "UI缩放v2", type: "combo",
@@ -74,63 +124,91 @@ const NodeAlignProSettings = [
},
}, */
{
id: "hNodeAlignPro.WorkMode", name: "工作模式", type: "combo",
options: [{ value: "hApBar2_Align", text: "-对 齐-" }],
id: "hNodeAlignPro.WorkMode", name: h_i18n('Setting_WorkMode','工作模式'), type: "combo",
options: [{ value: "hApBar2_Align", text: h_i18n('Option_Work_Align','-对 齐-') }],
defaultValue: "hApBar2_Align",
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", "工作模式"],
tooltip: "切换插件工作模式(与插件右键菜单设置同步)",
onChange: (value) => { try { if (window.NodeAlignProSettingsManager) { window.NodeAlignProSettingsManager.setWorkMode(value); } } catch (error) { console.error('设置工作模式失败:', error); } }
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", h_i18n('Setting_WorkMode','工作模式')],
tooltip: h_i18n('Setting_WorkMode','切换插件工作模式(与插件右键菜单设置同步)'),
onChange: (value) => { try { if (window.NodeAlignProSettingsManager && typeof window.NodeAlignProSettingsManager.setWorkMode === 'function') { window.NodeAlignProSettingsManager.setWorkMode(value); } else __hNodeAlignPro_safeCall(null, null, 'workMode', value); } catch (error) { console.error('设置工作模式失败:', error); } }
},
{
id: "hNodeAlignPro.DisplayMode", name: "显示模式", type: "combo",
options: [{ value: "hDispMode0_Always", text: "常驻显示(默认)" }, { value: "hDispMode1_Follow", text: "跟随选框" }],
id: "hNodeAlignPro.DisplayMode", name: h_i18n('Setting_DisplayMode','显示模式'), type: "combo",
options: [{ value: "hDispMode0_Always", text: h_i18n('Option_Display_Always','常驻显示') }, { value: "hDispMode1_Follow", text: h_i18n('Option_Display_Follow','跟随选框') }],
defaultValue: "hDispMode0_Always",
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", "显示模式"],
tooltip: "切换插件面板的显示模式(与插件右键菜单设置同步)",
onChange: (value) => { try { if (window.NodeAlignProSettingsManager) { window.NodeAlignProSettingsManager.setDisplayMode(value); } } catch (error) { console.error('设置显示模式失败:', error); } }
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", h_i18n('Setting_DisplayMode','显示模式')],
tooltip: h_i18n('Setting_DisplayMode','切换插件面板的显示模式(与插件右键菜单设置同步)'),
onChange: (value) => { try { if (window.NodeAlignProSettingsManager && typeof window.NodeAlignProSettingsManager.setDisplayMode === 'function') { window.NodeAlignProSettingsManager.setDisplayMode(value); } else __hNodeAlignPro_safeCall(null, null, 'displayMode', value); } catch (error) { console.error('设置显示模式失败:', error); } }
},
// 语言选择(优先级高于浏览器语言),切换即刻生效
{
id: "hNodeAlignPro.Language", name: h_i18n('Setting_Language','语言'), type: "combo",
options: [
{ value: 'auto', text: h_i18n('Option_Lang_Auto','自动(Auto)') },
{ value: 'cn', text: h_i18n('Option_Lang_CN','中文') },
{ value: 'en', text: h_i18n('Option_Lang_EN','English') }
],
defaultValue: 'cn',
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", h_i18n('Setting_Language','语言')],
tooltip: h_i18n('Setting_Language','选择插件界面语言(优先于浏览器语言设置)'),
onChange: (value) => {
try {
if (window.hLanguage && typeof window.hLanguage.setLang === 'function') {
// 'auto'表示不强制特定语言;遵循浏览器/ComfyUI设置
if (value === 'auto') window.hLanguage.setLang('auto'); else window.hLanguage.setLang(value);
// 立即应用到文档和现有插件容器
try { window.hLanguage.applyToDOM(document); } catch (e) { console.warn('应用语言到DOM失败:', e); }
// 如果存在插件容器,也应用到该容器
try { const c = document.getElementById('hNodeAlignKit'); if (c && window.hLanguage) window.hLanguage.applyToDOM(c); } catch (e) { /* 忽略 */ }
console.info('[NodeAlignPro 设置] 语言已切换为', window.hLanguage.getLang());
} else {
__hNodeAlignPro_safeCall(null, null, 'language', value);
}
} catch (error) { console.error('设置语言失败:', error); }
}
},
{
id: "hNodeAlignPro.hColor_SVG", name: "对齐按钮颜色", type: "color",
id: "hNodeAlignPro.hColor_SVG", name: h_i18n('Setting_AlignBtnColor','对齐按钮颜色'), type: "color",
defaultValue: "6B6B70",
category: ["🔥 NodeAlignPro", "NodeAlignPro预置颜色", "对齐按钮颜色"],
tooltip: "控制对齐按钮颜色",
onChange: (newVal) => { try { if (window.NodeAlignProSettingsManager) { window.NodeAlignProSettingsManager.setAlignButtonColor(newVal); } } catch (error) { console.error('设置对齐按钮颜色失败:', error); } }
category: ["🔥 NodeAlignPro", "NodeAlignPro预置颜色", h_i18n('Setting_AlignBtnColor','对齐按钮颜色')],
tooltip: h_i18n('Setting_AlignBtnColor','控制对齐按钮颜色'),
onChange: (newVal) => { try { if (window.NodeAlignProSettingsManager && typeof window.NodeAlignProSettingsManager.setAlignButtonColor === 'function') { window.NodeAlignProSettingsManager.setAlignButtonColor(newVal); } else __hNodeAlignPro_safeCall(null, null, 'alignButtonColor', newVal); } catch (error) { console.error('设置对齐按钮颜色失败:', error); } }
},
{
id: "hNodeAlignPro.hColor_bg", name: "工具栏背景色", type: "color",
id: "hNodeAlignPro.hColor_bg", name: h_i18n('Setting_ToolbarBgColor','工具栏背景色'), type: "color",
defaultValue: "18181B",
category: ["🔥 NodeAlignPro", "NodeAlignPro预置颜色", "工具栏背景色"],
tooltip: "控制对齐组件的背景色",
onChange: (newVal) => { try { if (window.NodeAlignProSettingsManager) { window.NodeAlignProSettingsManager.setToolbarBgColor(newVal); } } catch (error) { console.error('设置工具栏背景色失败:', error); } }
category: ["🔥 NodeAlignPro", "NodeAlignPro预置颜色", h_i18n('Setting_ToolbarBgColor','工具栏背景色')],
tooltip: h_i18n('Setting_ToolbarBgColor','控制对齐组件的背景色'),
onChange: (newVal) => { try { if (window.NodeAlignProSettingsManager && typeof window.NodeAlignProSettingsManager.setToolbarBgColor === 'function') { window.NodeAlignProSettingsManager.setToolbarBgColor(newVal); } else __hNodeAlignPro_safeCall(null, null, 'toolbarBgColor', newVal); } catch (error) { console.error('设置工具栏背景色失败:', error); } }
},
{
id: "hNodeAlignPro.hOpacity", name: "工具栏透明度", type: "slider",
id: "hNodeAlignPro.hOpacity", name: h_i18n('Setting_ToolbarOpacity','工具栏透明度'), type: "slider",
defaultValue: 95,
attrs: { min: 0, max: 100, step: 1 },
category: ["🔥 NodeAlignPro", "NodeAlignPro预置颜色", "工具栏透明度"],
tooltip: "控制对齐组件的背景透明度",
onChange: (newVal) => { try { if (window.NodeAlignProSettingsManager) { window.NodeAlignProSettingsManager.setToolbarOpacity(newVal); } } catch (error) { console.error('设置工具栏透明度失败:', error); } }
category: ["🔥 NodeAlignPro", "NodeAlignPro预置颜色", h_i18n('Setting_ToolbarOpacity','工具栏透明度')],
tooltip: h_i18n('Setting_ToolbarOpacity','控制对齐组件的背景透明度'),
onChange: (newVal) => { try { if (window.NodeAlignProSettingsManager && typeof window.NodeAlignProSettingsManager.setToolbarOpacity === 'function') { window.NodeAlignProSettingsManager.setToolbarOpacity(newVal); } else __hNodeAlignPro_safeCall(null, null, 'toolbarOpacity', newVal); } catch (error) { console.error('设置工具栏透明度失败:', error); } }
},
{
id: "hNodeAlignPro.NewVersionTips", name: "新版说明", type: "boolean",
id: "hNodeAlignPro.NewVersionTips", name: h_i18n('Setting_NewVersionTips','新版说明'), type: "boolean",
defaultValue: true,
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", "新版说明"],
tooltip: "v2.0.3_rc新版功能:按Shift、Alt、Ctrl Alt切换不同色卡模式... Alt+对齐按钮:对齐到“反向基准”节点^_^",
onChange: (value) => { try { if (window.NodeAlignProSettingsManager) { window.NodeAlignProSettingsManager.setNewVersionTips(value); } } catch (error) { console.error('设置新版说明失败:', error); } }
category: ["🔥 NodeAlignPro", "NodeAlignPro基本设置", h_i18n('Setting_NewVersionTips','新版说明')],
tooltip: h_i18n('Setting_NewVersionTips','v2.0.3_rc新版功能:按Shift、Alt、Ctrl Alt切换不同色卡模式... Alt+对齐按钮:对齐到“反向基准”节点^_^'),
onChange: (value) => { try { if (window.NodeAlignProSettingsManager && typeof window.NodeAlignProSettingsManager.setNewVersionTips === 'function') { window.NodeAlignProSettingsManager.setNewVersionTips(value); } else __hNodeAlignPro_safeCall(null, null, 'newVersionTips', value); } catch (error) { console.error('设置新版说明失败:', error); } }
},
{
id: "hNodeAlignPro.ColorApplyMode", name: "上色模式", type: "combo",
options: [ { value: "1", text: "整体色" }, { value: "0", text: "仅标题" } ],
id: "hNodeAlignPro.ColorApplyMode", name: h_i18n('Setting_ColorApplyMode','上色模式'), type: "combo",
options: [ { value: "1", text: h_i18n('Option_Color_Whole','整体色') }, { value: "0", text: h_i18n('Option_Color_TitleOnly','仅标题') } ],
defaultValue: "1",
category: ["🔥 NodeAlignPro", "NodeAlignPro节点设置", "上色模式"],
tooltip: "设置节点上色模式:整体色(背景+标题)或仅标题色",
onChange: (value) => { try { if (window.NodeAlignProSettingsManager) window.NodeAlignProSettingsManager.setColorApplyMode(parseInt(value)); } catch (error) { console.error('设置上色模式失败:', error); } }
category: ["🔥 NodeAlignPro", "NodeAlignPro节点设置", h_i18n('Setting_ColorApplyMode','上色模式')],
tooltip: h_i18n('Setting_ColorApplyMode','设置节点上色模式:整体色(背景+标题)或仅标题色'),
onChange: (value) => { try { const intVal = parseInt(value); if (window.NodeAlignProSettingsManager && typeof window.NodeAlignProSettingsManager.setColorApplyMode === 'function') { window.NodeAlignProSettingsManager.setColorApplyMode(intVal); } else __hNodeAlignPro_safeCall(null, null, 'colorApplyMode', intVal); } catch (error) { console.error('设置上色模式失败:', error); } }
}
];
@@ -147,10 +225,12 @@ function initNodeAlignProSettings() {
app.registerExtension({
name: "NodeAlignPro.Settings", settings: NodeAlignProSettings,
setup() {
if (app.ui?.settings?.addSetting) {
NodeAlignProSettings.forEach(setting => { try { app.ui.settings.addSetting(setting); } catch (err) { console.warn(`[NodeAlignPro.Settings] 注册设置项${setting.id}失败:`, err); } }); initNodeAlignProSettings(); // 注册设置项初始化设置
}
console.info("[NodeAlignPro.Settings] registered", NodeAlignProSettings.map(s => s.id));
try {
// ComfyUI会自动注册通过`settings`字段传递的设置
// 避免在此处手动调用addSetting,以防止重复注册错误
initNodeAlignProSettings(); // 初始化设置管理器和相关回调
console.info("[NodeAlignPro 设置模块] 已注册( NodeAlignPro.Settings)", NodeAlignProSettings.map(s => s.id));
} catch (e) { console.error('[NodeAlignPro 设置模块] 初始化时发生异常( NodeAlignPro.Settings):', e); }
},
beforeConfigureGraph(graph) { setTimeout(initNodeAlignProSettings, 2000); } // 延迟初始化,确保DOM已准备好
});