Compare commits

..
Author SHA1 Message Date
Vito 82d7243c42 Merge pull request #111 from ComfyAssets/chore/release-1.3.1
chore(release): 1.3.1 with a one-line startup banner and the Custom Colors fix
2026-11-01 03:54:09 -08:00
Vito Sansevero 4fdbc727cf Merge main (Custom Colors Nodes 2.0 fix) into chore/release-1.3.1 2026-11-01 03:24:59 -08:00
Vito 1e76311262 Merge pull request #112 from ComfyAssets/fix/custom-colors-nodes2-submenu
fix(custom_colors): flag the Custom Colors entry as a submenu for Nodes 2.0
2026-11-01 03:23:55 -08:00
Vito Sansevero 38ae722659 fix(custom_colors): flag the Custom Colors entry as a submenu for Nodes 2.0
- The Nodes 2.0 context menu converts litegraph items itself and only
  renders submenu.options for entries flagged has_submenu, so the
  "Custom Colors" entry was an inert label there; litegraph's own menu
  accepts either flag, so set both
- Add a real-host test that drives the actual right-click menu in both
  renderers through to the color dialog; the existing test invoked the
  callbacks directly and bypassed the converter
- Assert has_submenu on the node and group entries in the browser suite
2026-10-31 22:19:26 -07:00
Vito Sansevero 037ff39a3b chore(release): 1.3.1 with a one-line startup banner
- Collapse the console banner to a single line with the version and the
  tool count instead of one `Loaded:` line per node, matching
  ComfyUI-PromptManager
- Keep the banner ASCII so a legacy Windows console cannot abort the load
- Update the console encoding test to assert the summary line
- Ship the Krea 2 resolution family already on main
- Bump pyproject.toml to 1.3.1 and date the changelog section
2026-10-31 21:12:54 -07:00
Vito 6a4b9513d0 Merge pull request #110 from ComfyAssets/feat/krea2-resolution-family
feat(model_resolution_picker): add a Krea 2 family
2026-10-28 07:23:53 -07:00
Vito Sansevero 4532038c8a feat(model_resolution_picker): add a Krea 2 family
Krea 2 uses the Wan 2.1 latent (8x VAE) with patch 2, so sizes sit on a
16 px grid. Tiers 1.0 / 2.0 / 4.0 MP cover the model card's 1024x1024 and
2048x2048; Big Love KreaEdit3's 1024x1536 is listed under 2:3.
2026-10-28 07:12:29 -07:00
Vito c1a8477b8f Merge pull request #109 from ComfyAssets/chore/release-1.3.0
chore(release): 1.3.0 with the Model Resolution Picker
2026-10-27 16:26:15 -07:00
Vito Sansevero 98e9dc4661 chore(release): 1.3.0 with the Model Resolution Picker
- PR #108 changed pyproject.toml without bumping the version, so the
  registry publish on main failed with "The node version already exists".
- Bump to 1.3.0 (new node, route and override file) and add the release
  notes to CHANGELOG.md and the README.
