Comfy-Org / Comfy-Org/ComfyUI_frontend
Snap to grid - grid visual - grid offset - node positioning and resizing according to snap setting
- Dominant language
- TypeScript
- Stars
- 2k
- Forks
- 699
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 490
Description
### Prerequisites
- [x] I am running the latest version of ComfyUI
- [ ] I have custom nodes enabled
- [x] I tried with `--disable-all-custom-nodes`
### What happened?
*Everything wrong with the grid settings*
* Settings > Lite Graph > Canvas > Snap to grid size and Always Snap to Grid
* The "Snap to grid size" setting has no influence on the appearance of the grid(?), as you would expect, like in most graphical editors
* Instead the grid background is a image(?) which needs to be base-64 encoded(?) in the theme(?) which can only be updated via: export -> update colors.lightgraph_base.BACKGROUND_IMAGE -> delete old theme(?) -> import (?). This is very cumbersome. The [docs - appearance](https://docs.comfy.org/interface/appearance) don't tell me how where the theme is stored on disk, or how I can use a css-url reference to an actual file. I would like an option to overwrite the theme or set it in ComfyUI directly.
* I manually created the grid image now. The background image is not aligned with the snap origin(?). I had to shift the grid in the image upwards by 10pixel(?) to make it aligned.
* Node resizing adheres to snapping, minimum node size does not(?), thus when you resize the node too small it is not a multiple of your snap setting. The minimum node size should always ceil to the next snap value. also see Lite Graph > Node > Always shrink new nodes: this should increase to next grid size when Snap to grid is on
* When a new node is added the position and size are not snapped to the grid(?). There is also another bug in the frontend right now. Either the node search box closes on add or not. When it closes the node can be freely positioned (no snapping). If you refresh, they become auto-snapped though. If the bug hits and search box stays open, the node is positioned freely in the background off grid.
* When manually changing the position of a node that was not snapped, undo doesn't change the nodes back to their original position and size.
* Depending on Nodes 1.0 or 2.0 the horizontal and vertical size cannot be resized to the next grid step. It's always a half-way off in one direction or the other.
* When I refresh the page, nodes get repositioned and resized on their own(?). Even nodes which are not reduced to their minimum size get auto resized. This constant moving around and resizing when re-opening a workflow is annoying.
* adding a new re-route is not auto-snapped to grid. it should
* When I mouse-scroll to zoom, the zoom should always snap at 100%. There should also be a 100% setting when opening the combobox to select zoom level.
* Let's say the zoom level is `146%` and I want to enter the zoom level manually, and remove the first two numbers `1%`, it automatically adds a zero(?) `10%`, I enter two zeros because I didn't see the automatic insertion and now the zoom level is `1000%`.
### Steps to Reproduce
1. add a new node => it's off
2. resize it to minimum => it's off
3. refresh => it's off
**resizing**
1. nodes 1.0 and positioned on grid. I cannot resize vertically to grid step (it's always half-way off)
2. If I try to increase vertically, it doesn't snap (it's always half-way off)
3. when I switch to nodes 2.0, the minimum size increases for no reason. the auto-resizing doesn't consider snapping.
**group nodes**
are completely off in both versions:
**reroute knots**
half-way off vertically
**reroute boxes**
Nodes 1.0
vertical size is half-way between two grids
Nodes 2.0
horizontal size is half-way between two grids
### Workflow
N/A
### How is this affecting you?
Visual/UI issue only
### ComfyUI Frontend Version
1.51.9
### Browser
Firefox
### Console Errors
```javascript
```
### Logs
```shell
```
### Additional Context
Settings > Lite Graph > Canvas > Snap to grid size: My recommendation is to set it to 21. Why? Because the default text size spaces the node sockets such that they are 21pixels apart. Thus we 21 you can make straight connections for most of the part. It's unfortunate that this value is not a multiple of 10.
I use this setting (210x210px grid with 10x10 tiles):
`"BACKGROUND_IMAGE": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANIAAADSCAIAAACw+wkVAAACe0lEQVR4nO3bsY2EQBBFweG0cRBdRzmRYBPJxnA4T2ir/C9hPGkM1MfM7L3XU+d53vdtbv4vf4+X8JjsCMiOgOwIyI6A7AjIjoDsCMiOgOwIHDNzXVf9GfyWz1rrvb/2zF8698gSkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRcMJDwAmPeTD3yBKQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RFwwkPACY95MPfIEpAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEXDCQ8AJj3kw98gSkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRcMJDwAmPeTD3yBKQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RFwwkPACY95MPfIEpAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEXDCQ8AJj3kw98gSkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRcMJDwAmPeTD3yBKQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RFwwkPACY95MPfIEpAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEZAdAdkRkB0B2RGQHQHZEThmpv4Gfs5nrbX3frx/9SGJeTX3yBKQHQHZEZAdAdkRkB0B2RGQHYEvPstqm1tzdLAAAAAASUVORK5CYII=",`
I had to generate a python script to make a grid image:
```python
import base64
from PIL import Image, ImageDraw
# Image size and grid settings
width, height = 210, 210
grid_size = 21 # tile size
shift_amount = 10
# Colors
bg_color = (30, 30, 30) # Dark background
grid_color = (60, 60, 60) # Dark gray for minor grid
major_grid_color = (100, 100, 100) # Lighter dark gray for major grid
# Create image
img = Image.new('RGB', (width, height), bg_color)
draw = ImageDraw.Draw(img)
# Draw minor grid (10x10)
for x in range(0, width, grid_size):
draw.line([(x, 0), (x, height)], fill=grid_color, width=1)
for y in range(0, height, grid_size):
draw.line([(0, y), (width, y)], fill=grid_color, width=1)
# Draw major grid lines (only top and left borders)
draw.line([(0, 0), (width, 0)], fill=major_grid_color, width=1) # Top border
draw.line([(0, 0), (0, height)], fill=major_grid_color, width=1) # Left border
# Shift image vertically with wrap-around (12px up)
# Split image into top and bottom parts
top_part = img.crop((0, 0, width, shift_amount))
bottom_part = img.crop((0, shift_amount, width, height))
# Create new image and paste with wrap-around
shifted_img = Image.new('RGB', (width, height), bg_color)
shifted_img.paste(bottom_part, (0, 0))
shifted_img.paste(top_part, (0, height - shift_amount))
# Save image
shifted_img.save('grid.png')
# Convert to base64 and save to grid.txt
with open('grid.png', 'rb') as image_file:
base64_string = base64.b64encode(image_file.read()).decode('utf-8')
with open('grid.txt', 'w') as text_file:
text_file.write(base64_string)
print("Image saved as 'grid_tile.png' and base64 encoded to 'grid.txt'")
```
Contributor guide
Research direction
Start in Settings > Lite Graph > Canvas and reproduce the node, group, reroute, grid, and zoom cases described using Snap to grid size and Always Snap to Grid. Compare Nodes 1.0 and 2.0, including adding, resizing, refreshing, and undoing changes. Done means the reported positioning, sizing, grid alignment, and zoom behaviors consistently follow the selected snap settings.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100