Merge main (Custom Colors Nodes 2.0 fix) into chore/release-1.3.1

This commit is contained in:
Vito Sansevero
2026-11-01 03:24:59 -08:00
4 changed files with 56 additions and 4 deletions
+7
View File
@@ -7,6 +7,13 @@
- 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,
+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));
+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({