diff --git a/web/hLanguage.js b/web/hLanguage.js new file mode 100644 index 0000000..8136d71 --- /dev/null +++ b/web/hLanguage.js @@ -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); + } +})(); diff --git a/web/hNodeAlignPro.js b/web/hNodeAlignPro.js index e522d24..992b757 100644 --- a/web/hNodeAlignPro.js +++ b/web/hNodeAlignPro.js @@ -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 = ` -
v2.0.3_rc新版功能:按Shift、Alt、Ctrl Alt切换不同色卡模式...
Alt+对齐按钮:对齐到“反向基准”节点^_^(右键菜单>【新版说明】隐藏本提示)
+
v2.0.3_rc新版功能:按Shift、Alt、Ctrl Alt切换不同色卡模式...
Alt+对齐按钮:对齐到“反向基准”节点^_^(右键菜单>【新版说明】隐藏本提示)
-
+
@@ -563,20 +563,20 @@ - - - - + + + +
-
选择:
- - -
-
+
选择:
+ + +
+
- -
-
+ +
+
- -
-
- + + +
+
命名:
前缀
- -
+ +
后缀
- -
+ +
-
- - +
+ +
1x
0.5x
0.75x
1x
1.25x
1.5x
2x
-
-
对 齐
+
+
对 齐
-
AP球
色 卡
-对 齐-
标 准
专 业
+
AP球
色 卡
-对 齐-
标 准
专 业
-
常驻显示
常驻显示
跟随选框
+
常驻显示
常驻显示
跟随选框
- - - -
+ + + + `; 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核心组件初始化完毕! 请等待其它插件加载...
🔥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 ? '仅标题' : '整体色'}`); } } } diff --git a/web/hNodeAlignPro_settings.js b/web/hNodeAlignPro_settings.js index cfb4c51..a191401 100644 --- a/web/hNodeAlignPro_settings.js +++ b/web/hNodeAlignPro_settings.js @@ -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已准备好 });