Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82d7243c42 | ||
|
|
4fdbc727cf | ||
|
|
1e76311262 | ||
|
|
38ae722659 | ||
|
|
037ff39a3b | ||
|
|
6a4b9513d0 | ||
|
|
4532038c8a | ||
|
|
c1a8477b8f | ||
|
|
98e9dc4661 | ||
|
|
60f197274c |
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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 = []
|
||||
|
||||
|
||||
@@ -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' };
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)],
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user