2026-10-27 15:54:32 -07:00
Vito 60f197274c Merge pull request #108 from ComfyAssets/feat/model-resolution-picker
feat(model_resolution_picker): add the Model Resolution Picker node
2026-10-27 15:44:16 -07:00
11 changed files with 291 additions and 17 deletions
+40
View File
@@ -1,5 +1,45 @@
# Changelog
## 1.3.1
### Startup
- The console banner is one line, version plus tool count, instead of one
`Loaded:` line per node.
### Custom Colors
- The `🫶 Custom Colors` entry opens its Full / Title / BG submenu again in
Nodes 2.0. The Nodes 2.0 context menu converts litegraph items itself and
only renders a submenu for entries flagged `has_submenu`, so the entry was an
inert label there; the classic menu was unaffected.
### Model Resolution Picker
- New `Krea 2` family (Krea 2 Turbo, Raw and finetunes): 8x VAE, step 16,
1.0 / 2.0 / 4.0 MP tiers, with the model card's 1024×1024 and 2048×2048 and
Big Love KreaEdit3's 1024×1536.
## 1.3.0
### Model Resolution Picker
- New `Model Resolution Picker` node (🖼️ Resolution): pick a native size for a
model family from a chip grid of aspect ratios and output an empty latent plus
width/height. Fourteen shipped families (SD 1.5, SDXL, Illustrious XL, Anima,
Flux.1, Z-Image, Qwen-Image, Flux.2 / Klein / MOSS, Qwen-Image 2.1,
HunyuanImage 2.1 and four video keyframe rows), each rounded to its VAE step.
- Native `resolution` dropdown that mirrors the chip grid both ways; typed or
wired sizes show `custom`.
- Shipped family table in `families.json` with a validator, and a user override
file at `user/kikotools/model_resolutions.json` that can replace, add or remove
families and set the default. Invalid files are ignored whole with a warning
in the panel.
- The node height follows the panel content, and workflows saved before the
dropdown existed still load.
- `GET /kikotools/model_resolution/families` serves the merged table; the shared
dimension swap now works on nodes without a preset widget.
## 1.2.0
### Unified ComfyAssets UI
+7
View File
@@ -17,6 +17,13 @@ I tried forking, patching, and submitting merge requests, but eventually decided
I’m sharing them here with the community, and I hope you find them as useful as I do.
## 1.3.0 — Model Resolution Picker
A new node that picks a native resolution for a model family from a chip grid of
aspect ratios, with a synced `resolution` dropdown, a shipped family table and a
user override file. See the [release notes](CHANGELOG.md) and the
[node documentation](examples/documentation/model_resolution_picker.md).
## 1.2.0 — unified ComfyAssets UI
A consistent interface across the KikoTools controls, with ComfyUI-following
+7 -10
View File
@@ -114,15 +114,12 @@ def get_version():
return "unknown"
# Print startup message with loaded tools
print()
print(f"\033[94m[ComfyUI-KikoTools] Version:\033[0m {get_version()}")
for node_key, display_name in NODE_DISPLAY_NAME_MAPPINGS.items():
# ASCII only: a Windows console using a legacy code page cannot print emoji,
# and a UnicodeEncodeError here would abort loading the whole node.
ascii_name = display_name.encode("ascii", "ignore").decode().strip()
print(f" \033[94mLoaded:\033[0m {ascii_name}")
print(f"\033[94mTotal: {len(NODE_CLASS_MAPPINGS)} tools loaded\033[0m")
print()
# One-line startup banner. Keep it ASCII: a Windows console using a legacy
# code page raises UnicodeEncodeError on emoji, which would abort loading the
# whole node pack.
print(
f"\033[94m[ComfyUI-KikoTools] Version:\033[0m {get_version()} "
f"- {len(NODE_CLASS_MAPPINGS)} tools loaded"
)
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
@@ -44,6 +44,7 @@ The frontend reads the family table from `GET /kikotools/model_resolution/famili
| Z-Image | Z-Image Turbo and Z-Image Base | 8x | 16 | 1.0, 1.6 | 1024×1024 | image |
| Qwen-Image | Qwen-Image and Qwen-Image 2512 | 8x | 16 | 1.76 | 1328×1328 | image |
| Flux.2 / Klein / MOSS | Flux.2 dev, Flux.2 Klein, Flux.3 Klein, MOSS-Image, Ideogram 4 | 16x | 16 | 1.0, 2.0, 4.0 | 1024×1024 | image |
| Krea 2 | Krea 2 Turbo, Krea 2 Raw and Krea 2 finetunes (Big Love KreaEdit3) | 8x | 16 | 1.0, 2.0, 4.0 | 1024×1024 | image |
| Qwen-Image 2.1 | Qwen-Image 2.1 and 2.1 Turbo | 16x | 32 | 1.0, 4.0 | 1024×1024 | image |
| HunyuanImage 2.1 | HunyuanImage 2.1 | 32x | 32 | 4.0 | 2048×2048 | image |
| Wan 2.2 (keyframes) | Wan 2.2 (first/last-frame sizes) | 16x | 16 | 0.4, 0.9 | 1280×720 | video |
@@ -1107,6 +1107,188 @@
},
"notes": "generated at tiers 1.0, 2.0, 4.0 MP, step 16; 16:9 2.0 MP set to 1920x1088"
},
{
"label": "Krea 2",
"aliases": [],
"models": "Krea 2 Turbo, Krea 2 Raw and Krea 2 finetunes (Big Love KreaEdit3)",
"group": "image",
"vaeRatio": 8,
"step": 16,
"megapixels": [
1.0,
2.0,
4.0
],
"default": [
1024,
1024
],
"sizes": {
"1:1": [
[
1024,
1024
],
[
1456,
1456
],
[
2048,
2048
]
],
"4:5": [
[
912,
1152
],
[
1296,
1616
],
[
1824,
2288
]
],
"5:4": [
[
1152,
912
],
[
1616,
1296
],
[
2288,
1824
]
],
"3:4": [
[
880,
1184
],
[
1248,
1680
],
[
1776,
2368
]
],
"4:3": [
[
1184,
880
],
[
1680,
1248
],
[
2368,
1776
]
],
"2:3": [
[
832,
1248
],
[
1024,
1536
],
[
1184,
1776
],
[
1680,
2512
]
],
"3:2": [
[
1248,
832
],
[
1536,
1024
],
[
1776,
1184
],
[
2512,
1680
]
],
"9:16": [
[
768,
1360
],
[
1088,
1936
],
[
1536,
2736
]
],
"16:9": [
[
1360,
768
],
[
1936,
1088
],
[
2736,
1536
]
],
"21:9": [
[
1568,
672
],
[
2208,
944
],
[
3136,
1344
]
],
"9:21": [
[
672,
1568
],
[
944,
2208
],
[
1344,
3136
]
]
},
"notes": "Model card examples are 1024x1024 and 2048x2048; generated at tiers 1.0, 2.0, 4.0 MP, step 16 (Wan 2.1 latent, patch 2). 1024x1536 is Big Love KreaEdit3's model page size."
},
{
"label": "Qwen-Image 2.1",
"aliases": [],
+1 -1
View File
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "kikotools"
description = "Simple tools for ComfyUI"
version = "1.2.0"
version = "1.3.1"
license = {text = "MIT"}
dependencies = []
+5 -2
View File
@@ -1731,8 +1731,11 @@ test('custom colors handle absent settings, empty canvas menus, legacy groups an
expect(await page.evaluate(() => {
const extension = kikoHost.extensions.find(item => item.name === 'kikotools.customColors');
kikoHost.settings.delete('kikotools.custom_colors.enabled');
return [extension.getCanvasMenuItems({}), extension.getCanvasMenuItems({ graph_mouse: [0, 0], graph: { getGroupOnPos: () => null } }), extension.getNodeMenuItems(kikoHost.app.graph._nodes[0])[1].submenu.options.length];
})).toEqual([[], [], 3]);
const nodeMenu = extension.getNodeMenuItems(kikoHost.app.graph._nodes[0])[1];
const groupMenu = extension.getCanvasMenuItems({ graph_mouse: [0, 0], graph: { getGroupOnPos: () => ({ color: '#000000' }) } })[1];
// The Nodes 2.0 menu converter only renders submenu.options when has_submenu is set.
return [extension.getCanvasMenuItems({}), extension.getCanvasMenuItems({ graph_mouse: [0, 0], graph: { getGroupOnPos: () => null } }), nodeMenu.submenu.options.length, nodeMenu.has_submenu, groupMenu.has_submenu];
})).toEqual([[], [], 3, true, true]);
await page.evaluate(() => {
const extension = kikoHost.extensions.find(item => item.name === 'kikotools.customColors');
window.testGroup = { color: '#000000' };
+39
View File
@@ -733,6 +733,45 @@ test('Custom Colors preserves node colors across renderers and workflow reload',
await expect(page.locator('dialog.ca-color-dialog')).toHaveCount(0);
});
test('Custom Colors opens from the node context menu in both renderers', async ({ page }) => {
const errors = [];
page.on('pageerror', error => errors.push(error.message));
for (const renderer of [false, true]) {
await page.evaluate(async renderer => {
const { app } = await import('/scripts/app.js');
await app.ui.settings.setSettingValue('Comfy.VueNodes.Enabled', renderer);
}, renderer);
await expect.poll(() => page.evaluate(() => LiteGraph.vueNodesMode)).toBe(renderer);
const title = await page.evaluate(async () => {
const { app } = await import('/scripts/app.js');
app.graph.clear();
const node = LiteGraph.createNode('EmptyLatentImage');
app.graph.add(node);
app.canvas.centerOnNode(node);
app.canvas.setDirty(true, true);
await new Promise(resolve => setTimeout(resolve, 300));
const rect = app.canvas.canvas.getBoundingClientRect();
const [x, y] = app.canvas.ds.convertOffsetToCanvas([node.pos[0] + node.size[0] / 2, node.pos[1] - 10]);
return { x: rect.left + x, y: rect.top + y };
});
await page.mouse.click(title.x, title.y, { button: 'right' });
// The Nodes 2.0 menu converts litegraph items itself and only renders a
// submenu for entries flagged has_submenu; the classic menu opens
// submenu.options either way. Both must reach the picker.
const entry = page.getByText('🫶 Custom Colors', { exact: true }).first();
const full = page.getByText('🫶 Custom Full', { exact: true }).first();
await entry.hover();
try { await full.waitFor({ state: 'visible', timeout: 2000 }); } // Nodes 2.0 opens on hover
catch { await entry.click(); } // the classic litegraph menu opens on click
await full.click();
await expect(page.getByRole('dialog', { name: 'Custom full color', exact: true })).toBeVisible();
await page.keyboard.press('Escape');
await expect(page.locator('ca-color-picker')).toHaveCount(0);
await page.keyboard.press('Escape');
}
expect(errors).toEqual([]);
});
test('Follow Execution honors live settings and host menus in both renderers', async ({ page }, testInfo) => {
const errors = [];
page.on('pageerror', error => errors.push(error.message));
+3 -2
View File
@@ -38,8 +38,9 @@ def test_package_loads_with_a_legacy_windows_console(encoding):
stream.flush()
banner = stream.buffer.getvalue().decode(encoding)
assert "Loaded:" in banner
assert "Model Downloader" in banner # display name kept, emoji dropped
assert "[ComfyUI-KikoTools] Version:" in banner
assert "tools loaded" in banner
assert "Loaded:" not in banner # one summary line, not one line per tool
def test_preset_validation_messages_are_ascii(monkeypatch):
@@ -71,6 +71,7 @@ def test_defaults_labels_and_must_includes():
"SDXL": [(1344, 768)],
"Flux.1": [(1920, 1088), (1088, 1920)],
"Flux.2 / Klein / MOSS": flux2,
"Krea 2": [(1024, 1024), (2048, 2048), (1024, 1536), (1536, 1024)],
"Z-Image": [(1680, 720), (1280, 1280)],
"Qwen-Image": [(1664, 928)],
"Qwen-Image 2.1": [(2048, 2048), (2400, 1792), (2528, 1696), (2752, 1536)],
+5 -2
View File
@@ -86,10 +86,13 @@ const openPicker = (initialColor, apply, node, label) => {
picker.dialog.showModal();
};
// Wrap menu options in the "Custom Colors" submenu (or nothing if empty)
// Wrap menu options in the "Custom Colors" submenu (or nothing if empty).
// has_submenu is required as well as submenu: the Nodes 2.0 context menu
// converts litegraph items itself and only renders submenu.options for
// entries flagged has_submenu; without it the entry is an inert label.
const customColorsMenu = (options) => options.length === 0 ? [] : [
null, // separator
{ content: "🫶 Custom Colors", submenu: { options } },
{ content: "🫶 Custom Colors", has_submenu: true, submenu: { options } },
];
app.registerExtension({