diff --git a/locales/zh/settings.json b/locales/zh/settings.json
index d5bbb9e..edc833c 100644
--- a/locales/zh/settings.json
+++ b/locales/zh/settings.json
@@ -34,8 +34,7 @@
"Permanent": "常驻",
"Show on Node Selection": "在选中多个节点时",
"Disabled": "禁用"
- },
- "tooltip": "切换模式后点击画布空白处或刷新页面即可"
+ }
},
"KayTool_NodeAlignBackgroundOpacity": {
"name": "工具栏背景不透明度"
@@ -44,7 +43,8 @@
"name": "工具栏背景颜色"
},
"KayTool_NodeAlignIconBackgroundColor": {
- "name": "图标背景颜色"
+ "name": "图标背景颜色",
+ "tooltip": "数值“363636”接近ComfyUI菜单栏颜色,可模拟隐藏背景效果"
},
"KayTool_NodeAlignIconColor": {
"name": "图标颜色"
diff --git a/pyproject.toml b/pyproject.toml
index 368823a..777c9d0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "kaytool"
description ="KayTool enhances ComfyUI workflow efficiency with practical tools, including: image processing (resize, color, blur, expand, merge, crop), background/mask tasks via BiRefNet/RemBG, wireless data transfer (Get & Out), AI translation (Tencent/Baidu), dynamic math operations, flexible text handling, precision sliders, metadata/color profile-supported image saving, batch processing via image folder loading, canvas workflow node map export as PNG with embedded metadata, quick Run options via right-click (Run Group/Node), node alignment toolbar with custom styled styles, and custom ComfyUI logo integration."
-version = "0.50.5"
+version = "0.50.6"
license = {file = "LICENSE"}
dependencies = ["rembg, timm"]
diff --git a/web/nodealign.js b/web/nodealign.js
index f041aa1..47d2f8a 100644
--- a/web/nodealign.js
+++ b/web/nodealign.js
@@ -1,7 +1,6 @@
import { app } from "../../../scripts/app.js";
-// Based on /NodeAligner by Tenney95 GitHub:https://github.com/Tenney95/ComfyUI-NodeAligner
-
+// SVG 定义(已包含你的现有 SVG,新增参考代码中的两个)
const kayAlignBottomSvg = ``;
const kayAlignCenterHorizontallySvg = ``;
const kayAlignCenterVerticallySvg = ``;
@@ -10,6 +9,8 @@ const kayAlignRightSvg = ``;
const kayEqualWidthSvg = ``;
const kayEqualHeightSvg = ``;
+const kayHorizontalDistributionSvg = ``;
+const kayVerticalDistributionSvg = ``;
let stylesInjected = false;
@@ -87,7 +88,7 @@ const KayNodeAlignmentManager = {
transition: background-color 0.3s ease-in-out, transform 0.1s ease;
flex-shrink: 0;
}
- .kay-align-button svg { width: 70%; height: 70%; }
+ .kay-align-button svg { width: 66%; height: 66%; }
.kay-toolbar-divider {
width: 3.2px;
height: 15px;
@@ -156,7 +157,7 @@ const KayNodeAlignmentManager = {
const menuChildren = Array.from(this.menuElement.children).filter(child => child !== this.insertionIndicator);
const insertIndex = Math.min(this.position.insertIndex, menuChildren.length);
const insertBeforeElement = menuChildren[insertIndex] || null;
- this.menuElement.insertBefore(this.toolbarContainer, insertBeforeElement);;
+ this.menuElement.insertBefore(this.toolbarContainer, insertBeforeElement);
} else {
document.body.appendChild(this.toolbarContainer);
}
@@ -219,7 +220,10 @@ const KayNodeAlignmentManager = {
{ id: 'kay-align-bottom', svg: kayAlignBottomSvg, action: this.alignBottom },
{ type: 'divider' },
{ id: 'kay-equal-width', svg: kayEqualWidthSvg, action: this.equalWidth },
- { id: 'kay-equal-height', svg: kayEqualHeightSvg, action: this.equalHeight }
+ { id: 'kay-equal-height', svg: kayEqualHeightSvg, action: this.equalHeight },
+ { type: 'divider' },
+ { id: 'kay-horizontal-distribution', svg: kayHorizontalDistributionSvg, action: this.horizontalDistribution },
+ { id: 'kay-vertical-distribution', svg: kayVerticalDistributionSvg, action: this.verticalDistribution }
];
},
@@ -278,14 +282,23 @@ const KayNodeAlignmentManager = {
onDragStart(e) {
e.preventDefault();
const { toolbarRect } = this.getRect();
- this.dragState = { isDragging: true, offsetX: e.clientX - toolbarRect.left, offsetY: e.clientY - toolbarRect.top };
- if (this.position.isAttached) this.detachFromMenu(true);
- if (this.menuElement) this.menuElement.style.backgroundColor = '#d0ff00';
+ this.dragState = {
+ isDragging: true,
+ offsetX: e.clientX - toolbarRect.left,
+ offsetY: e.clientY - toolbarRect.top
+ };
},
onDragging(e) {
if (!this.dragState.isDragging) return;
+
const { windowRect, toolbarRect, menuRect } = this.getRect();
+
+ if (this.position.isAttached) {
+ this.detachFromMenu(true);
+ this.menuElement.style.backgroundColor = '#d0ff00';
+ }
+
let left = e.clientX - this.dragState.offsetX;
let top = e.clientY - this.dragState.offsetY;
left = Math.max(0, Math.min(left, windowRect.width - toolbarRect.width));
@@ -317,6 +330,7 @@ const KayNodeAlignmentManager = {
onDragEnd() {
if (!this.dragState.isDragging) return;
+
this.dragState.isDragging = false;
this.dragState.offsetX = 0;
this.dragState.offsetY = 0;
@@ -441,13 +455,85 @@ const KayNodeAlignmentManager = {
this.redraw();
},
+ horizontalDistribution() {
+ const nodes = this.getSelectedNodes();
+ const axis = 0;
+ if (nodes.length > 1) {
+ nodes.sort((a, b) => a.pos[axis] - b.pos[axis]);
+ const min = Math.min(...nodes.map(node => node.pos[axis]));
+ const max = Math.max(...nodes.map(node => node.pos[axis] + node.size[axis]));
+ const totalSize = nodes.reduce((sum, node) => sum + node.size[axis], 0);
+ const spacing = (max - min - totalSize) / (nodes.length - 1);
+ let current = min;
+ nodes.forEach(node => {
+ node.pos[axis] = current;
+ current += node.size[axis] + spacing;
+ });
+ this.redraw();
+ }
+ },
+
+ verticalDistribution() {
+ const nodes = this.getSelectedNodes();
+ if (nodes.length > 1) {
+ const axis = 1;
+ const otherAxis = 0;
+ const tolerance = 100;
+ const minSpacing = 20;
+
+ const columns = [];
+ nodes.forEach(node => {
+ let foundColumn = null;
+ for (let column of columns) {
+ const columnX = column[0].pos[otherAxis];
+ if (Math.abs(columnX - node.pos[otherAxis]) <= tolerance) {
+ foundColumn = column;
+ break;
+ }
+ }
+ if (foundColumn) {
+ foundColumn.push(node);
+ } else {
+ columns.push([node]);
+ }
+ });
+
+ const columnHeights = columns.map(column => {
+ const minY = Math.min(...column.map(node => node.pos[axis]));
+ const maxY = Math.max(...column.map(node => node.pos[axis] + node.size[axis]));
+ return maxY - minY;
+ });
+ const maxColumnHeight = Math.max(...columnHeights);
+
+ columns.forEach(column => {
+ if (column.length > 1) {
+ column.sort((a, b) => a.pos[axis] - b.pos[axis]);
+ const minY = Math.min(...column.map(node => node.pos[axis]));
+ const totalSize = column.reduce((sum, node) => sum + node.size[axis], 0);
+ let spacing = (maxColumnHeight - totalSize) / (column.length - 1);
+ spacing = Math.max(spacing, minSpacing);
+ let currentY = column[0].pos[axis];
+ column.forEach((node, idx) => {
+ if (idx === 0) {
+ currentY += node.size[axis] + spacing;
+ } else {
+ node.pos[axis] = currentY;
+ currentY += node.size[axis] + spacing;
+ }
+ node.pos[otherAxis] = column[0].pos[otherAxis];
+ });
+ }
+ });
+ this.redraw();
+ }
+ },
+
redraw() {
if (app.canvas) app.canvas.setDirty(true, true);
},
updateDisplayMode(mode) {
const effectiveMode = mode || "permanent";
-
if (effectiveMode === "disabled") {
this.hide();
} else if (effectiveMode === "permanent") {
diff --git a/web/settings.js b/web/settings.js
index 85b80b8..45ca678 100644
--- a/web/settings.js
+++ b/web/settings.js
@@ -93,7 +93,6 @@ app.registerExtension({
],
defaultValue: "permanent",
category: ["KayTool", "NodeAlignment", "DisplayMode"],
- tooltip: "Controls when the node alignment toolbar is displayed.",
onChange: (value) => {
const manager = window.KayNodeAlignmentManager;
const initFn = window.initializeKayNodeAlignment;
@@ -152,6 +151,7 @@ app.registerExtension({
type: "color",
defaultValue: "2b2b2b",
category: ["KayTool", "NodeAlignment", "IconBackgroundColor"],
+ tooltip: "Use parameter “363636” to match ComfyUI menu bar color for an icon-free background effect",
onChange: (newVal) => {
if (/^[0-9A-Fa-f]{6}$/.test(newVal)) {
const buttons = document.querySelectorAll('.kay-align-button');