From 688193ad9a1b504e7465d3f3ad321d6b0d7762f7 Mon Sep 17 00:00:00 2001 From: Comfy Org PR Bot Date: Fri, 11 Jul 2025 16:13:36 +0800 Subject: [PATCH 01/29] 1.24.1 (#4425) Co-authored-by: webfiltered <176114999+webfiltered@users.noreply.github.com> --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c535c0d0..70823c071 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@comfyorg/comfyui-frontend", - "version": "1.24.0", + "version": "1.24.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@comfyorg/comfyui-frontend", - "version": "1.24.0", + "version": "1.24.1", "license": "GPL-3.0-only", "dependencies": { "@alloc/quick-lru": "^5.2.0", diff --git a/package.json b/package.json index f53dc88cf..e6c107d79 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@comfyorg/comfyui-frontend", "private": true, - "version": "1.24.0", + "version": "1.24.1", "type": "module", "repository": "https://github.com/Comfy-Org/ComfyUI_frontend", "homepage": "https://comfy.org", From ef9b6252080ee7d663425570b01480f4ab099735 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Fri, 11 Jul 2025 19:27:31 +1000 Subject: [PATCH 02/29] Fix DTO return type to allow clean test/merge (#4426) --- src/utils/executableGroupNodeChildDTO.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/executableGroupNodeChildDTO.ts b/src/utils/executableGroupNodeChildDTO.ts index 1832adfdd..1250874ca 100644 --- a/src/utils/executableGroupNodeChildDTO.ts +++ b/src/utils/executableGroupNodeChildDTO.ts @@ -46,7 +46,7 @@ export class ExecutableGroupNodeChildDTO extends ExecutableNodeDTO { return { node: inputNodeDto, - origin_id: inputNode.id, + origin_id: String(inputNode.id), origin_slot: link.origin_slot } } From 054077c44561b6ef0837b3722186384f08342a1d Mon Sep 17 00:00:00 2001 From: Comfy Org PR Bot Date: Fri, 11 Jul 2025 20:28:02 +0800 Subject: [PATCH 03/29] [chore] Update litegraph to 0.16.8 (#4427) Co-authored-by: webfiltered <176114999+webfiltered@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 70823c071..0d1daa793 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@alloc/quick-lru": "^5.2.0", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@comfyorg/comfyui-electron-types": "^0.4.43", - "@comfyorg/litegraph": "^0.16.7", + "@comfyorg/litegraph": "^0.16.8", "@primevue/forms": "^4.2.5", "@primevue/themes": "^4.2.5", "@sentry/vue": "^8.48.0", @@ -949,9 +949,9 @@ "license": "GPL-3.0-only" }, "node_modules/@comfyorg/litegraph": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.16.7.tgz", - "integrity": "sha512-ua2nwnOpCzKxihLZOvvckz77wB6cMEZ3XeQtTTsg3nR5qLndZEbN8GpWXHWp0Cmf9XgT3pEWVPBBKLHvbn5S7g==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.16.8.tgz", + "integrity": "sha512-l2GGazOIfESYDQCxkmObMVlsjwTmCvF6e7chY+j1CovijXZQg8AXR5Q7lLms98K0LO62qtJtkLnAigeziHnAxQ==", "license": "MIT" }, "node_modules/@cspotcode/source-map-support": { diff --git a/package.json b/package.json index e6c107d79..24a9a009e 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@alloc/quick-lru": "^5.2.0", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@comfyorg/comfyui-electron-types": "^0.4.43", - "@comfyorg/litegraph": "^0.16.7", + "@comfyorg/litegraph": "^0.16.8", "@primevue/forms": "^4.2.5", "@primevue/themes": "^4.2.5", "@sentry/vue": "^8.48.0", From 19eaf6ecdc078679eed96ea0f38b51ccbe3e3c43 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Fri, 11 Jul 2025 22:37:23 +1000 Subject: [PATCH 04/29] Fix SubgraphNode widget values ignored (#4429) --- src/utils/executionUtil.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/utils/executionUtil.ts b/src/utils/executionUtil.ts index fec35d9bd..5310dbb48 100644 --- a/src/utils/executionUtil.ts +++ b/src/utils/executionUtil.ts @@ -145,6 +145,13 @@ export const graphToPrompt = async ( const resolvedInput = node.resolveInput(i) if (!resolvedInput) continue + // Input resolved to a SubgraphNode widget + const { value } = resolvedInput + if (value) { + inputs[input.name] = Array.isArray(value) ? { __value__: value } : value + continue + } + inputs[input.name] = [ String(resolvedInput.origin_id), // @ts-expect-error link.origin_slot is already number. From f0bc4c6959287034db008a49b788f1fae196ef33 Mon Sep 17 00:00:00 2001 From: Comfy Org PR Bot Date: Sat, 12 Jul 2025 07:01:24 +0800 Subject: [PATCH 05/29] [chore] Update litegraph to 0.16.9 (#4432) Co-authored-by: webfiltered <176114999+webfiltered@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d1daa793..fa5cee570 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@alloc/quick-lru": "^5.2.0", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@comfyorg/comfyui-electron-types": "^0.4.43", - "@comfyorg/litegraph": "^0.16.8", + "@comfyorg/litegraph": "^0.16.9", "@primevue/forms": "^4.2.5", "@primevue/themes": "^4.2.5", "@sentry/vue": "^8.48.0", @@ -949,9 +949,9 @@ "license": "GPL-3.0-only" }, "node_modules/@comfyorg/litegraph": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.16.8.tgz", - "integrity": "sha512-l2GGazOIfESYDQCxkmObMVlsjwTmCvF6e7chY+j1CovijXZQg8AXR5Q7lLms98K0LO62qtJtkLnAigeziHnAxQ==", + "version": "0.16.9", + "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.16.9.tgz", + "integrity": "sha512-ZsvqkLqdG65e2UyM8oTOUTv/7VFEyGbG/C9dCZnhxdNq30UaE+F0iLaKq/17u6w4yewyZuqIn5MoOtjpxPqLDQ==", "license": "MIT" }, "node_modules/@cspotcode/source-map-support": { diff --git a/package.json b/package.json index 24a9a009e..2dfd9aa87 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@alloc/quick-lru": "^5.2.0", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@comfyorg/comfyui-electron-types": "^0.4.43", - "@comfyorg/litegraph": "^0.16.8", + "@comfyorg/litegraph": "^0.16.9", "@primevue/forms": "^4.2.5", "@primevue/themes": "^4.2.5", "@sentry/vue": "^8.48.0", From 01c735d94316eb34b031e5436aaae2bb09805edf Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Sat, 12 Jul 2025 09:05:58 +1000 Subject: [PATCH 06/29] Fix cannot check widget value if undefined (#4433) --- src/utils/executionUtil.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/executionUtil.ts b/src/utils/executionUtil.ts index 5310dbb48..a0047d716 100644 --- a/src/utils/executionUtil.ts +++ b/src/utils/executionUtil.ts @@ -145,9 +145,9 @@ export const graphToPrompt = async ( const resolvedInput = node.resolveInput(i) if (!resolvedInput) continue - // Input resolved to a SubgraphNode widget - const { value } = resolvedInput - if (value) { + // Resolved to an actual widget value rather than a node connection + if (resolvedInput.widgetInfo) { + const { value } = resolvedInput.widgetInfo inputs[input.name] = Array.isArray(value) ? { __value__: value } : value continue } From a70d69cbd26be18ef0626e75b9b32870a50f1902 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Sat, 12 Jul 2025 14:37:48 -0700 Subject: [PATCH 07/29] [fix] Sync subgraph node title changes with breadcrumb navigation (#4394) --- browser_tests/assets/nested-subgraph.json | 716 ++++++++++++++++++ .../tests/subgraphBreadcrumb.spec.ts | 82 ++ src/components/graph/TitleEditor.vue | 10 +- 3 files changed, 807 insertions(+), 1 deletion(-) create mode 100644 browser_tests/assets/nested-subgraph.json create mode 100644 browser_tests/tests/subgraphBreadcrumb.spec.ts diff --git a/browser_tests/assets/nested-subgraph.json b/browser_tests/assets/nested-subgraph.json new file mode 100644 index 000000000..d65e0c29c --- /dev/null +++ b/browser_tests/assets/nested-subgraph.json @@ -0,0 +1,716 @@ +{ + "id": "976d6e9a-927d-42db-abd4-96bfc0ecf8d9", + "revision": 0, + "last_node_id": 10, + "last_link_id": 11, + "nodes": [ + { + "id": 10, + "type": "8beb610f-ddd1-4489-ae0d-2f732a4042ae", + "pos": [ + 532, + 412.5 + ], + "size": [ + 140, + 46 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 10 + ] + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 11 + ] + } + ], + "title": "subgraph 2", + "properties": {}, + "widgets_values": [] + }, + { + "id": 8, + "type": "VAEDecode", + "pos": [ + 758.2109985351562, + 398.3681335449219 + ], + "size": [ + 210, + 46 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 10 + }, + { + "name": "vae", + "type": "VAE", + "link": 11 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "slot_index": 0, + "links": [ + 9 + ] + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + }, + "widgets_values": [] + }, + { + "id": 9, + "type": "SaveImage", + "pos": [ + 1028.9615478515625, + 381.83746337890625 + ], + "size": [ + 210, + 270 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + } + ], + "outputs": [], + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + } + ], + "links": [ + [ + 9, + 8, + 0, + 9, + 0, + "IMAGE" + ], + [ + 10, + 10, + 0, + 8, + 0, + "LATENT" + ], + [ + 11, + 10, + 1, + 8, + 1, + "VAE" + ] + ], + "groups": [], + "definitions": { + "subgraphs": [ + { + "id": "8beb610f-ddd1-4489-ae0d-2f732a4042ae", + "version": 1, + "state": { + "lastGroupId": 0, + "lastNodeId": 10, + "lastLinkId": 14, + "lastRerouteId": 0 + }, + "revision": 0, + "config": {}, + "name": "subgraph 2", + "inputNode": { + "id": -10, + "bounding": [ + -154, + 415.5, + 120, + 40 + ] + }, + "outputNode": { + "id": -20, + "bounding": [ + 1238, + 395.5, + 120, + 80 + ] + }, + "inputs": [], + "outputs": [ + { + "id": "4d6c7e4e-971e-4f78-9218-9a604db53a4b", + "name": "LATENT", + "type": "LATENT", + "linkIds": [ + 7 + ], + "localized_name": "LATENT", + "pos": { + "0": 1258, + "1": 415.5 + } + }, + { + "id": "f8201d4f-7fc6-4a1b-b8c9-9f0716d9c09a", + "name": "VAE", + "type": "VAE", + "linkIds": [ + 14 + ], + "localized_name": "VAE", + "pos": { + "0": 1258, + "1": 435.5 + } + } + ], + "widgets": [], + "nodes": [ + { + "id": 6, + "type": "CLIPTextEncode", + "pos": [ + 415, + 186 + ], + "size": [ + 422.84503173828125, + 164.31304931640625 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [ + { + "localized_name": "clip", + "name": "clip", + "type": "CLIP", + "link": 13 + } + ], + "outputs": [ + { + "localized_name": "CONDITIONING", + "name": "CONDITIONING", + "type": "CONDITIONING", + "slot_index": 0, + "links": [ + 4 + ] + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," + ] + }, + { + "id": 3, + "type": "KSampler", + "pos": [ + 863, + 186 + ], + "size": [ + 315, + 262 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "localized_name": "model", + "name": "model", + "type": "MODEL", + "link": 12 + }, + { + "localized_name": "positive", + "name": "positive", + "type": "CONDITIONING", + "link": 4 + }, + { + "localized_name": "negative", + "name": "negative", + "type": "CONDITIONING", + "link": 10 + }, + { + "localized_name": "latent_image", + "name": "latent_image", + "type": "LATENT", + "link": 11 + } + ], + "outputs": [ + { + "localized_name": "LATENT", + "name": "LATENT", + "type": "LATENT", + "slot_index": 0, + "links": [ + 7 + ] + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 32115495257102, + "randomize", + 20, + 8, + "euler", + "normal", + 1 + ] + }, + { + "id": 10, + "type": "dbe5763f-440b-47b4-82ac-454f1f98b0e3", + "pos": [ + 194.13900756835938, + 657.3333740234375 + ], + "size": [ + 140, + 106 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [], + "outputs": [ + { + "localized_name": "CONDITIONING", + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 10 + ] + }, + { + "localized_name": "LATENT", + "name": "LATENT", + "type": "LATENT", + "links": [ + 11 + ] + }, + { + "localized_name": "MODEL", + "name": "MODEL", + "type": "MODEL", + "links": [ + 12 + ] + }, + { + "localized_name": "CLIP", + "name": "CLIP", + "type": "CLIP", + "links": [ + 13 + ] + }, + { + "localized_name": "VAE", + "name": "VAE", + "type": "VAE", + "links": [ + 14 + ] + } + ], + "title": "subgraph 3", + "properties": {}, + "widgets_values": [] + } + ], + "groups": [], + "links": [ + { + "id": 4, + "origin_id": 6, + "origin_slot": 0, + "target_id": 3, + "target_slot": 1, + "type": "CONDITIONING" + }, + { + "id": 7, + "origin_id": 3, + "origin_slot": 0, + "target_id": -20, + "target_slot": 0, + "type": "LATENT" + }, + { + "id": 10, + "origin_id": 10, + "origin_slot": 0, + "target_id": 3, + "target_slot": 2, + "type": "CONDITIONING" + }, + { + "id": 11, + "origin_id": 10, + "origin_slot": 1, + "target_id": 3, + "target_slot": 3, + "type": "LATENT" + }, + { + "id": 12, + "origin_id": 10, + "origin_slot": 2, + "target_id": 3, + "target_slot": 0, + "type": "MODEL" + }, + { + "id": 13, + "origin_id": 10, + "origin_slot": 3, + "target_id": 6, + "target_slot": 0, + "type": "CLIP" + }, + { + "id": 14, + "origin_id": 10, + "origin_slot": 4, + "target_id": -20, + "target_slot": 1, + "type": "VAE" + } + ], + "extra": {} + }, + { + "id": "dbe5763f-440b-47b4-82ac-454f1f98b0e3", + "version": 1, + "state": { + "lastGroupId": 0, + "lastNodeId": 9, + "lastLinkId": 9, + "lastRerouteId": 0 + }, + "revision": 0, + "config": {}, + "name": "subgraph 3", + "inputNode": { + "id": -10, + "bounding": [ + -154, + 517, + 120, + 40 + ] + }, + "outputNode": { + "id": -20, + "bounding": [ + 898.2780151367188, + 467, + 128.6640625, + 140 + ] + }, + "inputs": [], + "outputs": [ + { + "id": "b4882169-329b-43f6-a373-81abfbdea55b", + "name": "CONDITIONING", + "type": "CONDITIONING", + "linkIds": [ + 6 + ], + "localized_name": "CONDITIONING", + "pos": { + "0": 918.2780151367188, + "1": 487 + } + }, + { + "id": "01f51f96-a741-428e-8772-9557ee50b609", + "name": "LATENT", + "type": "LATENT", + "linkIds": [ + 2 + ], + "localized_name": "LATENT", + "pos": { + "0": 918.2780151367188, + "1": 507 + } + }, + { + "id": "47fa906e-d80b-45c3-a596-211a0e59d4a1", + "name": "MODEL", + "type": "MODEL", + "linkIds": [ + 1 + ], + "localized_name": "MODEL", + "pos": { + "0": 918.2780151367188, + "1": 527 + } + }, + { + "id": "f03dccd7-10e8-4513-9994-15854a92d192", + "name": "CLIP", + "type": "CLIP", + "linkIds": [ + 3 + ], + "localized_name": "CLIP", + "pos": { + "0": 918.2780151367188, + "1": 547 + } + }, + { + "id": "a666877f-e34f-49bc-8a78-b26156656b83", + "name": "VAE", + "type": "VAE", + "linkIds": [ + 8 + ], + "localized_name": "VAE", + "pos": { + "0": 918.2780151367188, + "1": 567 + } + } + ], + "widgets": [], + "nodes": [ + { + "id": 7, + "type": "CLIPTextEncode", + "pos": [ + 413, + 389 + ], + "size": [ + 425.27801513671875, + 180.6060791015625 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "localized_name": "clip", + "name": "clip", + "type": "CLIP", + "link": 5 + } + ], + "outputs": [ + { + "localized_name": "CONDITIONING", + "name": "CONDITIONING", + "type": "CONDITIONING", + "slot_index": 0, + "links": [ + 6 + ] + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "text, watermark" + ] + }, + { + "id": 5, + "type": "EmptyLatentImage", + "pos": [ + 473, + 609 + ], + "size": [ + 315, + 106 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [], + "outputs": [ + { + "localized_name": "LATENT", + "name": "LATENT", + "type": "LATENT", + "slot_index": 0, + "links": [ + 2 + ] + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 512, + 1 + ] + }, + { + "id": 4, + "type": "CheckpointLoaderSimple", + "pos": [ + 26, + 474 + ], + "size": [ + 315, + 98 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [], + "outputs": [ + { + "localized_name": "MODEL", + "name": "MODEL", + "type": "MODEL", + "slot_index": 0, + "links": [ + 1 + ] + }, + { + "localized_name": "CLIP", + "name": "CLIP", + "type": "CLIP", + "slot_index": 1, + "links": [ + 3, + 5 + ] + }, + { + "localized_name": "VAE", + "name": "VAE", + "type": "VAE", + "slot_index": 2, + "links": [ + 8 + ] + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "v1-5-pruned-emaonly-fp16.safetensors" + ] + } + ], + "groups": [], + "links": [ + { + "id": 5, + "origin_id": 4, + "origin_slot": 1, + "target_id": 7, + "target_slot": 0, + "type": "CLIP" + }, + { + "id": 6, + "origin_id": 7, + "origin_slot": 0, + "target_id": -20, + "target_slot": 0, + "type": "CONDITIONING" + }, + { + "id": 2, + "origin_id": 5, + "origin_slot": 0, + "target_id": -20, + "target_slot": 1, + "type": "LATENT" + }, + { + "id": 1, + "origin_id": 4, + "origin_slot": 0, + "target_id": -20, + "target_slot": 2, + "type": "MODEL" + }, + { + "id": 3, + "origin_id": 4, + "origin_slot": 1, + "target_id": -20, + "target_slot": 3, + "type": "CLIP" + }, + { + "id": 8, + "origin_id": 4, + "origin_slot": 2, + "target_id": -20, + "target_slot": 4, + "type": "VAE" + } + ], + "extra": {} + } + ] + }, + "config": {}, + "extra": { + "frontendVersion": "1.24.0-1" + }, + "version": 0.4 +} \ No newline at end of file diff --git a/browser_tests/tests/subgraphBreadcrumb.spec.ts b/browser_tests/tests/subgraphBreadcrumb.spec.ts new file mode 100644 index 000000000..d0bcb3360 --- /dev/null +++ b/browser_tests/tests/subgraphBreadcrumb.spec.ts @@ -0,0 +1,82 @@ +import { expect } from '@playwright/test' + +import { comfyPageFixture as test } from '../fixtures/ComfyPage' + +test.describe('Subgraph Breadcrumb Title Sync', () => { + test.beforeEach(async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.UseNewMenu', 'Top') + }) + + test('Breadcrumb updates when subgraph node title is changed', async ({ + comfyPage + }) => { + // Load a workflow with subgraphs + await comfyPage.loadWorkflow('nested-subgraph') + await comfyPage.nextFrame() + + // Get the subgraph node by ID (node 10 is the subgraph) + const subgraphNode = await comfyPage.getNodeRefById('10') + + // Get node position and double-click on it to enter the subgraph + const nodePos = await subgraphNode.getPosition() + const nodeSize = await subgraphNode.getSize() + await comfyPage.canvas.dblclick({ + position: { + x: nodePos.x + nodeSize.width / 2, + y: nodePos.y + nodeSize.height / 2 + 10 + } + }) + await comfyPage.nextFrame() + + // Wait for breadcrumb to appear + await comfyPage.page.waitForSelector('.subgraph-breadcrumb', { + state: 'visible', + timeout: 20000 + }) + + // Get initial breadcrumb text + const breadcrumb = comfyPage.page.locator('.subgraph-breadcrumb') + const initialBreadcrumbText = await breadcrumb.textContent() + + // Go back to main graph + await comfyPage.page.keyboard.press('Escape') + + // Double-click on the title area of the subgraph node to edit + await comfyPage.canvas.dblclick({ + position: { + x: nodePos.x + nodeSize.width / 2, + y: nodePos.y - 10 // Title area is above the node body + }, + delay: 5 + }) + + // Wait for title editor to appear + await expect(comfyPage.page.locator('.node-title-editor')).toBeVisible() + + // Clear existing text and type new title + await comfyPage.page.keyboard.press('Control+a') + const newTitle = 'Updated Subgraph Title' + await comfyPage.page.keyboard.type(newTitle) + await comfyPage.page.keyboard.press('Enter') + + // Wait a frame for the update to complete + await comfyPage.nextFrame() + + // Enter the subgraph again + await comfyPage.canvas.dblclick({ + position: { + x: nodePos.x + nodeSize.width / 2, + y: nodePos.y + nodeSize.height / 2 + }, + delay: 5 + }) + + // Wait for breadcrumb + await comfyPage.page.waitForSelector('.subgraph-breadcrumb') + + // Check that breadcrumb now shows the new title + const updatedBreadcrumbText = await breadcrumb.textContent() + expect(updatedBreadcrumbText).toContain(newTitle) + expect(updatedBreadcrumbText).not.toBe(initialBreadcrumbText) + }) +}) diff --git a/src/components/graph/TitleEditor.vue b/src/components/graph/TitleEditor.vue index eabc03bb3..f3e49e77f 100644 --- a/src/components/graph/TitleEditor.vue +++ b/src/components/graph/TitleEditor.vue @@ -41,7 +41,15 @@ const previousCanvasDraggable = ref(true) const onEdit = (newValue: string) => { if (titleEditorStore.titleEditorTarget && newValue.trim() !== '') { - titleEditorStore.titleEditorTarget.title = newValue.trim() + const trimmedTitle = newValue.trim() + titleEditorStore.titleEditorTarget.title = trimmedTitle + + // If this is a subgraph node, sync the runtime subgraph name for breadcrumb reactivity + const target = titleEditorStore.titleEditorTarget + if (target instanceof LGraphNode && target.isSubgraphNode?.()) { + target.subgraph.name = trimmedTitle + } + app.graph.setDirtyCanvas(true, true) } showInput.value = false From 8b8caa4b298cd04b0b2a066967e131fba93d9d22 Mon Sep 17 00:00:00 2001 From: Benjamin Lu Date: Mon, 14 Jul 2025 00:21:29 -0400 Subject: [PATCH 08/29] Add Danger PR Review (#4442) --- .github/workflows/danger.yaml | 27 ++ dangerfile.ts | 72 +++ package-lock.json | 806 +++++++++++++++++++++++++++++++++- package.json | 1 + 4 files changed, 898 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/danger.yaml create mode 100644 dangerfile.ts diff --git a/.github/workflows/danger.yaml b/.github/workflows/danger.yaml new file mode 100644 index 000000000..68520a8cb --- /dev/null +++ b/.github/workflows/danger.yaml @@ -0,0 +1,27 @@ +name: Danger PR Review +on: + pull_request: + types: [opened, edited, synchronize] + +jobs: + danger: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Install dependencies + run: npm ci + + - name: Run Danger + run: npx danger ci + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/dangerfile.ts b/dangerfile.ts new file mode 100644 index 000000000..6b4b69ab2 --- /dev/null +++ b/dangerfile.ts @@ -0,0 +1,72 @@ +import { danger, fail } from 'danger' + +// Check if we should run the checks +const shouldRunChecks = async () => { + const allChangedFiles = [ + ...danger.git.modified_files, + ...danger.git.created_files + ] + const srcChanges = allChangedFiles.filter((file) => file.startsWith('src/')) + + if (srcChanges.length === 0) { + return false + } + + // Check total lines changed in src files + let totalLinesChanged = 0 + for (const file of srcChanges) { + const diff = await danger.git.diffForFile(file) + if (diff) { + // Count only lines with actual content (non-empty after trimming whitespace) + // This excludes empty lines and lines containing only spaces/tabs + const additions = + diff.added?.split('\n').filter((line) => line.trim()).length || 0 + const deletions = + diff.removed?.split('\n').filter((line) => line.trim()).length || 0 + totalLinesChanged += additions + deletions + } + } + + return totalLinesChanged > 3 +} + +// Check if browser tests were updated +const checkBrowserTestCoverage = () => { + const allChangedFiles = [ + ...danger.git.modified_files, + ...danger.git.created_files + ] + const hasBrowserTestChanges = allChangedFiles.some( + (file) => file.startsWith('browser_tests/') && file.endsWith('.ts') + ) + + if (!hasBrowserTestChanges) { + fail(`🧪 **E2E Test Coverage Missing** + +All changes should be covered under E2E testing. Please add or update browser tests.`) + } +} + +// Check for screen recording in PR description +const checkScreenRecording = () => { + const description = danger.github.pr.body || '' + const hasRecording = + /github\.com\/user-attachments\/assets\/[a-f0-9-]+/i.test(description) || + /youtube\.com\/watch|youtu\.be\//i.test(description) + + if (!hasRecording) { + fail(`📹 **Visual Documentation Missing** + +Please add a screen recording or screenshot: +- GitHub: Drag & drop media to PR description +- YouTube: Add YouTube link`) + } +} + +// Run the checks only if conditions are met +shouldRunChecks().then((shouldRun) => { + if (shouldRun) { + checkBrowserTestCoverage() + checkScreenRecording() + } +}) diff --git a/package-lock.json b/package-lock.json index fa5cee570..961556ddb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,6 +67,7 @@ "@vue/test-utils": "^2.4.6", "autoprefixer": "^10.4.19", "chalk": "^5.3.0", + "danger": "^13.0.4", "eslint": "^9.12.0", "eslint-config-prettier": "^10.1.2", "eslint-plugin-prettier": "^5.2.6", @@ -2203,6 +2204,49 @@ "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==" }, + "node_modules/@gitbeaker/core": { + "version": "38.12.1", + "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-38.12.1.tgz", + "integrity": "sha512-8XMVcBIdVAAoxn7JtqmZ2Ee8f+AZLcCPmqEmPFOXY2jPS84y/DERISg/+sbhhb18iRy+ZsZhpWgQ/r3CkYNJOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@gitbeaker/requester-utils": "^38.12.1", + "qs": "^6.11.1", + "xcase": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@gitbeaker/requester-utils": { + "version": "38.12.1", + "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-38.12.1.tgz", + "integrity": "sha512-Rc/DgngS0YPN+AY1s9UnexKSy4Lh0bkQVAq9p7PRbRpXb33SlTeCg8eg/8+A/mrMcHgYmP0XhH8lkizyA5tBUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "qs": "^6.11.1", + "xcase": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@gitbeaker/rest": { + "version": "38.12.1", + "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-38.12.1.tgz", + "integrity": "sha512-9KMSDtJ/sIov+5pcH+CAfiJXSiuYgN0KLKQFg0HHWR2DwcjGYkcbmhoZcWsaOWOqq4kihN1l7wX91UoRxxKKTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@gitbeaker/core": "^38.12.1", + "@gitbeaker/requester-utils": "^38.12.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@grpc/grpc-js": { "version": "1.9.15", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", @@ -3003,6 +3047,173 @@ "node": ">= 8" } }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.4.4-cjs.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.4-cjs.2.tgz", + "integrity": "sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.7.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", + "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.3.2-cjs.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.2-cjs.1.tgz", + "integrity": "sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.8.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.2.tgz", + "integrity": "sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^5.0.2", + "@octokit/plugin-paginate-rest": "11.4.4-cjs.2", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, "node_modules/@one-ini/wasm": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", @@ -4305,8 +4516,6 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">= 10" } @@ -5424,8 +5633,6 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "debug": "4" }, @@ -5696,6 +5903,26 @@ "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, + "node_modules/async-retry": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz", + "integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "retry": "0.12.0" + } + }, + "node_modules/async-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -5816,6 +6043,13 @@ ], "license": "MIT" }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -6006,6 +6240,13 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6431,6 +6672,16 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -6724,6 +6975,18 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/core-js": { + "version": "3.44.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.44.0.tgz", + "integrity": "sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -6914,6 +7177,174 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/danger": { + "version": "13.0.4", + "resolved": "https://registry.npmjs.org/danger/-/danger-13.0.4.tgz", + "integrity": "sha512-IAdQ5nSJyIs4zKj6AN35ixt2B0Ce3WZUm3IFe/CMnL/Op7wV7IGg4D348U0EKNaNPP58QgXbdSk9pM+IXP1QXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@gitbeaker/rest": "^38.0.0", + "@octokit/rest": "^20.1.2", + "async-retry": "1.2.3", + "chalk": "^2.3.0", + "commander": "^2.18.0", + "core-js": "^3.8.2", + "debug": "^4.1.1", + "fast-json-patch": "^3.0.0-1", + "get-stdin": "^6.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "hyperlinker": "^1.0.0", + "ini": "^5.0.0", + "json5": "^2.2.3", + "jsonpointer": "^5.0.0", + "jsonwebtoken": "^9.0.0", + "lodash.find": "^4.6.0", + "lodash.includes": "^4.3.0", + "lodash.isobject": "^3.0.2", + "lodash.keys": "^4.0.8", + "lodash.mapvalues": "^4.6.0", + "lodash.memoize": "^4.1.2", + "memfs-or-file-map-to-github-branch": "^1.3.0", + "micromatch": "^4.0.4", + "node-cleanup": "^2.1.2", + "node-fetch": "^2.6.7", + "override-require": "^1.1.1", + "p-limit": "^2.1.0", + "parse-diff": "^0.7.0", + "parse-github-url": "^1.0.2", + "parse-link-header": "^2.0.0", + "pinpoint": "^1.1.0", + "prettyjson": "^1.2.1", + "readline-sync": "^1.4.9", + "regenerator-runtime": "^0.13.9", + "require-from-string": "^2.0.2", + "supports-hyperlinks": "^1.0.1" + }, + "bin": { + "danger": "distribution/commands/danger.js", + "danger-ci": "distribution/commands/danger-ci.js", + "danger-init": "distribution/commands/danger-init.js", + "danger-js": "distribution/commands/danger.js", + "danger-local": "distribution/commands/danger-local.js", + "danger-pr": "distribution/commands/danger-pr.js", + "danger-process": "distribution/commands/danger-process.js", + "danger-reset-status": "distribution/commands/danger-reset-status.js", + "danger-runner": "distribution/commands/danger-runner.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/danger/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/danger/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/danger/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/danger/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/danger/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/danger/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/danger/node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/danger/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/danger/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true, + "license": "MIT" + }, + "node_modules/danger/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -7126,6 +7557,13 @@ "node": ">= 0.8" } }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "license": "ISC" + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -7394,6 +7832,16 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/editorconfig": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", @@ -7642,6 +8090,16 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/escodegen": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", @@ -8360,6 +8818,13 @@ "node": ">=8.6.0" } }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -8874,6 +9339,16 @@ "node": ">= 0.4" } }, + "node_modules/get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -9215,8 +9690,6 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -9231,8 +9704,6 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "agent-base": "6", "debug": "4" @@ -9276,6 +9747,16 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/hyperlinker": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", + "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -10354,6 +10835,42 @@ "node": ">=0.10.0" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -10373,6 +10890,29 @@ "dev": true, "license": "MIT" }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10895,6 +11435,83 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "node_modules/lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", + "integrity": "sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -10902,6 +11519,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, "node_modules/log-update": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", @@ -11417,6 +12041,16 @@ "node": ">= 0.8" } }, + "node_modules/memfs-or-file-map-to-github-branch": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.3.0.tgz", + "integrity": "sha512-AzgIEodmt51dgwB3TmihTf1Fh2SmszdZskC6trFHy4v71R5shLmdjJSYI7ocVfFa7C/TE6ncb0OZ9eBg2rmkBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/rest": "*" + } + }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -12246,6 +12880,13 @@ "tslib": "^2.0.3" } }, + "node_modules/node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -12574,6 +13215,13 @@ "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", "license": "MIT" }, + "node_modules/override-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/override-require/-/override-require-1.1.1.tgz", + "integrity": "sha512-eoJ9YWxFcXbrn2U8FKT6RV+/Kj7fiGAB1VvHzbYKt8xM5ZuKZgCGvnHzDxmreEjcBH28ejg5MiOH4iyY1mQnkg==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -12663,6 +13311,16 @@ "node": ">=8" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/package-json": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-10.0.1.tgz", @@ -12746,6 +13404,26 @@ "node": ">=6" } }, + "node_modules/parse-diff": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/parse-diff/-/parse-diff-0.7.1.tgz", + "integrity": "sha512-1j3l8IKcy4yRK2W4o9EYvJLSzpAVwz4DXqCewYyx2vEwk2gcf3DBPqc8Fj4XV3K33OYJ08A8fWwyu/ykD/HUSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-github-url": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.3.tgz", + "integrity": "sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==", + "dev": true, + "license": "MIT", + "bin": { + "parse-github-url": "cli.js" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -12764,6 +13442,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-link-header": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-2.0.0.tgz", + "integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "xtend": "~4.0.1" + } + }, "node_modules/parse-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", @@ -12980,6 +13668,13 @@ } } }, + "node_modules/pinpoint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pinpoint/-/pinpoint-1.1.0.tgz", + "integrity": "sha512-+04FTD9x7Cls2rihLlo57QDCcHoLBGn5Dk51SwtFBWkUWLxZaBXyNVpCw1S+atvE7GmnFjeaRZ0WLq3UYuqAdg==", + "dev": true, + "license": "MIT" + }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -13258,6 +13953,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/prettyjson": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.5.tgz", + "integrity": "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "1.4.0", + "minimist": "^1.2.0" + }, + "bin": { + "prettyjson": "bin/prettyjson" + } + }, "node_modules/primeicons": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", @@ -13754,6 +14463,16 @@ "node": ">=8.10.0" } }, + "node_modules/readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -14900,6 +15619,53 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", + "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^2.0.0", + "supports-color": "^5.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -16008,6 +16774,13 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true, + "license": "ISC" + }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", @@ -17519,6 +18292,13 @@ } } }, + "node_modules/xcase": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz", + "integrity": "sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw==", + "dev": true, + "license": "MIT" + }, "node_modules/xdg-basedir": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", @@ -17549,6 +18329,16 @@ "optional": true, "peer": true }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 2dfd9aa87..edb2bc5de 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@vue/test-utils": "^2.4.6", "autoprefixer": "^10.4.19", "chalk": "^5.3.0", + "danger": "^13.0.4", "eslint": "^9.12.0", "eslint-config-prettier": "^10.1.2", "eslint-plugin-prettier": "^5.2.6", From 2f3c762e8544aafc20cfd2affb2f480521faaa41 Mon Sep 17 00:00:00 2001 From: Benjamin Lu Date: Mon, 14 Jul 2025 14:08:44 -0400 Subject: [PATCH 09/29] [fix] Fix Danger CI permissions for PRs from forks (#4449) It's good to have working and in ASAP, although better approaches are being researched and investigated --- .github/workflows/danger.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/danger.yaml b/.github/workflows/danger.yaml index 68520a8cb..8f66cfebd 100644 --- a/.github/workflows/danger.yaml +++ b/.github/workflows/danger.yaml @@ -1,8 +1,14 @@ name: Danger PR Review on: - pull_request: + pull_request_target: types: [opened, edited, synchronize] +permissions: + contents: read + issues: write # What Danger needs to comment on PRs + pull-requests: write + statuses: write + jobs: danger: runs-on: ubuntu-latest @@ -10,6 +16,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 with: + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - name: Setup Node.js From f3168aac89bcc0765c0553d3beecedd3ac4f70d5 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Tue, 15 Jul 2025 04:52:01 +1000 Subject: [PATCH 10/29] Revert "[fix] Fix Danger CI permissions for PRs from forks" (#4450) --- .github/workflows/danger.yaml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/danger.yaml b/.github/workflows/danger.yaml index 8f66cfebd..68520a8cb 100644 --- a/.github/workflows/danger.yaml +++ b/.github/workflows/danger.yaml @@ -1,14 +1,8 @@ name: Danger PR Review on: - pull_request_target: + pull_request: types: [opened, edited, synchronize] -permissions: - contents: read - issues: write # What Danger needs to comment on PRs - pull-requests: write - statuses: write - jobs: danger: runs-on: ubuntu-latest @@ -16,7 +10,6 @@ jobs: - name: Checkout code uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - name: Setup Node.js From be7edab141d33310acd599e291921b08c0f0cf64 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Mon, 14 Jul 2025 16:44:51 -0700 Subject: [PATCH 11/29] [feat] Add GitHub Action for automated Claude PR reviews (#4452) --- .claude/commands/comprehensive-pr-review.md | 708 ++++++++++++++++++++ .github/CLAUDE.md | 36 + .github/workflows/claude-pr-review.yml | 75 +++ 3 files changed, 819 insertions(+) create mode 100644 .claude/commands/comprehensive-pr-review.md create mode 100644 .github/CLAUDE.md create mode 100644 .github/workflows/claude-pr-review.yml diff --git a/.claude/commands/comprehensive-pr-review.md b/.claude/commands/comprehensive-pr-review.md new file mode 100644 index 000000000..a3310127c --- /dev/null +++ b/.claude/commands/comprehensive-pr-review.md @@ -0,0 +1,708 @@ +# Comprehensive PR Review for ComfyUI Frontend + + +You are performing a comprehensive code review for PR #$1 in the ComfyUI frontend repository. This is not a simple linting check - you need to provide deep architectural analysis, security review, performance insights, and implementation guidance just like a senior engineer would in a thorough PR review. + +Your review should cover: +1. Architecture and design patterns +2. Security vulnerabilities and risks +3. Performance implications +4. Code quality and maintainability +5. Integration with existing systems +6. Best practices and conventions +7. Testing considerations +8. Documentation needs + + +Arguments: PR number passed via PR_NUMBER environment variable + +## Phase 0: Initialize Variables and Helper Functions + +```bash +# Validate PR_NUMBER first thing +if [ -z "$PR_NUMBER" ]; then + echo "Error: PR_NUMBER environment variable is not set" + echo "Usage: PR_NUMBER= claude run /comprehensive-pr-review" + exit 1 +fi + +# Initialize all counters at the start +CRITICAL_COUNT=0 +HIGH_COUNT=0 +MEDIUM_COUNT=0 +LOW_COUNT=0 +ARCHITECTURE_ISSUES=0 +SECURITY_ISSUES=0 +PERFORMANCE_ISSUES=0 +QUALITY_ISSUES=0 + +# Helper function for posting review comments +post_review_comment() { + local file_path=$1 + local line_number=$2 + local severity=$3 # critical/high/medium/low + local category=$4 # architecture/security/performance/quality + local issue=$5 + local context=$6 + local suggestion=$7 + + # Update counters + case $severity in + "critical") ((CRITICAL_COUNT++)) ;; + "high") ((HIGH_COUNT++)) ;; + "medium") ((MEDIUM_COUNT++)) ;; + "low") ((LOW_COUNT++)) ;; + esac + + case $category in + "architecture") ((ARCHITECTURE_ISSUES++)) ;; + "security") ((SECURITY_ISSUES++)) ;; + "performance") ((PERFORMANCE_ISSUES++)) ;; + "quality") ((QUALITY_ISSUES++)) ;; + esac + + # Post inline comment via GitHub CLI + local comment="${issue}\n${context}\n${suggestion}" + gh pr review $PR_NUMBER --comment --body "$comment" -F - <<< "$comment" +} +``` + +## Phase 1: Environment Setup and PR Context + +```bash +# Pre-flight checks +check_prerequisites() { + # Check gh CLI is available + if ! command -v gh &> /dev/null; then + echo "Error: gh CLI is not installed" + exit 1 + fi + + # In GitHub Actions, auth is handled via GITHUB_TOKEN + if [ -n "$GITHUB_ACTIONS" ] && [ -z "$GITHUB_TOKEN" ]; then + echo "Error: GITHUB_TOKEN is not set in GitHub Actions" + exit 1 + fi + + # Check if we're authenticated + if ! gh auth status &> /dev/null; then + echo "Error: Not authenticated with GitHub. Run 'gh auth login'" + exit 1 + fi + + # Set repository if not already set + if [ -z "$REPOSITORY" ]; then + REPOSITORY="Comfy-Org/ComfyUI_frontend" + fi + + # Check PR exists and is open + PR_STATE=$(gh pr view $PR_NUMBER --repo $REPOSITORY --json state -q .state 2>/dev/null || echo "NOT_FOUND") + if [ "$PR_STATE" = "NOT_FOUND" ]; then + echo "Error: PR #$PR_NUMBER not found in $REPOSITORY" + exit 1 + elif [ "$PR_STATE" != "OPEN" ]; then + echo "Error: PR #$PR_NUMBER is not open (state: $PR_STATE)" + exit 1 + fi + + # Check API rate limits + RATE_REMAINING=$(gh api /rate_limit --jq '.rate.remaining' 2>/dev/null || echo "5000") + if [ "$RATE_REMAINING" -lt 100 ]; then + echo "Warning: Low API rate limit: $RATE_REMAINING remaining" + if [ "$RATE_REMAINING" -lt 50 ]; then + echo "Error: Insufficient API rate limit for comprehensive review" + exit 1 + fi + fi + + echo "Pre-flight checks passed" +} + +# Run pre-flight checks +check_prerequisites + +echo "Starting comprehensive review of PR #$PR_NUMBER" + +# Fetch PR information with error handling +echo "Fetching PR information..." +if ! gh pr view $PR_NUMBER --repo $REPOSITORY --json files,title,body,additions,deletions,baseRefName,headRefName > pr_info.json; then + echo "Error: Failed to fetch PR information" + exit 1 +fi + +# Extract branch names +BASE_BRANCH=$(jq -r '.baseRefName' < pr_info.json) +HEAD_BRANCH=$(jq -r '.headRefName' < pr_info.json) + +# Checkout PR branch locally for better file inspection +echo "Checking out PR branch..." +git fetch origin "pull/$PR_NUMBER/head:pr-$PR_NUMBER" +git checkout "pr-$PR_NUMBER" + +# Get changed files using git locally (much faster) +git diff --name-only "origin/$BASE_BRANCH" > changed_files.txt + +# Get the diff using git locally +git diff "origin/$BASE_BRANCH" > pr_diff.txt + +# Get detailed file changes with line numbers +git diff --name-status "origin/$BASE_BRANCH" > file_changes.txt + +# For API compatibility, create a simplified pr_files.json +echo '[]' > pr_files.json +while IFS=$'\t' read -r status file; do + if [[ "$status" != "D" ]]; then # Skip deleted files + # Get the patch for this file + patch=$(git diff "origin/$BASE_BRANCH" -- "$file" | jq -Rs .) + additions=$(git diff --numstat "origin/$BASE_BRANCH" -- "$file" | awk '{print $1}') + deletions=$(git diff --numstat "origin/$BASE_BRANCH" -- "$file" | awk '{print $2}') + + jq --arg file "$file" \ + --arg patch "$patch" \ + --arg additions "$additions" \ + --arg deletions "$deletions" \ + '. += [{ + "filename": $file, + "patch": $patch, + "additions": ($additions | tonumber), + "deletions": ($deletions | tonumber) + }]' pr_files.json > pr_files.json.tmp + mv pr_files.json.tmp pr_files.json + fi +done < file_changes.txt + +# Setup caching directory +CACHE_DIR=".claude-review-cache" +mkdir -p "$CACHE_DIR" + +# Function to cache analysis results +cache_analysis() { + local file_path=$1 + local analysis_result=$2 + local file_hash=$(git hash-object "$file_path" 2>/dev/null || echo "no-hash") + + if [ "$file_hash" != "no-hash" ]; then + echo "$analysis_result" > "$CACHE_DIR/${file_hash}.cache" + fi +} + +# Function to get cached analysis +get_cached_analysis() { + local file_path=$1 + local file_hash=$(git hash-object "$file_path" 2>/dev/null || echo "no-hash") + + if [ "$file_hash" != "no-hash" ] && [ -f "$CACHE_DIR/${file_hash}.cache" ]; then + cat "$CACHE_DIR/${file_hash}.cache" + return 0 + fi + return 1 +} + +# Clean old cache entries (older than 7 days) +find "$CACHE_DIR" -name "*.cache" -mtime +7 -delete 2>/dev/null || true +``` + +## Phase 2: Load Comprehensive Knowledge Base + +```bash +# Don't create knowledge directory until we know we need it +KNOWLEDGE_FOUND=false + +# Use local cache for knowledge base to avoid repeated downloads +KNOWLEDGE_CACHE_DIR=".claude-knowledge-cache" +mkdir -p "$KNOWLEDGE_CACHE_DIR" + +# Option to use cloned prompt library for better performance +PROMPT_LIBRARY_PATH="../comfy-claude-prompt-library" +if [ -d "$PROMPT_LIBRARY_PATH" ]; then + echo "Using local prompt library at $PROMPT_LIBRARY_PATH" + USE_LOCAL_PROMPT_LIBRARY=true +else + echo "No local prompt library found, will use GitHub API" + USE_LOCAL_PROMPT_LIBRARY=false +fi + +# Function to fetch with cache +fetch_with_cache() { + local url=$1 + local output_file=$2 + local cache_file="$KNOWLEDGE_CACHE_DIR/$(echo "$url" | sed 's/[^a-zA-Z0-9]/_/g')" + + # Check if cached version exists and is less than 1 day old + if [ -f "$cache_file" ] && [ $(find "$cache_file" -mtime -1 2>/dev/null | wc -l) -gt 0 ]; then + # Create knowledge directory only when we actually have content + if [ "$KNOWLEDGE_FOUND" = "false" ]; then + mkdir -p review_knowledge + KNOWLEDGE_FOUND=true + fi + cp "$cache_file" "$output_file" + echo "Using cached version of $(basename "$output_file")" + return 0 + fi + + # Try to fetch fresh version + if curl -s -f "$url" > "$output_file.tmp"; then + # Create knowledge directory only when we actually have content + if [ "$KNOWLEDGE_FOUND" = "false" ]; then + mkdir -p review_knowledge + KNOWLEDGE_FOUND=true + fi + mv "$output_file.tmp" "$output_file" + cp "$output_file" "$cache_file" + echo "Downloaded fresh version of $(basename "$output_file")" + return 0 + else + # If fetch failed but we have a cache, use it + if [ -f "$cache_file" ]; then + if [ "$KNOWLEDGE_FOUND" = "false" ]; then + mkdir -p review_knowledge + KNOWLEDGE_FOUND=true + fi + cp "$cache_file" "$output_file" + echo "Using stale cache for $(basename "$output_file") (download failed)" + return 0 + fi + echo "Failed to load $(basename "$output_file")" + return 1 + fi +} + +# Load REPOSITORY_GUIDE.md for deep architectural understanding +echo "Loading ComfyUI Frontend repository guide..." +if [ "$USE_LOCAL_PROMPT_LIBRARY" = "true" ] && [ -f "$PROMPT_LIBRARY_PATH/project-summaries-for-agents/ComfyUI_frontend/REPOSITORY_GUIDE.md" ]; then + if [ "$KNOWLEDGE_FOUND" = "false" ]; then + mkdir -p review_knowledge + KNOWLEDGE_FOUND=true + fi + cp "$PROMPT_LIBRARY_PATH/project-summaries-for-agents/ComfyUI_frontend/REPOSITORY_GUIDE.md" "review_knowledge/repository_guide.md" + echo "Loaded repository guide from local prompt library" +else + fetch_with_cache "https://raw.githubusercontent.com/Comfy-Org/comfy-claude-prompt-library/master/project-summaries-for-agents/ComfyUI_frontend/REPOSITORY_GUIDE.md" "review_knowledge/repository_guide.md" +fi + +# 3. Discover and load relevant knowledge folders from GitHub API +echo "Discovering available knowledge folders..." +KNOWLEDGE_API_URL="https://api.github.com/repos/Comfy-Org/comfy-claude-prompt-library/contents/.claude/knowledge" +if KNOWLEDGE_FOLDERS=$(curl -s "$KNOWLEDGE_API_URL" | jq -r '.[] | select(.type=="dir") | .name' 2>/dev/null); then + echo "Available knowledge folders: $KNOWLEDGE_FOLDERS" + + # Analyze changed files to determine which knowledge folders might be relevant + CHANGED_FILES=$(cat changed_files.txt) + PR_TITLE=$(jq -r '.title' < pr_info.json) + PR_BODY=$(jq -r '.body // ""' < pr_info.json) + + # For each knowledge folder, check if it might be relevant to the PR + for folder in $KNOWLEDGE_FOLDERS; do + # Simple heuristic: if folder name appears in changed file paths or PR context + if echo "$CHANGED_FILES $PR_TITLE $PR_BODY" | grep -qi "$folder"; then + echo "Loading knowledge folder: $folder" + # Fetch all files in that knowledge folder + FOLDER_API_URL="https://api.github.com/repos/Comfy-Org/comfy-claude-prompt-library/contents/.claude/knowledge/$folder" + curl -s "$FOLDER_API_URL" | jq -r '.[] | select(.type=="file") | .download_url' 2>/dev/null | \ + while read url; do + if [ -n "$url" ]; then + filename=$(basename "$url") + fetch_with_cache "$url" "review_knowledge/${folder}_${filename}" + fi + done + fi + done +else + echo "Could not discover knowledge folders" +fi + +# 4. Load validation rules from the repository +echo "Loading validation rules..." +VALIDATION_API_URL="https://api.github.com/repos/Comfy-Org/comfy-claude-prompt-library/contents/.claude/commands/validation" +if VALIDATION_FILES=$(curl -s "$VALIDATION_API_URL" | jq -r '.[] | select(.name | contains("frontend") or contains("security") or contains("performance")) | .download_url' 2>/dev/null); then + for url in $VALIDATION_FILES; do + if [ -n "$url" ]; then + filename=$(basename "$url") + fetch_with_cache "$url" "review_knowledge/validation_${filename}" + fi + done +else + echo "Could not load validation rules" +fi + +# 5. Load local project guidelines +if [ -f "CLAUDE.md" ]; then + if [ "$KNOWLEDGE_FOUND" = "false" ]; then + mkdir -p review_knowledge + KNOWLEDGE_FOUND=true + fi + cp CLAUDE.md review_knowledge/local_claude.md +fi +if [ -f ".github/CLAUDE.md" ]; then + if [ "$KNOWLEDGE_FOUND" = "false" ]; then + mkdir -p review_knowledge + KNOWLEDGE_FOUND=true + fi + cp .github/CLAUDE.md review_knowledge/github_claude.md +fi +``` + +## Phase 3: Deep Analysis Instructions + +Perform a comprehensive analysis covering these areas: + +### 3.1 Architectural Analysis +Based on the repository guide and project summary, evaluate: +- Does this change align with the established architecture patterns? +- Are domain boundaries respected? +- Is the extension system used appropriately? +- Are components properly organized by feature? +- Does it follow the established service/composable/store patterns? + +### 3.2 Code Quality Beyond Linting +- Cyclomatic complexity and cognitive load +- SOLID principles adherence +- DRY violations that aren't caught by simple duplication checks +- Proper abstraction levels +- Interface design and API clarity +- No leftover debug code (console.log, commented code, TODO comments) + +### 3.3 Library Usage Enforcement +CRITICAL: Never re-implement functionality that exists in our standard libraries: +- **Tailwind CSS**: Use utility classes instead of custom CSS or style attributes +- **PrimeVue**: Never re-implement components that exist in PrimeVue (buttons, modals, dropdowns, etc.) +- **VueUse**: Never re-implement composables that exist in VueUse (useLocalStorage, useDebounceFn, etc.) +- **Lodash**: Never re-implement utility functions (debounce, throttle, cloneDeep, etc.) +- **Common components**: Reuse components from src/components/common/ +- **DOMPurify**: Always use for HTML sanitization +- **Fuse.js**: Use for fuzzy search functionality +- **Marked**: Use for markdown parsing +- **Pinia**: Use for global state management, not custom solutions +- **Zod**: Use for form validation with zodResolver pattern +- **Tiptap**: Use for rich text/markdown editing +- **Xterm.js**: Use for terminal emulation +- **Axios**: Use for HTTP client initialization + +### 3.4 Security Deep Dive +Beyond obvious vulnerabilities: +- Authentication/authorization implications +- Data validation completeness +- State management security +- Cross-origin concerns +- Extension security boundaries + +### 3.5 Performance Analysis +- Render performance implications +- Layout thrashing prevention +- Memory leak potential +- Network request optimization +- State management efficiency + +### 3.6 Integration Concerns +- Breaking changes to internal APIs +- Extension compatibility +- Backward compatibility +- Migration requirements + +## Phase 4: Create Detailed Review Comments + +CRITICAL: Keep comments extremely concise and effective. Use only as many words as absolutely necessary. +- NO markdown formatting (no #, ##, ###, **, etc.) +- NO emojis +- Get to the point immediately +- Burden the reader as little as possible + +For each issue found, create a concise inline comment with: +1. What's wrong (one line) +2. Why it matters (one line) +3. How to fix it (one line) +4. Code example only if essential + +```bash +# Helper function for comprehensive comments +post_review_comment() { + local file_path=$1 + local line_number=$2 + local severity=$3 # critical/high/medium/low + local category=$4 # architecture/security/performance/quality + local issue=$5 + local context=$6 + local suggestion=$7 + local example=$8 + + local body="### [$category] $severity Priority + +**Issue**: $issue + +**Context**: $context + +**Suggestion**: $suggestion" + + if [ -n "$example" ]; then + body="$body + +**Example**: +\`\`\`typescript +$example +\`\`\`" + fi + + body="$body + +*Related: See [repository guide](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/project-summaries-for-agents/ComfyUI_frontend/REPOSITORY_GUIDE.md) for patterns*" + + gh api -X POST /repos/$REPOSITORY/pulls/$PR_NUMBER/comments \ + -f path="$file_path" \ + -f line=$line_number \ + -f body="$body" \ + -f commit_id="$COMMIT_SHA" \ + -f side='RIGHT' || echo "Failed to post comment at $file_path:$line_number" +} +``` + +## Phase 5: Validation Rules Application + +Apply ALL validation rules from the loaded knowledge, but focus on the changed lines: + +### From Frontend Standards +- Vue 3 Composition API patterns +- Component communication patterns +- Proper use of composables +- TypeScript strict mode compliance +- Bundle optimization + +### From Security Audit +- Input validation +- XSS prevention +- CSRF protection +- Secure state management +- API security + +### From Performance Check +- Render optimization +- Memory management +- Network efficiency +- Bundle size impact + +## Phase 6: Contextual Review Based on PR Type + +Analyze the PR description and changes to determine the type: + +```bash +# Extract PR metadata with error handling +if [ ! -f pr_info.json ]; then + echo "Error: pr_info.json not found" + exit 1 +fi + +PR_TITLE=$(jq -r '.title // "Unknown"' < pr_info.json) +PR_BODY=$(jq -r '.body // ""' < pr_info.json) +FILE_COUNT=$(wc -l < changed_files.txt) +ADDITIONS=$(jq -r '.additions // 0' < pr_info.json) +DELETIONS=$(jq -r '.deletions // 0' < pr_info.json) + +# Determine PR type and apply specific review criteria +if echo "$PR_TITLE $PR_BODY" | grep -qiE "(feature|feat)"; then + echo "Detected feature PR - applying feature review criteria" + # Check for tests, documentation, backward compatibility +elif echo "$PR_TITLE $PR_BODY" | grep -qiE "(fix|bug)"; then + echo "Detected bug fix - checking root cause and regression tests" + # Verify fix addresses root cause, includes tests +elif echo "$PR_TITLE $PR_BODY" | grep -qiE "(refactor)"; then + echo "Detected refactoring - ensuring behavior preservation" + # Check that tests still pass, no behavior changes +fi +``` + +## Phase 7: Generate Comprehensive Summary + +After all inline comments, create a detailed summary: + +```bash +# Initialize metrics tracking +REVIEW_START_TIME=$(date +%s) + +# Create the comprehensive summary +gh pr review $PR_NUMBER --comment --body "# Comprehensive PR Review + +This review is generated by Claude. It may not always be accurate, as with human reviewers. If you believe that any of the comments are invalid or incorrect, please state why for each. For others, please implement the changes in one way or another. + +## Review Summary + +**PR**: $PR_TITLE (#$PR_NUMBER) +**Impact**: $ADDITIONS additions, $DELETIONS deletions across $FILE_COUNT files + +### Issue Distribution +- Critical: $CRITICAL_COUNT +- High: $HIGH_COUNT +- Medium: $MEDIUM_COUNT +- Low: $LOW_COUNT + +### Category Breakdown +- Architecture: $ARCHITECTURE_ISSUES issues +- Security: $SECURITY_ISSUES issues +- Performance: $PERFORMANCE_ISSUES issues +- Code Quality: $QUALITY_ISSUES issues + +## Key Findings + +### Architecture & Design +[Detailed architectural analysis based on repository patterns] + +### Security Considerations +[Security implications beyond basic vulnerabilities] + +### Performance Impact +[Performance analysis including bundle size, render impact] + +### Integration Points +[How this affects other systems, extensions, etc.] + +## Positive Observations +[What was done well, good patterns followed] + +## References +- [Repository Architecture Guide](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/project-summaries-for-agents/ComfyUI_frontend/REPOSITORY_GUIDE.md) +- [Frontend Standards](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/.claude/commands/validation/frontend-code-standards.md) +- [Security Guidelines](https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/.claude/commands/validation/security-audit.md) + +## Next Steps +1. Address critical issues before merge +2. Consider architectural feedback for long-term maintainability +3. Add tests for uncovered scenarios +4. Update documentation if needed + +--- +*This is a comprehensive automated review. For architectural decisions requiring human judgment, please request additional manual review.*" +``` + +## Important: Think Deeply + +When reviewing: +1. **Think hard** about architectural implications +2. Consider how changes affect the entire system +3. Look for subtle bugs and edge cases +4. Evaluate maintainability over time +5. Consider extension developer experience +6. Think about migration paths + +This is a COMPREHENSIVE review, not a linting pass. Provide the same quality feedback a senior engineer would give after careful consideration. + +## Phase 8: Track Review Metrics + +After completing the review, save metrics for analysis: + +```bash +# Calculate review duration +REVIEW_END_TIME=$(date +%s) +REVIEW_DURATION=$((REVIEW_END_TIME - REVIEW_START_TIME)) + +# Calculate total issues +TOTAL_ISSUES=$((CRITICAL_COUNT + HIGH_COUNT + MEDIUM_COUNT + LOW_COUNT)) + +# Create metrics directory if it doesn't exist +METRICS_DIR=".claude/review-metrics" +mkdir -p "$METRICS_DIR" + +# Generate metrics file +METRICS_FILE="$METRICS_DIR/metrics-$(date +%Y%m).json" + +# Create or update monthly metrics file +if [ -f "$METRICS_FILE" ]; then + # Append to existing file + jq -n \ + --arg pr "$PR_NUMBER" \ + --arg title "$PR_TITLE" \ + --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + --arg duration "$REVIEW_DURATION" \ + --arg files "$FILE_COUNT" \ + --arg additions "$ADDITIONS" \ + --arg deletions "$DELETIONS" \ + --arg total "$TOTAL_ISSUES" \ + --arg critical "$CRITICAL_COUNT" \ + --arg high "$HIGH_COUNT" \ + --arg medium "$MEDIUM_COUNT" \ + --arg low "$LOW_COUNT" \ + --arg architecture "$ARCHITECTURE_ISSUES" \ + --arg security "$SECURITY_ISSUES" \ + --arg performance "$PERFORMANCE_ISSUES" \ + --arg quality "$QUALITY_ISSUES" \ + '{ + pr_number: $pr, + pr_title: $title, + timestamp: $timestamp, + review_duration_seconds: ($duration | tonumber), + files_reviewed: ($files | tonumber), + lines_added: ($additions | tonumber), + lines_deleted: ($deletions | tonumber), + issues: { + total: ($total | tonumber), + by_severity: { + critical: ($critical | tonumber), + high: ($high | tonumber), + medium: ($medium | tonumber), + low: ($low | tonumber) + }, + by_category: { + architecture: ($architecture | tonumber), + security: ($security | tonumber), + performance: ($performance | tonumber), + quality: ($quality | tonumber) + } + } + }' > "$METRICS_FILE.new" + + # Merge with existing data + jq -s '.[0] + [.[1]]' "$METRICS_FILE" "$METRICS_FILE.new" > "$METRICS_FILE.tmp" + mv "$METRICS_FILE.tmp" "$METRICS_FILE" + rm "$METRICS_FILE.new" +else + # Create new file + jq -n \ + --arg pr "$PR_NUMBER" \ + --arg title "$PR_TITLE" \ + --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + --arg duration "$REVIEW_DURATION" \ + --arg files "$FILE_COUNT" \ + --arg additions "$ADDITIONS" \ + --arg deletions "$DELETIONS" \ + --arg total "$TOTAL_ISSUES" \ + --arg critical "$CRITICAL_COUNT" \ + --arg high "$HIGH_COUNT" \ + --arg medium "$MEDIUM_COUNT" \ + --arg low "$LOW_COUNT" \ + --arg architecture "$ARCHITECTURE_ISSUES" \ + --arg security "$SECURITY_ISSUES" \ + --arg performance "$PERFORMANCE_ISSUES" \ + --arg quality "$QUALITY_ISSUES" \ + '[{ + pr_number: $pr, + pr_title: $title, + timestamp: $timestamp, + review_duration_seconds: ($duration | tonumber), + files_reviewed: ($files | tonumber), + lines_added: ($additions | tonumber), + lines_deleted: ($deletions | tonumber), + issues: { + total: ($total | tonumber), + by_severity: { + critical: ($critical | tonumber), + high: ($high | tonumber), + medium: ($medium | tonumber), + low: ($low | tonumber) + }, + by_category: { + architecture: ($architecture | tonumber), + security: ($security | tonumber), + performance: ($performance | tonumber), + quality: ($quality | tonumber) + } + } + }]' > "$METRICS_FILE" +fi + +echo "Review metrics saved to $METRICS_FILE" +``` + +This creates monthly metrics files (e.g., `metrics-202407.json`) that track: +- Which PRs were reviewed +- How long reviews took +- Types and severity of issues found +- Trends over time + +You can later analyze these to see patterns and improve your development process. \ No newline at end of file diff --git a/.github/CLAUDE.md b/.github/CLAUDE.md new file mode 100644 index 000000000..9a95d8cd0 --- /dev/null +++ b/.github/CLAUDE.md @@ -0,0 +1,36 @@ +# ComfyUI Frontend - Claude Review Context + +This file provides additional context for the automated PR review system. + +## Quick Reference + +### PrimeVue Component Migrations + +When reviewing, flag these deprecated components: +- `Dropdown` → Use `Select` from 'primevue/select' +- `OverlayPanel` → Use `Popover` from 'primevue/popover' +- `Calendar` → Use `DatePicker` from 'primevue/datepicker' +- `InputSwitch` → Use `ToggleSwitch` from 'primevue/toggleswitch' +- `Sidebar` → Use `Drawer` from 'primevue/drawer' +- `Chips` → Use `AutoComplete` with multiple enabled and typeahead disabled +- `TabMenu` → Use `Tabs` without panels +- `Steps` → Use `Stepper` without panels +- `InlineMessage` → Use `Message` component + +### API Utilities Reference + +- `api.apiURL()` - Backend API calls (/prompt, /queue, /view, etc.) +- `api.fileURL()` - Static file access (templates, extensions) +- `$t()` / `i18n.global.t()` - Internationalization +- `DOMPurify.sanitize()` - HTML sanitization + +## Review Scope + +This automated review performs comprehensive analysis including: +- Architecture and design patterns +- Security vulnerabilities +- Performance implications +- Code quality and maintainability +- Integration concerns + +For implementation details, see `.claude/commands/comprehensive-pr-review.md`. \ No newline at end of file diff --git a/.github/workflows/claude-pr-review.yml b/.github/workflows/claude-pr-review.yml new file mode 100644 index 000000000..1b7f86934 --- /dev/null +++ b/.github/workflows/claude-pr-review.yml @@ -0,0 +1,75 @@ +name: Claude PR Review + +permissions: + contents: read + pull-requests: write + issues: write + +on: + pull_request: + types: [labeled] + +jobs: + wait-for-ci: + runs-on: ubuntu-latest + if: github.event.label.name == 'claude-review' + outputs: + should-proceed: ${{ steps.check-status.outputs.proceed }} + steps: + - name: Wait for other CI checks + uses: lewagon/wait-on-check-action@v1.3.1 + with: + ref: ${{ github.event.pull_request.head.sha }} + check-regexp: '^(ESLint|Prettier Check|Tests CI|Vitest Tests)' + wait-interval: 30 + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if we should proceed + id: check-status + run: | + # Get all check runs for this commit + CHECK_RUNS=$(gh api repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs --jq '.check_runs[] | select(.name | test("ESLint|Prettier Check|Tests CI|Vitest Tests")) | {name, conclusion}') + + # Check if any required checks failed + if echo "$CHECK_RUNS" | grep -q '"conclusion": "failure"'; then + echo "Some CI checks failed - skipping Claude review" + echo "proceed=false" >> $GITHUB_OUTPUT + else + echo "All CI checks passed - proceeding with Claude review" + echo "proceed=true" >> $GITHUB_OUTPUT + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + claude-review: + needs: wait-for-ci + if: needs.wait-for-ci.outputs.should-proceed == 'true' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies for analysis tools + run: | + npm install -g typescript @vue/compiler-sfc + + - name: Run Claude PR Review + uses: anthropics/claude-code-action@main + with: + prompt_file: .claude/commands/comprehensive-pr-review.md + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + max_turns: 1 + timeout_minutes: 30 + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + REPOSITORY: ${{ github.repository }} \ No newline at end of file From 1200c07fcd6e14a4c3a8734468bd6cf694c23205 Mon Sep 17 00:00:00 2001 From: guill Date: Tue, 15 Jul 2025 15:59:11 -0700 Subject: [PATCH 12/29] Add support for Feature Flags (#4439) --- browser_tests/tests/featureFlags.spec.ts | 382 +++++++++++++++++++++++ src/config/clientFeatureFlags.json | 3 + src/schemas/apiSchema.ts | 3 + src/scripts/api.ts | 60 ++++ tests-ui/tests/api.featureFlags.test.ts | 208 ++++++++++++ 5 files changed, 656 insertions(+) create mode 100644 browser_tests/tests/featureFlags.spec.ts create mode 100644 src/config/clientFeatureFlags.json create mode 100644 tests-ui/tests/api.featureFlags.test.ts diff --git a/browser_tests/tests/featureFlags.spec.ts b/browser_tests/tests/featureFlags.spec.ts new file mode 100644 index 000000000..73eb35f47 --- /dev/null +++ b/browser_tests/tests/featureFlags.spec.ts @@ -0,0 +1,382 @@ +import { expect } from '@playwright/test' + +import { comfyPageFixture as test } from '../fixtures/ComfyPage' + +test.describe('Feature Flags', () => { + test('Client and server exchange feature flags on connection', async ({ + comfyPage + }) => { + // Navigate to a new page to capture the initial WebSocket connection + const newPage = await comfyPage.page.context().newPage() + + // Set up monitoring before navigation + await newPage.addInitScript(() => { + // This runs before any page scripts + window.__capturedMessages = { + clientFeatureFlags: null, + serverFeatureFlags: null + } + + // Capture outgoing client messages + const originalSend = WebSocket.prototype.send + WebSocket.prototype.send = function (data) { + try { + const parsed = JSON.parse(data) + if (parsed.type === 'feature_flags') { + window.__capturedMessages.clientFeatureFlags = parsed + } + } catch (e) { + // Not JSON, ignore + } + return originalSend.call(this, data) + } + + // Monitor for server feature flags + const checkInterval = setInterval(() => { + if ( + window['app']?.api?.serverFeatureFlags && + Object.keys(window['app'].api.serverFeatureFlags).length > 0 + ) { + window.__capturedMessages.serverFeatureFlags = + window['app'].api.serverFeatureFlags + clearInterval(checkInterval) + } + }, 100) + + // Clear after 10 seconds + setTimeout(() => clearInterval(checkInterval), 10000) + }) + + // Navigate to the app + await newPage.goto(comfyPage.url) + + // Wait for both client and server feature flags + await newPage.waitForFunction( + () => + window.__capturedMessages.clientFeatureFlags !== null && + window.__capturedMessages.serverFeatureFlags !== null, + { timeout: 10000 } + ) + + // Get the captured messages + const messages = await newPage.evaluate(() => window.__capturedMessages) + + // Verify client sent feature flags + expect(messages.clientFeatureFlags).toBeTruthy() + expect(messages.clientFeatureFlags).toHaveProperty('type', 'feature_flags') + expect(messages.clientFeatureFlags).toHaveProperty('data') + expect(messages.clientFeatureFlags.data).toHaveProperty( + 'supports_preview_metadata' + ) + expect( + typeof messages.clientFeatureFlags.data.supports_preview_metadata + ).toBe('boolean') + + // Verify server sent feature flags back + expect(messages.serverFeatureFlags).toBeTruthy() + expect(messages.serverFeatureFlags).toHaveProperty( + 'supports_preview_metadata' + ) + expect(typeof messages.serverFeatureFlags.supports_preview_metadata).toBe( + 'boolean' + ) + expect(messages.serverFeatureFlags).toHaveProperty('max_upload_size') + expect(typeof messages.serverFeatureFlags.max_upload_size).toBe('number') + expect(Object.keys(messages.serverFeatureFlags).length).toBeGreaterThan(0) + + await newPage.close() + }) + + test('Server feature flags are received and accessible', async ({ + comfyPage + }) => { + // Wait for connection to establish + await comfyPage.page.waitForTimeout(1000) + + // Get the actual server feature flags from the backend + const serverFlags = await comfyPage.page.evaluate(() => { + return window['app'].api.serverFeatureFlags + }) + + // Verify we received real feature flags from the backend + expect(serverFlags).toBeTruthy() + expect(Object.keys(serverFlags).length).toBeGreaterThan(0) + + // The backend should send feature flags + expect(serverFlags).toHaveProperty('supports_preview_metadata') + expect(typeof serverFlags.supports_preview_metadata).toBe('boolean') + expect(serverFlags).toHaveProperty('max_upload_size') + expect(typeof serverFlags.max_upload_size).toBe('number') + }) + + test('serverSupportsFeature method works with real backend flags', async ({ + comfyPage + }) => { + // Wait for connection + await comfyPage.page.waitForTimeout(1000) + + // Test serverSupportsFeature with real backend flags + const supportsPreviewMetadata = await comfyPage.page.evaluate(() => { + return window['app'].api.serverSupportsFeature( + 'supports_preview_metadata' + ) + }) + // The method should return a boolean based on the backend's value + expect(typeof supportsPreviewMetadata).toBe('boolean') + + // Test non-existent feature - should always return false + const supportsNonExistent = await comfyPage.page.evaluate(() => { + return window['app'].api.serverSupportsFeature('non_existent_feature_xyz') + }) + expect(supportsNonExistent).toBe(false) + + // Test that the method only returns true for boolean true values + const testResults = await comfyPage.page.evaluate(() => { + // Temporarily modify serverFeatureFlags to test behavior + const original = window['app'].api.serverFeatureFlags + window['app'].api.serverFeatureFlags = { + bool_true: true, + bool_false: false, + string_value: 'yes', + number_value: 1, + null_value: null + } + + const results = { + bool_true: window['app'].api.serverSupportsFeature('bool_true'), + bool_false: window['app'].api.serverSupportsFeature('bool_false'), + string_value: window['app'].api.serverSupportsFeature('string_value'), + number_value: window['app'].api.serverSupportsFeature('number_value'), + null_value: window['app'].api.serverSupportsFeature('null_value') + } + + // Restore original + window['app'].api.serverFeatureFlags = original + return results + }) + + // serverSupportsFeature should only return true for boolean true values + expect(testResults.bool_true).toBe(true) + expect(testResults.bool_false).toBe(false) + expect(testResults.string_value).toBe(false) + expect(testResults.number_value).toBe(false) + expect(testResults.null_value).toBe(false) + }) + + test('getServerFeature method works with real backend data', async ({ + comfyPage + }) => { + // Wait for connection + await comfyPage.page.waitForTimeout(1000) + + // Test getServerFeature method + const previewMetadataValue = await comfyPage.page.evaluate(() => { + return window['app'].api.getServerFeature('supports_preview_metadata') + }) + expect(typeof previewMetadataValue).toBe('boolean') + + // Test getting max_upload_size + const maxUploadSize = await comfyPage.page.evaluate(() => { + return window['app'].api.getServerFeature('max_upload_size') + }) + expect(typeof maxUploadSize).toBe('number') + expect(maxUploadSize).toBeGreaterThan(0) + + // Test getServerFeature with default value for non-existent feature + const defaultValue = await comfyPage.page.evaluate(() => { + return window['app'].api.getServerFeature( + 'non_existent_feature_xyz', + 'default' + ) + }) + expect(defaultValue).toBe('default') + }) + + test('getServerFeatures returns all backend feature flags', async ({ + comfyPage + }) => { + // Wait for connection + await comfyPage.page.waitForTimeout(1000) + + // Test getServerFeatures returns all flags + const allFeatures = await comfyPage.page.evaluate(() => { + return window['app'].api.getServerFeatures() + }) + + expect(allFeatures).toBeTruthy() + expect(allFeatures).toHaveProperty('supports_preview_metadata') + expect(typeof allFeatures.supports_preview_metadata).toBe('boolean') + expect(allFeatures).toHaveProperty('max_upload_size') + expect(Object.keys(allFeatures).length).toBeGreaterThan(0) + }) + + test('Client feature flags are immutable', async ({ comfyPage }) => { + // Test that getClientFeatureFlags returns a copy + const immutabilityTest = await comfyPage.page.evaluate(() => { + const flags1 = window['app'].api.getClientFeatureFlags() + const flags2 = window['app'].api.getClientFeatureFlags() + + // Modify the first object + flags1.test_modification = true + + // Get flags again to check if original was modified + const flags3 = window['app'].api.getClientFeatureFlags() + + return { + areEqual: flags1 === flags2, + hasModification: flags3.test_modification !== undefined, + hasSupportsPreview: flags1.supports_preview_metadata !== undefined, + supportsPreviewValue: flags1.supports_preview_metadata + } + }) + + // Verify they are different objects (not the same reference) + expect(immutabilityTest.areEqual).toBe(false) + + // Verify modification didn't affect the original + expect(immutabilityTest.hasModification).toBe(false) + + // Verify the flags contain expected properties + expect(immutabilityTest.hasSupportsPreview).toBe(true) + expect(typeof immutabilityTest.supportsPreviewValue).toBe('boolean') // From clientFeatureFlags.json + }) + + test('Server features are immutable when accessed via getServerFeatures', async ({ + comfyPage + }) => { + // Wait for connection to establish + await comfyPage.page.waitForTimeout(1000) + + const immutabilityTest = await comfyPage.page.evaluate(() => { + // Get a copy of server features + const features1 = window['app'].api.getServerFeatures() + + // Try to modify it + features1.supports_preview_metadata = false + features1.new_feature = 'added' + + // Get another copy + const features2 = window['app'].api.getServerFeatures() + + return { + modifiedValue: features1.supports_preview_metadata, + originalValue: features2.supports_preview_metadata, + hasNewFeature: features2.new_feature !== undefined, + hasSupportsPreview: features2.supports_preview_metadata !== undefined + } + }) + + // The modification should only affect the copy + expect(immutabilityTest.modifiedValue).toBe(false) + expect(typeof immutabilityTest.originalValue).toBe('boolean') // Backend sends boolean for supports_preview_metadata + expect(immutabilityTest.hasNewFeature).toBe(false) + expect(immutabilityTest.hasSupportsPreview).toBe(true) + }) + + test('Feature flags are negotiated early in connection lifecycle', async ({ + comfyPage + }) => { + // This test verifies that feature flags are available early in the app lifecycle + // which is important for protocol negotiation + + // Create a new page to ensure clean state + const newPage = await comfyPage.page.context().newPage() + + // Set up monitoring before navigation + await newPage.addInitScript(() => { + // Track when various app components are ready + ;(window as any).__appReadiness = { + featureFlagsReceived: false, + apiInitialized: false, + appInitialized: false + } + + // Monitor when feature flags arrive by checking periodically + const checkFeatureFlags = setInterval(() => { + if ( + window['app']?.api?.serverFeatureFlags?.supports_preview_metadata !== + undefined + ) { + ;(window as any).__appReadiness.featureFlagsReceived = true + clearInterval(checkFeatureFlags) + } + }, 10) + + // Monitor API initialization + const checkApi = setInterval(() => { + if (window['app']?.api) { + ;(window as any).__appReadiness.apiInitialized = true + clearInterval(checkApi) + } + }, 10) + + // Monitor app initialization + const checkApp = setInterval(() => { + if (window['app']?.graph) { + ;(window as any).__appReadiness.appInitialized = true + clearInterval(checkApp) + } + }, 10) + + // Clean up after 10 seconds + setTimeout(() => { + clearInterval(checkFeatureFlags) + clearInterval(checkApi) + clearInterval(checkApp) + }, 10000) + }) + + // Navigate to the app + await newPage.goto(comfyPage.url) + + // Wait for feature flags to be received + await newPage.waitForFunction( + () => + window['app']?.api?.serverFeatureFlags?.supports_preview_metadata !== + undefined, + { + timeout: 10000 + } + ) + + // Get readiness state + const readiness = await newPage.evaluate(() => { + return { + ...(window as any).__appReadiness, + currentFlags: window['app'].api.serverFeatureFlags + } + }) + + // Verify feature flags are available + expect(readiness.currentFlags).toHaveProperty('supports_preview_metadata') + expect(typeof readiness.currentFlags.supports_preview_metadata).toBe( + 'boolean' + ) + expect(readiness.currentFlags).toHaveProperty('max_upload_size') + + // Verify feature flags were received (we detected them via polling) + expect(readiness.featureFlagsReceived).toBe(true) + + // Verify API was initialized (feature flags require API) + expect(readiness.apiInitialized).toBe(true) + + await newPage.close() + }) + + test('Backend /features endpoint returns feature flags', async ({ + comfyPage + }) => { + // Test the HTTP endpoint directly + const response = await comfyPage.page.request.get( + `${comfyPage.url}/api/features` + ) + expect(response.ok()).toBe(true) + + const features = await response.json() + expect(features).toBeTruthy() + expect(features).toHaveProperty('supports_preview_metadata') + expect(typeof features.supports_preview_metadata).toBe('boolean') + expect(features).toHaveProperty('max_upload_size') + expect(Object.keys(features).length).toBeGreaterThan(0) + }) +}) diff --git a/src/config/clientFeatureFlags.json b/src/config/clientFeatureFlags.json new file mode 100644 index 000000000..ebd6bcf60 --- /dev/null +++ b/src/config/clientFeatureFlags.json @@ -0,0 +1,3 @@ +{ + "supports_preview_metadata": false +} diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index 010ca077e..12fbe2981 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -113,6 +113,8 @@ const zLogRawResponse = z.object({ entries: z.array(zLogEntry) }) +const zFeatureFlagsWsMessage = z.record(z.string(), z.any()) + export type StatusWsMessageStatus = z.infer export type StatusWsMessage = z.infer export type ProgressWsMessage = z.infer @@ -132,6 +134,7 @@ export type ProgressTextWsMessage = z.infer export type DisplayComponentWsMessage = z.infer< typeof zDisplayComponentWsMessage > +export type FeatureFlagsWsMessage = z.infer // End of ws messages const zPromptInputItem = z.object({ diff --git a/src/scripts/api.ts b/src/scripts/api.ts index b17df5bac..7be323588 100644 --- a/src/scripts/api.ts +++ b/src/scripts/api.ts @@ -1,5 +1,6 @@ import axios from 'axios' +import defaultClientFeatureFlags from '@/config/clientFeatureFlags.json' import type { DisplayComponentWsMessage, EmbeddingsResponse, @@ -11,6 +12,7 @@ import type { ExecutionStartWsMessage, ExecutionSuccessWsMessage, ExtensionsResponse, + FeatureFlagsWsMessage, HistoryTaskItem, LogsRawResponse, LogsWsMessage, @@ -105,6 +107,7 @@ interface BackendApiCalls { b_preview: Blob progress_text: ProgressTextWsMessage display_component: DisplayComponentWsMessage + feature_flags: FeatureFlagsWsMessage } /** Dictionary of all api calls */ @@ -234,6 +237,19 @@ export class ComfyApi extends EventTarget { reportedUnknownMessageTypes = new Set() + /** + * Get feature flags supported by this frontend client. + * Returns a copy to prevent external modification. + */ + getClientFeatureFlags(): Record { + return { ...defaultClientFeatureFlags } + } + + /** + * Feature flags received from the backend server. + */ + serverFeatureFlags: Record = {} + /** * The auth token for the comfy org account if the user is logged in. * This is only used for {@link queuePrompt} now. It is not directly @@ -375,6 +391,15 @@ export class ComfyApi extends EventTarget { this.socket.addEventListener('open', () => { opened = true + + // Send feature flags as the first message + this.socket!.send( + JSON.stringify({ + type: 'feature_flags', + data: this.getClientFeatureFlags() + }) + ) + if (isReconnect) { this.dispatchCustomEvent('reconnected') } @@ -468,6 +493,14 @@ export class ComfyApi extends EventTarget { case 'b_preview': this.dispatchCustomEvent(msg.type, msg.data) break + case 'feature_flags': + // Store server feature flags + this.serverFeatureFlags = msg.data + console.log( + 'Server feature flags received:', + this.serverFeatureFlags + ) + break default: if (this.#registered.has(msg.type)) { // Fallback for custom types - calls super direct. @@ -962,6 +995,33 @@ export class ComfyApi extends EventTarget { async getCustomNodesI18n(): Promise> { return (await axios.get(this.apiURL('/i18n'))).data } + + /** + * Checks if the server supports a specific feature. + * @param featureName The name of the feature to check + * @returns true if the feature is supported, false otherwise + */ + serverSupportsFeature(featureName: string): boolean { + return this.serverFeatureFlags[featureName] === true + } + + /** + * Gets a server feature flag value. + * @param featureName The name of the feature to get + * @param defaultValue The default value if the feature is not found + * @returns The feature value or default + */ + getServerFeature(featureName: string, defaultValue?: T): T { + return (this.serverFeatureFlags[featureName] ?? defaultValue) as T + } + + /** + * Gets all server feature flags. + * @returns Copy of all server feature flags + */ + getServerFeatures(): Record { + return { ...this.serverFeatureFlags } + } } export const api = new ComfyApi() diff --git a/tests-ui/tests/api.featureFlags.test.ts b/tests-ui/tests/api.featureFlags.test.ts new file mode 100644 index 000000000..42399e859 --- /dev/null +++ b/tests-ui/tests/api.featureFlags.test.ts @@ -0,0 +1,208 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +import { api } from '@/scripts/api' + +describe('API Feature Flags', () => { + let mockWebSocket: any + const wsEventHandlers: { [key: string]: (event: any) => void } = {} + + beforeEach(() => { + // Use fake timers + vi.useFakeTimers() + + // Mock WebSocket + mockWebSocket = { + readyState: 1, // WebSocket.OPEN + send: vi.fn(), + close: vi.fn(), + addEventListener: vi.fn( + (event: string, handler: (event: any) => void) => { + wsEventHandlers[event] = handler + } + ), + removeEventListener: vi.fn() + } + + // Mock WebSocket constructor + global.WebSocket = vi.fn().mockImplementation(() => mockWebSocket) as any + + // Reset API state + api.serverFeatureFlags = {} + + // Mock getClientFeatureFlags to return test feature flags + vi.spyOn(api, 'getClientFeatureFlags').mockReturnValue({ + supports_preview_metadata: true, + api_version: '1.0.0', + capabilities: ['bulk_operations', 'async_nodes'] + }) + }) + + afterEach(() => { + vi.useRealTimers() + vi.restoreAllMocks() + }) + + describe('Feature flags negotiation', () => { + it('should send client feature flags as first message on connection', async () => { + // Initialize API connection + const initPromise = api.init() + + // Simulate connection open + wsEventHandlers['open'](new Event('open')) + + // Check that feature flags were sent as first message + expect(mockWebSocket.send).toHaveBeenCalledTimes(1) + const sentMessage = JSON.parse(mockWebSocket.send.mock.calls[0][0]) + expect(sentMessage).toEqual({ + type: 'feature_flags', + data: { + supports_preview_metadata: true, + api_version: '1.0.0', + capabilities: ['bulk_operations', 'async_nodes'] + } + }) + + // Simulate server response with status message + wsEventHandlers['message']({ + data: JSON.stringify({ + type: 'status', + data: { + status: { exec_info: { queue_remaining: 0 } }, + sid: 'test-sid' + } + }) + }) + + // Simulate server feature flags response + wsEventHandlers['message']({ + data: JSON.stringify({ + type: 'feature_flags', + data: { + supports_preview_metadata: true, + async_execution: true, + supported_formats: ['webp', 'jpeg', 'png'], + api_version: '1.0.0', + max_upload_size: 104857600, + capabilities: ['isolated_nodes', 'dynamic_models'] + } + }) + }) + + await initPromise + + // Check that server features were stored + expect(api.serverFeatureFlags).toEqual({ + supports_preview_metadata: true, + async_execution: true, + supported_formats: ['webp', 'jpeg', 'png'], + api_version: '1.0.0', + max_upload_size: 104857600, + capabilities: ['isolated_nodes', 'dynamic_models'] + }) + }) + + it('should handle server without feature flags support', async () => { + // Initialize API connection + const initPromise = api.init() + + // Simulate connection open + wsEventHandlers['open'](new Event('open')) + + // Clear the send mock to reset + mockWebSocket.send.mockClear() + + // Simulate server response with status but no feature flags + wsEventHandlers['message']({ + data: JSON.stringify({ + type: 'status', + data: { + status: { exec_info: { queue_remaining: 0 } }, + sid: 'test-sid' + } + }) + }) + + // Simulate some other message (not feature flags) + wsEventHandlers['message']({ + data: JSON.stringify({ + type: 'execution_start', + data: {} + }) + }) + + await initPromise + + // Server features should remain empty + expect(api.serverFeatureFlags).toEqual({}) + }) + }) + + describe('Feature checking methods', () => { + beforeEach(() => { + // Set up some test features + api.serverFeatureFlags = { + supports_preview_metadata: true, + async_execution: false, + capabilities: ['isolated_nodes', 'dynamic_models'] + } + }) + + it('should check if server supports a boolean feature', () => { + expect(api.serverSupportsFeature('supports_preview_metadata')).toBe(true) + expect(api.serverSupportsFeature('async_execution')).toBe(false) + expect(api.serverSupportsFeature('non_existent_feature')).toBe(false) + }) + + it('should get server feature value', () => { + expect(api.getServerFeature('supports_preview_metadata')).toBe(true) + expect(api.getServerFeature('capabilities')).toEqual([ + 'isolated_nodes', + 'dynamic_models' + ]) + expect(api.getServerFeature('non_existent_feature')).toBeUndefined() + }) + }) + + describe('Client feature flags configuration', () => { + it('should use mocked client feature flags', () => { + // Verify mocked flags are returned + const clientFlags = api.getClientFeatureFlags() + expect(clientFlags).toEqual({ + supports_preview_metadata: true, + api_version: '1.0.0', + capabilities: ['bulk_operations', 'async_nodes'] + }) + }) + + it('should return a copy of client feature flags', () => { + // Temporarily restore the real implementation for this test + vi.mocked(api.getClientFeatureFlags).mockRestore() + + // Verify that modifications to returned object don't affect original + const clientFlags1 = api.getClientFeatureFlags() + const clientFlags2 = api.getClientFeatureFlags() + + // Should be different objects + expect(clientFlags1).not.toBe(clientFlags2) + + // But with same content + expect(clientFlags1).toEqual(clientFlags2) + + // Modifying one should not affect the other + clientFlags1.test_flag = true + expect(api.getClientFeatureFlags()).not.toHaveProperty('test_flag') + }) + }) + + describe('Integration with preview messages', () => { + it('should affect preview message handling based on feature support', () => { + // Test with metadata support + api.serverFeatureFlags = { supports_preview_metadata: true } + expect(api.serverSupportsFeature('supports_preview_metadata')).toBe(true) + + // Test without metadata support + api.serverFeatureFlags = {} + expect(api.serverSupportsFeature('supports_preview_metadata')).toBe(false) + }) + }) +}) From 927773f5531b2af77767350ae5d50035150b3e70 Mon Sep 17 00:00:00 2001 From: Benjamin Lu Date: Wed, 16 Jul 2025 15:15:05 -0400 Subject: [PATCH 13/29] Fix Danger.js Security Issues (#4462) --- .github/workflows/danger.yaml | 27 -- .github/workflows/pr-checks.yml | 154 ++++++ .github/workflows/pr-comment.yml | 149 ++++++ dangerfile.ts | 72 --- package-lock.json | 806 +------------------------------ package.json | 1 - 6 files changed, 311 insertions(+), 898 deletions(-) delete mode 100644 .github/workflows/danger.yaml create mode 100644 .github/workflows/pr-checks.yml create mode 100644 .github/workflows/pr-comment.yml delete mode 100644 dangerfile.ts diff --git a/.github/workflows/danger.yaml b/.github/workflows/danger.yaml deleted file mode 100644 index 68520a8cb..000000000 --- a/.github/workflows/danger.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: Danger PR Review -on: - pull_request: - types: [opened, edited, synchronize] - -jobs: - danger: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 'lts/*' - - - name: Install dependencies - run: npm ci - - - name: Run Danger - run: npx danger ci - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml new file mode 100644 index 000000000..12ea6cfdb --- /dev/null +++ b/.github/workflows/pr-checks.yml @@ -0,0 +1,154 @@ +name: PR Checks +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +permissions: + contents: read + pull-requests: read + +jobs: + analyze: + runs-on: ubuntu-latest + outputs: + should_run: ${{ steps.check-changes.outputs.should_run }} + has_browser_tests: ${{ steps.check-coverage.outputs.has_browser_tests }} + has_screen_recording: ${{ steps.check-recording.outputs.has_recording }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Ensure base branch is available + run: | + # Fetch the specific base commit to ensure it's available for git diff + git fetch origin ${{ github.event.pull_request.base.sha }} + + - name: Check if significant changes exist + id: check-changes + run: | + # Get list of changed files + CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}) + + # Filter for src/ files + SRC_FILES=$(echo "$CHANGED_FILES" | grep '^src/' || true) + + if [ -z "$SRC_FILES" ]; then + echo "No src/ files changed" + echo "should_run=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Count lines changed in src files + TOTAL_LINES=0 + for file in $SRC_FILES; do + if [ -f "$file" ]; then + # Count added lines (non-empty) + ADDED=$(git diff ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} -- "$file" | grep '^+' | grep -v '^+++' | grep -v '^+$' | wc -l) + # Count removed lines (non-empty) + REMOVED=$(git diff ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} -- "$file" | grep '^-' | grep -v '^---' | grep -v '^-$' | wc -l) + TOTAL_LINES=$((TOTAL_LINES + ADDED + REMOVED)) + fi + done + + echo "Total lines changed in src/: $TOTAL_LINES" + + if [ $TOTAL_LINES -gt 3 ]; then + echo "should_run=true" >> "$GITHUB_OUTPUT" + else + echo "should_run=false" >> "$GITHUB_OUTPUT" + fi + + - name: Check browser test coverage + id: check-coverage + if: steps.check-changes.outputs.should_run == 'true' + run: | + # Check if browser tests were updated + BROWSER_TEST_CHANGES=$(git diff --name-only ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} | grep '^browser_tests/.*\.ts$' || true) + + if [ -n "$BROWSER_TEST_CHANGES" ]; then + echo "has_browser_tests=true" >> "$GITHUB_OUTPUT" + else + echo "has_browser_tests=false" >> "$GITHUB_OUTPUT" + fi + + - name: Check for screen recording + id: check-recording + if: steps.check-changes.outputs.should_run == 'true' + run: | + # Check PR body for screen recording + PR_BODY="${{ github.event.pull_request.body }}" + + # Check for GitHub user attachments or YouTube links + if echo "$PR_BODY" | grep -qiE 'github\.com/user-attachments/assets/[a-f0-9-]+|youtube\.com/watch|youtu\.be/'; then + echo "has_recording=true" >> "$GITHUB_OUTPUT" + else + echo "has_recording=false" >> "$GITHUB_OUTPUT" + fi + + - name: Final check and create results + id: final-check + if: always() + run: | + # Initialize results + WARNINGS_JSON="" + + # Only run checks if should_run is true + if [ "${{ steps.check-changes.outputs.should_run }}" == "true" ]; then + # Check browser test coverage + if [ "${{ steps.check-coverage.outputs.has_browser_tests }}" != "true" ]; then + if [ -n "$WARNINGS_JSON" ]; then + WARNINGS_JSON="${WARNINGS_JSON}," + fi + WARNINGS_JSON="${WARNINGS_JSON}{\"message\":\"⚠️ **Warning: E2E Test Coverage Missing**\\n\\nIf this PR modifies behavior that can be covered by browser-based E2E tests, those tests are required. PRs lacking applicable test coverage may not be reviewed until added. Please add or update browser tests to ensure code quality and prevent regressions.\"}" + fi + + # Check screen recording + if [ "${{ steps.check-recording.outputs.has_recording }}" != "true" ]; then + if [ -n "$WARNINGS_JSON" ]; then + WARNINGS_JSON="${WARNINGS_JSON}," + fi + WARNINGS_JSON="${WARNINGS_JSON}{\"message\":\"⚠️ **Warning: Visual Documentation Missing**\\n\\nIf this PR changes user-facing behavior, visual proof (screen recording or screenshot) is required. PRs without applicable visual documentation may not be reviewed until provided.\\nYou can add it by:\\n\\n- GitHub: Drag & drop media directly into the PR description\\n\\n- YouTube: Include a link to a short demo\"}" + fi + fi + + # Create results JSON + if [ -n "$WARNINGS_JSON" ]; then + # Create JSON with warnings + cat > pr-check-results.json << EOF + { + "fails": [], + "warnings": [$WARNINGS_JSON], + "messages": [], + "markdowns": [] + } + EOF + echo "failed=false" >> "$GITHUB_OUTPUT" + else + # Create JSON with success + cat > pr-check-results.json << 'EOF' + { + "fails": [], + "warnings": [], + "messages": [], + "markdowns": [] + } + EOF + echo "failed=false" >> "$GITHUB_OUTPUT" + fi + + # Write PR metadata + echo "${{ github.event.pull_request.number }}" > pr-number.txt + echo "${{ github.event.pull_request.head.sha }}" > pr-sha.txt + + - name: Upload results + uses: actions/upload-artifact@v4 + if: always() + with: + name: pr-check-results-${{ github.run_id }} + path: | + pr-check-results.json + pr-number.txt + pr-sha.txt + retention-days: 1 \ No newline at end of file diff --git a/.github/workflows/pr-comment.yml b/.github/workflows/pr-comment.yml new file mode 100644 index 000000000..5ae8cf83a --- /dev/null +++ b/.github/workflows/pr-comment.yml @@ -0,0 +1,149 @@ +name: PR Comment +on: + workflow_run: + workflows: ["PR Checks"] + types: [completed] + +permissions: + pull-requests: write + issues: write + statuses: write + +jobs: + comment: + if: github.event.workflow_run.event == 'pull_request' + runs-on: ubuntu-latest + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: pr-check-results-${{ github.event.workflow_run.id }} + path: /tmp/pr-artifacts + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + + - name: Post results + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const path = require('path'); + + // Helper function to safely read files + function safeReadFile(filePath) { + try { + if (!fs.existsSync(filePath)) return null; + return fs.readFileSync(filePath, 'utf8').trim(); + } catch (e) { + console.error(`Error reading ${filePath}:`, e); + return null; + } + } + + // Read artifact files + const artifactDir = '/tmp/pr-artifacts'; + const prNumber = safeReadFile(path.join(artifactDir, 'pr-number.txt')); + const prSha = safeReadFile(path.join(artifactDir, 'pr-sha.txt')); + const resultsJson = safeReadFile(path.join(artifactDir, 'pr-check-results.json')); + + // Validate PR number + if (!prNumber || isNaN(parseInt(prNumber))) { + throw new Error('Invalid or missing PR number'); + } + + // Parse and validate results + let results; + try { + results = JSON.parse(resultsJson || '{}'); + } catch (e) { + console.error('Failed to parse check results:', e); + + // Post error comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parseInt(prNumber), + body: `⚠️ PR checks failed to complete properly. Error parsing results: ${e.message}` + }); + return; + } + + // Format check messages + const messages = []; + + if (results.fails && results.fails.length > 0) { + messages.push('### ❌ Failures\n' + results.fails.map(f => f.message).join('\n\n')); + } + + if (results.warnings && results.warnings.length > 0) { + messages.push('### ⚠️ Warnings\n' + results.warnings.map(w => w.message).join('\n\n')); + } + + if (results.messages && results.messages.length > 0) { + messages.push('### 💬 Messages\n' + results.messages.map(m => m.message).join('\n\n')); + } + + if (results.markdowns && results.markdowns.length > 0) { + messages.push(...results.markdowns.map(m => m.message)); + } + + // Find existing bot comment + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parseInt(prNumber) + }); + + const botComment = comments.data.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('') + ); + + // Post comment if there are any messages + if (messages.length > 0) { + const body = messages.join('\n\n'); + const commentBody = `\n${body}`; + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: commentBody + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parseInt(prNumber), + body: commentBody + }); + } + } else { + // No messages - delete existing comment if present + if (botComment) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id + }); + } + } + + // Set commit status based on failures + if (prSha) { + const hasFailures = results.fails && results.fails.length > 0; + const hasWarnings = results.warnings && results.warnings.length > 0; + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: prSha, + state: hasFailures ? 'failure' : 'success', + context: 'pr-checks', + description: hasFailures + ? `${results.fails.length} check(s) failed` + : hasWarnings + ? `${results.warnings.length} warning(s)` + : 'All checks passed' + }); + } \ No newline at end of file diff --git a/dangerfile.ts b/dangerfile.ts deleted file mode 100644 index 6b4b69ab2..000000000 --- a/dangerfile.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { danger, fail } from 'danger' - -// Check if we should run the checks -const shouldRunChecks = async () => { - const allChangedFiles = [ - ...danger.git.modified_files, - ...danger.git.created_files - ] - const srcChanges = allChangedFiles.filter((file) => file.startsWith('src/')) - - if (srcChanges.length === 0) { - return false - } - - // Check total lines changed in src files - let totalLinesChanged = 0 - for (const file of srcChanges) { - const diff = await danger.git.diffForFile(file) - if (diff) { - // Count only lines with actual content (non-empty after trimming whitespace) - // This excludes empty lines and lines containing only spaces/tabs - const additions = - diff.added?.split('\n').filter((line) => line.trim()).length || 0 - const deletions = - diff.removed?.split('\n').filter((line) => line.trim()).length || 0 - totalLinesChanged += additions + deletions - } - } - - return totalLinesChanged > 3 -} - -// Check if browser tests were updated -const checkBrowserTestCoverage = () => { - const allChangedFiles = [ - ...danger.git.modified_files, - ...danger.git.created_files - ] - const hasBrowserTestChanges = allChangedFiles.some( - (file) => file.startsWith('browser_tests/') && file.endsWith('.ts') - ) - - if (!hasBrowserTestChanges) { - fail(`🧪 **E2E Test Coverage Missing** - -All changes should be covered under E2E testing. Please add or update browser tests.`) - } -} - -// Check for screen recording in PR description -const checkScreenRecording = () => { - const description = danger.github.pr.body || '' - const hasRecording = - /github\.com\/user-attachments\/assets\/[a-f0-9-]+/i.test(description) || - /youtube\.com\/watch|youtu\.be\//i.test(description) - - if (!hasRecording) { - fail(`📹 **Visual Documentation Missing** - -Please add a screen recording or screenshot: -- GitHub: Drag & drop media to PR description -- YouTube: Add YouTube link`) - } -} - -// Run the checks only if conditions are met -shouldRunChecks().then((shouldRun) => { - if (shouldRun) { - checkBrowserTestCoverage() - checkScreenRecording() - } -}) diff --git a/package-lock.json b/package-lock.json index 961556ddb..fa5cee570 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,6 @@ "@vue/test-utils": "^2.4.6", "autoprefixer": "^10.4.19", "chalk": "^5.3.0", - "danger": "^13.0.4", "eslint": "^9.12.0", "eslint-config-prettier": "^10.1.2", "eslint-plugin-prettier": "^5.2.6", @@ -2204,49 +2203,6 @@ "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==" }, - "node_modules/@gitbeaker/core": { - "version": "38.12.1", - "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-38.12.1.tgz", - "integrity": "sha512-8XMVcBIdVAAoxn7JtqmZ2Ee8f+AZLcCPmqEmPFOXY2jPS84y/DERISg/+sbhhb18iRy+ZsZhpWgQ/r3CkYNJOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@gitbeaker/requester-utils": "^38.12.1", - "qs": "^6.11.1", - "xcase": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@gitbeaker/requester-utils": { - "version": "38.12.1", - "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-38.12.1.tgz", - "integrity": "sha512-Rc/DgngS0YPN+AY1s9UnexKSy4Lh0bkQVAq9p7PRbRpXb33SlTeCg8eg/8+A/mrMcHgYmP0XhH8lkizyA5tBUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "qs": "^6.11.1", - "xcase": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@gitbeaker/rest": { - "version": "38.12.1", - "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-38.12.1.tgz", - "integrity": "sha512-9KMSDtJ/sIov+5pcH+CAfiJXSiuYgN0KLKQFg0HHWR2DwcjGYkcbmhoZcWsaOWOqq4kihN1l7wX91UoRxxKKTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@gitbeaker/core": "^38.12.1", - "@gitbeaker/requester-utils": "^38.12.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@grpc/grpc-js": { "version": "1.9.15", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", @@ -3047,173 +3003,6 @@ "node": ">= 8" } }, - "node_modules/@octokit/auth-token": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/core": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", - "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.1.0", - "@octokit/request": "^8.4.1", - "@octokit/request-error": "^5.1.1", - "@octokit/types": "^13.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/endpoint": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", - "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^13.1.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/graphql": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", - "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/request": "^8.4.1", - "@octokit/types": "^13.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "24.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", - "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "11.4.4-cjs.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.4-cjs.2.tgz", - "integrity": "sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^13.7.0" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": "5" - } - }, - "node_modules/@octokit/plugin-request-log": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", - "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": "5" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "13.3.2-cjs.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.2-cjs.1.tgz", - "integrity": "sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^13.8.0" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": "^5" - } - }, - "node_modules/@octokit/request": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", - "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/endpoint": "^9.0.6", - "@octokit/request-error": "^5.1.1", - "@octokit/types": "^13.1.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/request-error": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", - "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^13.1.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/rest": { - "version": "20.1.2", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.2.tgz", - "integrity": "sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/core": "^5.0.2", - "@octokit/plugin-paginate-rest": "11.4.4-cjs.2", - "@octokit/plugin-request-log": "^4.0.0", - "@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/types": { - "version": "13.10.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", - "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/openapi-types": "^24.2.0" - } - }, "node_modules/@one-ini/wasm": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", @@ -4516,6 +4305,8 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">= 10" } @@ -5633,6 +5424,8 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "debug": "4" }, @@ -5903,26 +5696,6 @@ "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, - "node_modules/async-retry": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz", - "integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "retry": "0.12.0" - } - }, - "node_modules/async-retry/node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6043,13 +5816,6 @@ ], "license": "MIT" }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -6240,13 +6006,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6672,16 +6431,6 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -6975,18 +6724,6 @@ "url": "https://github.com/sponsors/mesqueeb" } }, - "node_modules/core-js": { - "version": "3.44.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.44.0.tgz", - "integrity": "sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -7177,174 +6914,6 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, - "node_modules/danger": { - "version": "13.0.4", - "resolved": "https://registry.npmjs.org/danger/-/danger-13.0.4.tgz", - "integrity": "sha512-IAdQ5nSJyIs4zKj6AN35ixt2B0Ce3WZUm3IFe/CMnL/Op7wV7IGg4D348U0EKNaNPP58QgXbdSk9pM+IXP1QXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@gitbeaker/rest": "^38.0.0", - "@octokit/rest": "^20.1.2", - "async-retry": "1.2.3", - "chalk": "^2.3.0", - "commander": "^2.18.0", - "core-js": "^3.8.2", - "debug": "^4.1.1", - "fast-json-patch": "^3.0.0-1", - "get-stdin": "^6.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "hyperlinker": "^1.0.0", - "ini": "^5.0.0", - "json5": "^2.2.3", - "jsonpointer": "^5.0.0", - "jsonwebtoken": "^9.0.0", - "lodash.find": "^4.6.0", - "lodash.includes": "^4.3.0", - "lodash.isobject": "^3.0.2", - "lodash.keys": "^4.0.8", - "lodash.mapvalues": "^4.6.0", - "lodash.memoize": "^4.1.2", - "memfs-or-file-map-to-github-branch": "^1.3.0", - "micromatch": "^4.0.4", - "node-cleanup": "^2.1.2", - "node-fetch": "^2.6.7", - "override-require": "^1.1.1", - "p-limit": "^2.1.0", - "parse-diff": "^0.7.0", - "parse-github-url": "^1.0.2", - "parse-link-header": "^2.0.0", - "pinpoint": "^1.1.0", - "prettyjson": "^1.2.1", - "readline-sync": "^1.4.9", - "regenerator-runtime": "^0.13.9", - "require-from-string": "^2.0.2", - "supports-hyperlinks": "^1.0.1" - }, - "bin": { - "danger": "distribution/commands/danger.js", - "danger-ci": "distribution/commands/danger-ci.js", - "danger-init": "distribution/commands/danger-init.js", - "danger-js": "distribution/commands/danger.js", - "danger-local": "distribution/commands/danger-local.js", - "danger-pr": "distribution/commands/danger-pr.js", - "danger-process": "distribution/commands/danger-process.js", - "danger-reset-status": "distribution/commands/danger-reset-status.js", - "danger-runner": "distribution/commands/danger-runner.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/danger/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/danger/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/danger/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/danger/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/danger/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/danger/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/danger/node_modules/ini": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", - "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/danger/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/danger/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true, - "license": "MIT" - }, - "node_modules/danger/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -7557,13 +7126,6 @@ "node": ">= 0.8" } }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true, - "license": "ISC" - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -7832,16 +7394,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, "node_modules/editorconfig": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", @@ -8090,16 +7642,6 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/escodegen": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", @@ -8818,13 +8360,6 @@ "node": ">=8.6.0" } }, - "node_modules/fast-json-patch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", - "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -9339,16 +8874,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -9690,6 +9215,8 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -9704,6 +9231,8 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "agent-base": "6", "debug": "4" @@ -9747,16 +9276,6 @@ "url": "https://github.com/sponsors/typicode" } }, - "node_modules/hyperlinker": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", - "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -10835,42 +10354,6 @@ "node": ">=0.10.0" } }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -10890,29 +10373,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -11435,83 +10895,6 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, - "node_modules/lodash.find": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", - "integrity": "sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isobject": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", - "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", - "integrity": "sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.mapvalues": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", - "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -11519,13 +10902,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true, - "license": "MIT" - }, "node_modules/log-update": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", @@ -12041,16 +11417,6 @@ "node": ">= 0.8" } }, - "node_modules/memfs-or-file-map-to-github-branch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.3.0.tgz", - "integrity": "sha512-AzgIEodmt51dgwB3TmihTf1Fh2SmszdZskC6trFHy4v71R5shLmdjJSYI7ocVfFa7C/TE6ncb0OZ9eBg2rmkBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/rest": "*" - } - }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -12880,13 +12246,6 @@ "tslib": "^2.0.3" } }, - "node_modules/node-cleanup": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", - "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==", - "dev": true, - "license": "MIT" - }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -13215,13 +12574,6 @@ "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", "license": "MIT" }, - "node_modules/override-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/override-require/-/override-require-1.1.1.tgz", - "integrity": "sha512-eoJ9YWxFcXbrn2U8FKT6RV+/Kj7fiGAB1VvHzbYKt8xM5ZuKZgCGvnHzDxmreEjcBH28ejg5MiOH4iyY1mQnkg==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -13311,16 +12663,6 @@ "node": ">=8" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/package-json": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-10.0.1.tgz", @@ -13404,26 +12746,6 @@ "node": ">=6" } }, - "node_modules/parse-diff": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/parse-diff/-/parse-diff-0.7.1.tgz", - "integrity": "sha512-1j3l8IKcy4yRK2W4o9EYvJLSzpAVwz4DXqCewYyx2vEwk2gcf3DBPqc8Fj4XV3K33OYJ08A8fWwyu/ykD/HUSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/parse-github-url": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.3.tgz", - "integrity": "sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==", - "dev": true, - "license": "MIT", - "bin": { - "parse-github-url": "cli.js" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -13442,16 +12764,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-link-header": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-2.0.0.tgz", - "integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "xtend": "~4.0.1" - } - }, "node_modules/parse-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", @@ -13668,13 +12980,6 @@ } } }, - "node_modules/pinpoint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pinpoint/-/pinpoint-1.1.0.tgz", - "integrity": "sha512-+04FTD9x7Cls2rihLlo57QDCcHoLBGn5Dk51SwtFBWkUWLxZaBXyNVpCw1S+atvE7GmnFjeaRZ0WLq3UYuqAdg==", - "dev": true, - "license": "MIT" - }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -13953,20 +13258,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/prettyjson": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.5.tgz", - "integrity": "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "colors": "1.4.0", - "minimist": "^1.2.0" - }, - "bin": { - "prettyjson": "bin/prettyjson" - } - }, "node_modules/primeicons": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", @@ -14463,16 +13754,6 @@ "node": ">=8.10.0" } }, - "node_modules/readline-sync": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", - "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -15619,53 +14900,6 @@ "node": ">=8" } }, - "node_modules/supports-hyperlinks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", - "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^2.0.0", - "supports-color": "^5.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -16774,13 +16008,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/universal-user-agent": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", - "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", - "dev": true, - "license": "ISC" - }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", @@ -18292,13 +17519,6 @@ } } }, - "node_modules/xcase": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz", - "integrity": "sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw==", - "dev": true, - "license": "MIT" - }, "node_modules/xdg-basedir": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", @@ -18329,16 +17549,6 @@ "optional": true, "peer": true }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index edb2bc5de..2dfd9aa87 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "@vue/test-utils": "^2.4.6", "autoprefixer": "^10.4.19", "chalk": "^5.3.0", - "danger": "^13.0.4", "eslint": "^9.12.0", "eslint-config-prettier": "^10.1.2", "eslint-plugin-prettier": "^5.2.6", From 11eff4981febe5ed790ec21269b8ec9217c77b30 Mon Sep 17 00:00:00 2001 From: Rizumu Ayaka Date: Fri, 18 Jul 2025 08:13:01 +0800 Subject: [PATCH 14/29] Fix Help Center changelog toast overflows viewport (#4469) --- src/components/helpcenter/WhatsNewPopup.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/helpcenter/WhatsNewPopup.vue b/src/components/helpcenter/WhatsNewPopup.vue index b7d3000ae..26d1dc8a0 100644 --- a/src/components/helpcenter/WhatsNewPopup.vue +++ b/src/components/helpcenter/WhatsNewPopup.vue @@ -217,7 +217,8 @@ defineExpose({ display: flex; flex-direction: column; gap: 24px; - overflow: hidden; + max-height: 80vh; + overflow-y: auto; padding: 32px 32px 24px; border-radius: 12px; } From 282f9ce27a864d81a0d79538177125311fed48d1 Mon Sep 17 00:00:00 2001 From: Comfy Org PR Bot Date: Fri, 18 Jul 2025 14:53:21 +0800 Subject: [PATCH 15/29] [chore] Update Comfy Registry API types from comfy-api@9ccb96a (#4470) Co-authored-by: viva-jinyi <53567196+viva-jinyi@users.noreply.github.com> --- src/types/comfyRegistryTypes.ts | 468 ++++++++++++++++++++++++++++++-- 1 file changed, 446 insertions(+), 22 deletions(-) diff --git a/src/types/comfyRegistryTypes.ts b/src/types/comfyRegistryTypes.ts index 17f22a7e1..be7211ed8 100644 --- a/src/types/comfyRegistryTypes.ts +++ b/src/types/comfyRegistryTypes.ts @@ -699,6 +699,27 @@ export interface paths { patch?: never trace?: never } + '/publishers/{publisherId}/nodes/{nodeId}/claim-my-node': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** + * Claim nodeId into publisherId for the authenticated publisher + * @description This endpoint allows a publisher to claim an unclaimed node that they own the repo, which is identified by the nodeId. The unclaimed node's repository must be owned by the authenticated user. + * + */ + post: operations['claimMyNode'] + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/publishers/{publisherId}/nodes/v2': { parameters: { query?: never @@ -1061,6 +1082,23 @@ export interface paths { patch?: never trace?: never } + '/bulk/nodes/versions': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Retrieve multiple node versions in a single request */ + post: operations['getBulkNodeVersions'] + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/versions': { parameters: { query?: never @@ -1095,6 +1133,26 @@ export interface paths { patch?: never trace?: never } + '/admin/nodes/{nodeId}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + /** + * Admin Update Node + * @description Only admins can update a node with admin privileges. + */ + put: operations['adminUpdateNode'] + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/admin/nodes/{nodeId}/versions/{versionNumber}': { parameters: { query?: never @@ -2951,7 +3009,7 @@ export interface paths { patch?: never trace?: never } - '/proxy/moonvalley/text-to-video': { + '/proxy/moonvalley/prompts/text-to-video': { parameters: { query?: never header?: never @@ -2968,7 +3026,7 @@ export interface paths { patch?: never trace?: never } - '/proxy/moonvalley/text-to-image': { + '/proxy/moonvalley/prompts/text-to-image': { parameters: { query?: never header?: never @@ -3057,6 +3115,37 @@ export interface paths { export type webhooks = Record export interface components { schemas: { + ClaimMyNodeRequest: { + /** @description GitHub token to verify if the user owns the repo of the node */ + GH_TOKEN: string + } + BulkNodeVersionsRequest: { + /** @description List of node ID and version pairs to retrieve */ + node_versions: components['schemas']['NodeVersionIdentifier'][] + } + NodeVersionIdentifier: { + /** @description The unique identifier of the node */ + node_id: string + /** @description The version of the node */ + version: string + } + BulkNodeVersionsResponse: { + /** @description List of retrieved node versions with their status */ + node_versions: components['schemas']['BulkNodeVersionResult'][] + } + BulkNodeVersionResult: { + /** @description The node and version identifier */ + identifier: components['schemas']['NodeVersionIdentifier'] + /** + * @description Status of the retrieval operation + * @enum {string} + */ + status: 'success' | 'not_found' | 'error' + /** @description The retrieved node version data (only present if status is success) */ + node_version?: components['schemas']['NodeVersion'] + /** @description Error message if retrieval failed (only present if status is error) */ + error_message?: string + } PersonalAccessToken: { /** * Format: uuid @@ -8713,71 +8802,212 @@ export interface components { | 'computer-use-preview' | 'computer-use-preview-2025-03-11' | 'chatgpt-4o-latest' - MoonvalleyInferenceParams: { - /** @default 1080 */ + MoonvalleyTextToVideoInferenceParams: { + /** + * @description Height of the generated video in pixels + * @default 1080 + */ height: number - /** @default 1920 */ + /** + * @description Width of the generated video in pixels + * @default 1920 + */ width: number - /** @default 64 */ + /** + * @description Number of frames to generate + * @default 64 + */ num_frames: number - /** @default 24 */ + /** + * @description Frames per second of the generated video + * @default 24 + */ fps: number /** * Format: float - * @default 12.5 + * @description Guidance scale for generation control + * @default 10 */ guidance_scale: number + /** @description Random seed for generation (default: random) */ seed?: number - /** @default 80 */ + /** + * @description Number of denoising steps + * @default 80 + */ steps: number - /** @default true */ + /** + * @description Whether to use timestep transformation + * @default true + */ use_timestep_transform: boolean /** * Format: float + * @description Shift value for generation control * @default 3 */ shift_value: number - /** @default true */ + /** + * @description Whether to use guidance scheduling + * @default true + */ use_guidance_schedule: boolean - /** @default true */ + /** + * @description Whether to add quality guidance + * @default true + */ add_quality_guidance: boolean /** * Format: float + * @description CLIP value for generation control * @default 3 */ clip_value: number - /** @default false */ + /** + * @description Whether to use negative prompts + * @default false + */ use_negative_prompts: boolean + /** @description Negative prompt text */ negative_prompt?: string - warmup_steps?: number - cooldown_steps?: number + /** + * @description Number of warmup steps (calculated based on num_frames) + * @default 0 + */ + warmup_steps: number + /** + * @description Number of cooldown steps (calculated based on num_frames) + * @default 75 + */ + cooldown_steps: number /** * Format: float + * @description Caching coefficient for optimization * @default 0.3 */ caching_coefficient: number - /** @default 3 */ + /** + * @description Number of caching warmup steps + * @default 3 + */ caching_warmup: number - /** @default 3 */ + /** + * @description Number of caching cooldown steps + * @default 3 + */ caching_cooldown: number - /** @default 0 */ + /** + * @description Index of the conditioning frame + * @default 0 + */ + conditioning_frame_index: number + } + MoonvalleyVideoToVideoInferenceParams: { + /** + * Format: float + * @description Guidance scale for generation control + * @default 15 + */ + guidance_scale: number + /** @description Random seed for generation (default: random) */ + seed?: number + /** + * @description Number of denoising steps + * @default 80 + */ + steps: number + /** + * @description Whether to use timestep transformation + * @default true + */ + use_timestep_transform: boolean + /** + * Format: float + * @description Shift value for generation control + * @default 3 + */ + shift_value: number + /** + * @description Whether to use guidance scheduling + * @default true + */ + use_guidance_schedule: boolean + /** + * @description Whether to add quality guidance + * @default true + */ + add_quality_guidance: boolean + /** + * Format: float + * @description CLIP value for generation control + * @default 3 + */ + clip_value: number + /** + * @description Whether to use negative prompts + * @default false + */ + use_negative_prompts: boolean + /** @description Negative prompt text */ + negative_prompt?: string + /** + * @description Number of warmup steps (calculated based on num_frames) + * @default 24 + */ + warmup_steps: number + /** + * @description Number of cooldown steps (calculated based on num_frames) + * @default 36 + */ + cooldown_steps: number + /** + * Format: float + * @description Caching coefficient for optimization + * @default 0.3 + */ + caching_coefficient: number + /** + * @description Number of caching warmup steps + * @default 3 + */ + caching_warmup: number + /** + * @description Number of caching cooldown steps + * @default 3 + */ + caching_cooldown: number + /** + * @description Index of the conditioning frame + * @default 0 + */ conditioning_frame_index: number } MoonvalleyTextToImageRequest: { prompt_text?: string image_url?: string - inference_params?: components['schemas']['MoonvalleyInferenceParams'] + inference_params?: components['schemas']['MoonvalleyTextToVideoInferenceParams'] webhook_url?: string } MoonvalleyTextToVideoRequest: { prompt_text?: string image_url?: string - inference_params?: components['schemas']['MoonvalleyInferenceParams'] + inference_params?: components['schemas']['MoonvalleyTextToVideoInferenceParams'] webhook_url?: string } - MoonvalleyVideoToVideoRequest: components['schemas']['MoonvalleyTextToVideoRequest'] & { + MoonvalleyVideoToVideoRequest: { + /** @description Describes the video to generate */ + prompt_text: string + /** @description Url to control video */ video_url: string - control_type: string + /** + * @description Supported types for video control + * @enum {string} + */ + control_type: 'motion_control' | 'pose_control' + /** @description Parameters for video-to-video generation inference */ + inference_params?: components['schemas']['MoonvalleyVideoToVideoInferenceParams'] + /** @description Optional webhook URL for notifications */ + webhook_url?: string } MoonvalleyPromptResponse: { id?: string @@ -10421,6 +10651,89 @@ export interface operations { } } } + claimMyNode: { + parameters: { + query?: never + header?: never + path: { + publisherId: string + nodeId: string + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['ClaimMyNodeRequest'] + } + } + responses: { + /** @description Node claimed successfully */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Bad request, invalid input data */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden - various authorization and permission issues + * Includes: + * - The authenticated user does not have permission to claim the node + * - The node is already claimed by another publisher + * - The GH_TOKEN is invalid + * - The repository is not owned by the authenticated GitHub user + * */ + 403: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + /** @description Too many requests - GitHub API rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + /** @description Service unavailable - GitHub API is currently unavailable */ + 503: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + } + } listNodesForPublisherV2: { parameters: { query?: { @@ -11709,6 +12022,48 @@ export interface operations { } } } + getBulkNodeVersions: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['BulkNodeVersionsRequest'] + } + } + responses: { + /** @description Successfully retrieved node versions */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['BulkNodeVersionsResponse'] + } + } + /** @description Bad request, invalid input */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + } + } listAllNodeVersions: { parameters: { query?: { @@ -11834,6 +12189,75 @@ export interface operations { } } } + adminUpdateNode: { + parameters: { + query?: never + header?: never + path: { + nodeId: string + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['Node'] + } + } + responses: { + /** @description Node updated successfully */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Node'] + } + } + /** @description Bad request, invalid input data. */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + /** @description Unauthorized */ + 401: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Forbidden */ + 403: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + /** @description Node not found */ + 404: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ErrorResponse'] + } + } + } + } adminUpdateNodeVersion: { parameters: { query?: never From c28d4514fcb7d76d65cf5a0bd5f84fbd5e57ee73 Mon Sep 17 00:00:00 2001 From: bymyself Date: Wed, 9 Apr 2025 09:52:06 -0700 Subject: [PATCH 16/29] migrate manager menu items --- src/composables/useCoreCommands.ts | 85 ++++++++++++++++++++++++++++-- src/constants/coreMenuCommands.ts | 10 ++++ src/constants/coreSettings.ts | 7 +++ src/locales/en/main.json | 4 +- src/scripts/api.ts | 51 ++++++++++++++++++ 5 files changed, 153 insertions(+), 4 deletions(-) diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index 5e4cf6125..e3627658b 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -19,6 +19,7 @@ import { useDialogService } from '@/services/dialogService' import { useLitegraphService } from '@/services/litegraphService' import { useWorkflowService } from '@/services/workflowService' import type { ComfyCommand } from '@/stores/commandStore' +import { useCommandStore } from '@/stores/commandStore' import { useExecutionStore } from '@/stores/executionStore' import { useCanvasStore, useTitleEditorStore } from '@/stores/graphStore' import { useQueueSettingsStore, useQueueStore } from '@/stores/queueStore' @@ -660,9 +661,9 @@ export function useCoreCommands(): ComfyCommand[] { } }, { - id: 'Comfy.Manager.CustomNodesManager', - icon: 'pi pi-puzzle', - label: 'Toggle the Custom Nodes Manager', + id: 'Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu', + icon: 'pi pi-objects-column', + label: 'Custom Nodes (Beta)', versionAdded: '1.12.10', function: () => { dialogService.toggleManagerDialog() @@ -757,6 +758,84 @@ export function useCoreCommands(): ComfyCommand[] { const { node } = res canvas.select(node) } + }, + { + id: 'Comfy.Manager.CustomNodesManager.ShowLegacyCustomNodesMenu', + icon: 'pi pi-bars', + label: 'Custom Nodes (Legacy)', + versionAdded: '1.16.4', + function: () => { + try { + useCommandStore().execute( + 'Comfy.Manager.CustomNodesManager.ToggleVisibility' + ) + } catch (error) { + useToastStore().add({ + severity: 'error', + summary: t('g.error'), + detail: t('manager.legacyMenuNotAvailable'), + life: 3000 + }) + } + } + }, + { + id: 'Comfy.Manager.ShowLegacyManagerMenu', + icon: 'mdi mdi-puzzle', + label: 'Manager Menu (Legacy)', + versionAdded: '1.16.4', + function: () => { + try { + useCommandStore().execute('Comfy.Manager.Menu.ToggleVisibility') + } catch (error) { + useToastStore().add({ + severity: 'error', + summary: t('g.error'), + detail: t('manager.legacyMenuNotAvailable'), + life: 3000 + }) + } + } + }, + { + id: 'Comfy.Memory.UnloadModels', + icon: 'mdi mdi-vacuum-outline', + label: 'Unload Models', + versionAdded: '1.16.4', + function: async () => { + if (!useSettingStore().get('Comfy.Memory.AllowManualUnload')) { + useToastStore().add({ + severity: 'error', + summary: t('g.error'), + detail: t('g.commandProhibited', { + command: 'Comfy.Memory.UnloadModels' + }), + life: 3000 + }) + return + } + await api.freeMemory({ freeExecutionCache: false }) + } + }, + { + id: 'Comfy.Memory.UnloadModelsAndExecutionCache', + icon: 'mdi mdi-vacuum-outline', + label: 'Unload Models and Execution Cache', + versionAdded: '1.16.4', + function: async () => { + if (!useSettingStore().get('Comfy.Memory.AllowManualUnload')) { + useToastStore().add({ + severity: 'error', + summary: t('g.error'), + detail: t('g.commandProhibited', { + command: 'Comfy.Memory.UnloadModelsAndExecutionCache' + }), + life: 3000 + }) + return + } + await api.freeMemory({ freeExecutionCache: true }) + } } ] diff --git a/src/constants/coreMenuCommands.ts b/src/constants/coreMenuCommands.ts index 9173366a4..9fc0561eb 100644 --- a/src/constants/coreMenuCommands.ts +++ b/src/constants/coreMenuCommands.ts @@ -14,6 +14,16 @@ export const CORE_MENU_COMMANDS = [ [['Edit'], ['Comfy.RefreshNodeDefinitions']], [['Edit'], ['Comfy.ClearWorkflow']], [['Edit'], ['Comfy.OpenClipspace']], + [ + ['Manager'], + [ + 'Comfy.Manager.ShowLegacyManagerMenu', + 'Comfy.Manager.CustomNodesManager.ShowLegacyCustomNodesMenu', + 'Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu', + 'Comfy.Memory.UnloadModels', + 'Comfy.Memory.UnloadModelsAndExecutionCache' + ] + ], [ ['Help'], [ diff --git a/src/constants/coreSettings.ts b/src/constants/coreSettings.ts index c3a9b30af..72943046c 100644 --- a/src/constants/coreSettings.ts +++ b/src/constants/coreSettings.ts @@ -14,6 +14,13 @@ import type { SettingParams } from '@/types/settingTypes' * when they are no longer needed. */ export const CORE_SETTINGS: SettingParams[] = [ + { + id: 'Comfy.Memory.AllowManualUnload', + name: 'Allow manual unload of models and execution cache via user command', + type: 'hidden', + defaultValue: true, + versionAdded: '1.18.0' + }, { id: 'Comfy.Validation.Workflows', name: 'Validate workflows', diff --git a/src/locales/en/main.json b/src/locales/en/main.json index df92e481b..589cbc533 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -133,10 +133,12 @@ "copyURL": "Copy URL", "releaseTitle": "{package} {version} Release", "progressCountOf": "of", - "keybindingAlreadyExists": "Keybinding already exists on" + "keybindingAlreadyExists": "Keybinding already exists on", + "commandProhibited": "Command {command} is prohibited. Contact an administrator for more information." }, "manager": { "title": "Custom Nodes Manager", + "legacyMenuNotAvailable": "Legacy manager menu is not available in this version of ComfyUI. Please use the new manager menu instead.", "failed": "Failed ({count})", "noNodesFound": "No nodes found", "noNodesFoundDescription": "The pack's nodes either could not be parsed, or the pack is a frontend extension only and doesn't have any nodes.", diff --git a/src/scripts/api.ts b/src/scripts/api.ts index 7be323588..66ad0b0e9 100644 --- a/src/scripts/api.ts +++ b/src/scripts/api.ts @@ -37,6 +37,7 @@ import { type ComfyNodeDef, validateComfyNodeDef } from '@/schemas/nodeDefSchema' +import { useToastStore } from '@/stores/toastStore' import { WorkflowTemplates } from '@/types/workflowTemplateTypes' interface QueuePromptRequestBody { @@ -987,6 +988,56 @@ export class ComfyApi extends EventTarget { return (await axios.get(this.internalURL('/folder_paths'))).data } + /* Frees memory by unloading models and optionally freeing execution cache + * @param {Object} options - The options object + * @param {boolean} options.freeExecutionCache - If true, also frees execution cache + */ + async freeMemory(options: { freeExecutionCache: boolean }) { + try { + let mode = '' + if (options.freeExecutionCache) { + mode = '{"unload_models": true, "free_memory": true}' + } else { + mode = '{"unload_models": true}' + } + + const res = await this.fetchApi(`/free`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: mode + }) + + if (res.status === 200) { + if (options.freeExecutionCache) { + useToastStore().add({ + severity: 'success', + summary: 'Models and Execution Cache have been cleared.', + life: 3000 + }) + } else { + useToastStore().add({ + severity: 'success', + summary: 'Models have been unloaded.', + life: 3000 + }) + } + } else { + useToastStore().add({ + severity: 'error', + summary: + 'Unloading of models failed. Installed ComfyUI may be an outdated version.', + life: 5000 + }) + } + } catch (error) { + useToastStore().add({ + severity: 'error', + summary: 'An error occurred while trying to unload models.', + life: 5000 + }) + } + } + /** * Gets the custom nodes i18n data from the server. * From 588bd99c3a3f38bf896587e6567940b15b5947fe Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 9 Apr 2025 17:46:39 +0000 Subject: [PATCH 17/29] Update locales [skip ci] --- src/locales/en/commands.json | 16 ++++++++++++++-- src/locales/en/main.json | 7 +++++++ src/locales/es/commands.json | 16 ++++++++++++++-- src/locales/es/main.json | 8 ++++++++ src/locales/fr/commands.json | 16 ++++++++++++++-- src/locales/fr/main.json | 8 ++++++++ src/locales/ja/commands.json | 16 ++++++++++++++-- src/locales/ja/main.json | 8 ++++++++ src/locales/ko/commands.json | 16 ++++++++++++++-- src/locales/ko/main.json | 8 ++++++++ src/locales/ru/commands.json | 16 ++++++++++++++-- src/locales/ru/main.json | 8 ++++++++ src/locales/zh/commands.json | 16 ++++++++++++++-- src/locales/zh/main.json | 8 ++++++++ 14 files changed, 153 insertions(+), 14 deletions(-) diff --git a/src/locales/en/commands.json b/src/locales/en/commands.json index 780ec7164..65c49a504 100644 --- a/src/locales/en/commands.json +++ b/src/locales/en/commands.json @@ -152,8 +152,14 @@ "Comfy_LoadDefaultWorkflow": { "label": "Load Default Workflow" }, - "Comfy_Manager_CustomNodesManager": { - "label": "Toggle the Custom Nodes Manager" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "Custom Nodes (Beta)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "Custom Nodes (Legacy)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "Manager Menu (Legacy)" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Toggle the Custom Nodes Manager Progress Bar" @@ -161,6 +167,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "Open Mask Editor for Selected Node" }, + "Comfy_Memory_UnloadModels": { + "label": "Unload Models" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "Unload Models and Execution Cache" + }, "Comfy_NewBlankWorkflow": { "label": "New Blank Workflow" }, diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 589cbc533..90be6ca73 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -915,6 +915,7 @@ "menuLabels": { "Workflow": "Workflow", "Edit": "Edit", + "Manager": "Manager", "Help": "Help", "Check for Updates": "Check for Updates", "Open Custom Nodes Folder": "Open Custom Nodes Folder", @@ -970,6 +971,12 @@ "Toggle the Custom Nodes Manager": "Toggle the Custom Nodes Manager", "Toggle the Custom Nodes Manager Progress Bar": "Toggle the Custom Nodes Manager Progress Bar", "Open Mask Editor for Selected Node": "Open Mask Editor for Selected Node", + "Custom Nodes (Beta)": "Custom Nodes (Beta)", + "Custom Nodes (Legacy)": "Custom Nodes (Legacy)", + "Manager Menu (Legacy)": "Manager Menu (Legacy)", + "Toggle Progress Dialog": "Toggle Progress Dialog", + "Unload Models": "Unload Models", + "Unload Models and Execution Cache": "Unload Models and Execution Cache", "New": "New", "Clipspace": "Clipspace", "Open": "Open", diff --git a/src/locales/es/commands.json b/src/locales/es/commands.json index 0160c9814..e20d7bd55 100644 --- a/src/locales/es/commands.json +++ b/src/locales/es/commands.json @@ -152,8 +152,14 @@ "Comfy_LoadDefaultWorkflow": { "label": "Cargar flujo de trabajo predeterminado" }, - "Comfy_Manager_CustomNodesManager": { - "label": "Administrador de nodos personalizados" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "Nodos personalizados (Beta)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "Nodos personalizados (Legacy)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "Menú del administrador (Legacy)" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Alternar diálogo de progreso del administrador" @@ -161,6 +167,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "Abrir editor de máscara para el nodo seleccionado" }, + "Comfy_Memory_UnloadModels": { + "label": "Descargar modelos" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "Descargar modelos y caché de ejecución" + }, "Comfy_NewBlankWorkflow": { "label": "Nuevo flujo de trabajo en blanco" }, diff --git a/src/locales/es/main.json b/src/locales/es/main.json index e59f889d0..17a39a237 100644 --- a/src/locales/es/main.json +++ b/src/locales/es/main.json @@ -270,6 +270,7 @@ "color": "Color", "comingSoon": "Próximamente", "command": "Comando", + "commandProhibited": "El comando {command} está prohibido. Contacta a un administrador para más información.", "community": "Comunidad", "completed": "Completado", "confirm": "Confirmar", @@ -628,6 +629,7 @@ "installationQueue": "Cola de Instalación", "lastUpdated": "Última Actualización", "latestVersion": "Última", + "legacyMenuNotAvailable": "El menú del administrador antiguo no está disponible en esta versión de ComfyUI. Por favor, utiliza el nuevo menú del administrador en su lugar.", "license": "Licencia", "loadingVersions": "Cargando versiones...", "nightlyVersion": "Nocturna", @@ -734,6 +736,8 @@ "Contact Support": "Contactar soporte", "Convert Selection to Subgraph": "Convertir selección en subgrafo", "Convert selected nodes to group node": "Convertir nodos seleccionados en nodo de grupo", + "Custom Nodes (Beta)": "Nodos personalizados (Beta)", + "Custom Nodes (Legacy)": "Nodos personalizados (Antiguo)", "Delete Selected Items": "Eliminar elementos seleccionados", "Desktop User Guide": "Guía de usuario de escritorio", "Duplicate Current Workflow": "Duplicar flujo de trabajo actual", @@ -752,6 +756,8 @@ "Move Selected Nodes Left": "Mover nodos seleccionados hacia la izquierda", "Move Selected Nodes Right": "Mover nodos seleccionados hacia la derecha", "Move Selected Nodes Up": "Mover nodos seleccionados hacia arriba", + "Manager": "Administrador", + "Manager Menu (Legacy)": "Menú del administrador (Antiguo)", "Mute/Unmute Selected Nodes": "Silenciar/Activar sonido de nodos seleccionados", "New": "Nuevo", "Next Opened Workflow": "Siguiente flujo de trabajo abierto", @@ -796,6 +802,8 @@ "Toggle the Custom Nodes Manager Progress Bar": "Alternar la Barra de Progreso del Administrador de Nodos Personalizados", "Undo": "Deshacer", "Ungroup selected group nodes": "Desagrupar nodos de grupo seleccionados", + "Unload Models": "Descargar modelos", + "Unload Models and Execution Cache": "Descargar modelos y caché de ejecución", "Workflow": "Flujo de trabajo", "Zoom In": "Acercar", "Zoom Out": "Alejar" diff --git a/src/locales/fr/commands.json b/src/locales/fr/commands.json index e1595c471..c77231f2b 100644 --- a/src/locales/fr/commands.json +++ b/src/locales/fr/commands.json @@ -152,8 +152,14 @@ "Comfy_LoadDefaultWorkflow": { "label": "Charger le flux de travail par défaut" }, - "Comfy_Manager_CustomNodesManager": { - "label": "Gestionnaire de Nœuds Personnalisés" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "Nœuds personnalisés (Beta)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "Nœuds personnalisés (Legacy)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "Menu du gestionnaire (Legacy)" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Basculer la boîte de dialogue de progression" @@ -161,6 +167,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "Ouvrir l'éditeur de masque pour le nœud sélectionné" }, + "Comfy_Memory_UnloadModels": { + "label": "Décharger les modèles" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "Décharger les modèles et le cache d'exécution" + }, "Comfy_NewBlankWorkflow": { "label": "Nouveau flux de travail vierge" }, diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index 4529f7c8e..6849ad805 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -270,6 +270,7 @@ "color": "Couleur", "comingSoon": "Bientôt disponible", "command": "Commande", + "commandProhibited": "La commande {command} est interdite. Contactez un administrateur pour plus d'informations.", "community": "Communauté", "completed": "Terminé", "confirm": "Confirmer", @@ -628,6 +629,7 @@ "installationQueue": "File d'attente d'installation", "lastUpdated": "Dernière mise à jour", "latestVersion": "Dernière", + "legacyMenuNotAvailable": "Le menu du gestionnaire de l'ancienne version n'est pas disponible dans cette version de ComfyUI. Veuillez utiliser le nouveau menu du gestionnaire à la place.", "license": "Licence", "loadingVersions": "Chargement des versions...", "nightlyVersion": "Nocturne", @@ -734,6 +736,8 @@ "Contact Support": "Contacter le support", "Convert Selection to Subgraph": "Convertir la sélection en sous-graphe", "Convert selected nodes to group node": "Convertir les nœuds sélectionnés en nœud de groupe", + "Custom Nodes (Beta)": "Nœuds personnalisés (Beta)", + "Custom Nodes (Legacy)": "Nœuds personnalisés (Ancienne version)", "Delete Selected Items": "Supprimer les éléments sélectionnés", "Desktop User Guide": "Guide de l'utilisateur de bureau", "Duplicate Current Workflow": "Dupliquer le flux de travail actuel", @@ -752,6 +756,8 @@ "Move Selected Nodes Left": "Déplacer les nœuds sélectionnés vers la gauche", "Move Selected Nodes Right": "Déplacer les nœuds sélectionnés vers la droite", "Move Selected Nodes Up": "Déplacer les nœuds sélectionnés vers le haut", + "Manager": "Gestionnaire", + "Manager Menu (Legacy)": "Menu du gestionnaire (Ancienne version)", "Mute/Unmute Selected Nodes": "Mettre en sourdine/Activer le son des nœuds sélectionnés", "New": "Nouveau", "Next Opened Workflow": "Prochain flux de travail ouvert", @@ -796,6 +802,8 @@ "Toggle the Custom Nodes Manager Progress Bar": "Basculer la barre de progression du gestionnaire de nœuds personnalisés", "Undo": "Annuler", "Ungroup selected group nodes": "Dégrouper les nœuds de groupe sélectionnés", + "Unload Models": "Décharger les modèles", + "Unload Models and Execution Cache": "Décharger les modèles et le cache d'exécution", "Workflow": "Flux de travail", "Zoom In": "Zoom avant", "Zoom Out": "Zoom arrière" diff --git a/src/locales/ja/commands.json b/src/locales/ja/commands.json index 3ee56580e..ce0d9848d 100644 --- a/src/locales/ja/commands.json +++ b/src/locales/ja/commands.json @@ -152,8 +152,14 @@ "Comfy_LoadDefaultWorkflow": { "label": "デフォルトのワークフローを読み込む" }, - "Comfy_Manager_CustomNodesManager": { - "label": "カスタムノードマネージャ" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "カスタムノード(ベータ版)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "カスタムノード(レガシー)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "マネージャーメニュー(レガシー)" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "プログレスダイアログの切り替え" @@ -161,6 +167,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "選択したノードのマスクエディタを開く" }, + "Comfy_Memory_UnloadModels": { + "label": "モデルのアンロード" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "モデルと実行キャッシュのアンロード" + }, "Comfy_NewBlankWorkflow": { "label": "新しい空のワークフロー" }, diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index 3a2b3ff53..989664f5c 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -270,6 +270,7 @@ "color": "色", "comingSoon": "近日公開", "command": "コマンド", + "commandProhibited": "コマンド {command} は禁止されています。詳細は管理者にお問い合わせください。", "community": "コミュニティ", "completed": "完了", "confirm": "確認", @@ -628,6 +629,7 @@ "installationQueue": "インストールキュー", "lastUpdated": "最終更新日", "latestVersion": "最新", + "legacyMenuNotAvailable": "このバージョンのComfyUIでは、レガシーマネージャーメニューは利用できません。新しいマネージャーメニューを使用してください。", "license": "ライセンス", "loadingVersions": "バージョンを読み込んでいます...", "nightlyVersion": "ナイトリー", @@ -734,6 +736,8 @@ "Contact Support": "サポートに連絡", "Convert Selection to Subgraph": "選択範囲をサブグラフに変換", "Convert selected nodes to group node": "選択したノードをグループノードに変換", + "Custom Nodes (Beta)": "カスタムノード(ベータ)", + "Custom Nodes (Legacy)": "カスタムノード(レガシー)", "Delete Selected Items": "選択したアイテムを削除", "Desktop User Guide": "デスクトップユーザーガイド", "Duplicate Current Workflow": "現在のワークフローを複製", @@ -752,6 +756,8 @@ "Move Selected Nodes Left": "選択したノードを左へ移動", "Move Selected Nodes Right": "選択したノードを右へ移動", "Move Selected Nodes Up": "選択したノードを上へ移動", + "Manager": "マネージャー", + "Manager Menu (Legacy)": "マネージャーメニュー(レガシー)", "Mute/Unmute Selected Nodes": "選択したノードのミュート/ミュート解除", "New": "新規", "Next Opened Workflow": "次に開いたワークフロー", @@ -796,6 +802,8 @@ "Toggle the Custom Nodes Manager Progress Bar": "カスタムノードマネージャーの進行状況バーを切り替え", "Undo": "元に戻す", "Ungroup selected group nodes": "選択したグループノードのグループ解除", + "Unload Models": "モデルのアンロード", + "Unload Models and Execution Cache": "モデルと実行キャッシュのアンロード", "Workflow": "ワークフロー", "Zoom In": "ズームイン", "Zoom Out": "ズームアウト" diff --git a/src/locales/ko/commands.json b/src/locales/ko/commands.json index becb3f560..bb5dc7d51 100644 --- a/src/locales/ko/commands.json +++ b/src/locales/ko/commands.json @@ -152,8 +152,14 @@ "Comfy_LoadDefaultWorkflow": { "label": "기본 워크플로 로드" }, - "Comfy_Manager_CustomNodesManager": { - "label": "사용자 정의 노드 관리자" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "사용자 정의 노드 (베타)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "사용자 정의 노드 (레거시)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "매니저 메뉴 (레거시)" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "진행 상황 대화 상자 전환" @@ -161,6 +167,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "선택한 노드 마스크 편집기 열기" }, + "Comfy_Memory_UnloadModels": { + "label": "모델 언로드" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "모델 및 실행 캐시 언로드" + }, "Comfy_NewBlankWorkflow": { "label": "새로운 빈 워크플로" }, diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index b09399130..cd1d1beab 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -270,6 +270,7 @@ "color": "색상", "comingSoon": "곧 출시 예정", "command": "명령", + "commandProhibited": "명령 {command}은 금지되었습니다. 자세한 정보는 관리자에게 문의하십시오.", "community": "커뮤니티", "completed": "완료됨", "confirm": "확인", @@ -628,6 +629,7 @@ "installationQueue": "설치 대기열", "lastUpdated": "마지막 업데이트", "latestVersion": "최신", + "legacyMenuNotAvailable": "이 버전의 ComfyUI에서는 레거시 매니저 메뉴를 사용할 수 없습니다. 대신 새로운 매니저 메뉴를 사용하십시오.", "license": "라이선스", "loadingVersions": "버전 로딩 중...", "nightlyVersion": "최신 테스트 버전(nightly)", @@ -734,6 +736,8 @@ "Contact Support": "고객 지원 문의", "Convert Selection to Subgraph": "선택 영역을 서브그래프로 변환", "Convert selected nodes to group node": "선택한 노드를 그룹 노드로 변환", + "Custom Nodes (Beta)": "사용자 정의 노드 (베타)", + "Custom Nodes (Legacy)": "사용자 정의 노드 (레거시)", "Delete Selected Items": "선택한 항목 삭제", "Desktop User Guide": "데스크톱 사용자 가이드", "Duplicate Current Workflow": "현재 워크플로 복제", @@ -752,6 +756,8 @@ "Move Selected Nodes Left": "선택한 노드 왼쪽으로 이동", "Move Selected Nodes Right": "선택한 노드 오른쪽으로 이동", "Move Selected Nodes Up": "선택한 노드 위로 이동", + "Manager": "매니저", + "Manager Menu (Legacy)": "매니저 메뉴 (레거시)", "Mute/Unmute Selected Nodes": "선택한 노드 활성화/비활성화", "New": "새로 만들기", "Next Opened Workflow": "다음 열린 워크플로", @@ -796,6 +802,8 @@ "Toggle the Custom Nodes Manager Progress Bar": "커스텀 노드 매니저 진행률 표시줄 전환", "Undo": "실행 취소", "Ungroup selected group nodes": "선택한 그룹 노드 그룹 해제", + "Unload Models": "모델 언로드", + "Unload Models and Execution Cache": "모델 및 실행 캐시 언로드", "Workflow": "워크플로", "Zoom In": "확대", "Zoom Out": "축소" diff --git a/src/locales/ru/commands.json b/src/locales/ru/commands.json index ff3ae1195..8e881eccd 100644 --- a/src/locales/ru/commands.json +++ b/src/locales/ru/commands.json @@ -152,8 +152,14 @@ "Comfy_LoadDefaultWorkflow": { "label": "Загрузить стандартный рабочий процесс" }, - "Comfy_Manager_CustomNodesManager": { - "label": "Менеджер Пользовательских Узлов" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "Пользовательские узлы (Бета)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "Пользовательские узлы (Устаревшие)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "Меню менеджера (Устаревшее)" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Переключить диалоговое окно прогресса" @@ -161,6 +167,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "Открыть редактор масок для выбранной ноды" }, + "Comfy_Memory_UnloadModels": { + "label": "Выгрузить модели" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "Выгрузить модели и кэш выполнения" + }, "Comfy_NewBlankWorkflow": { "label": "Новый пустой рабочий процесс" }, diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index bd75079e9..11eab9c32 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -270,6 +270,7 @@ "color": "Цвет", "comingSoon": "Скоро будет", "command": "Команда", + "commandProhibited": "Команда {command} запрещена. Свяжитесь с администратором для получения дополнительной информации.", "community": "Сообщество", "completed": "Завершено", "confirm": "Подтвердить", @@ -628,6 +629,7 @@ "installationQueue": "Очередь установки", "lastUpdated": "Последнее обновление", "latestVersion": "Последняя", + "legacyMenuNotAvailable": "Устаревшее меню менеджера недоступно в этой версии ComfyUI. Пожалуйста, используйте новое меню менеджера.", "license": "Лицензия", "loadingVersions": "Загрузка версий...", "nightlyVersion": "Ночная", @@ -734,6 +736,8 @@ "Contact Support": "Связаться с поддержкой", "Convert Selection to Subgraph": "Преобразовать выделенное в подграф", "Convert selected nodes to group node": "Преобразовать выбранные ноды в групповую ноду", + "Custom Nodes (Beta)": "Пользовательские узлы (Бета)", + "Custom Nodes (Legacy)": "Пользовательские узлы (Устаревшие)", "Delete Selected Items": "Удалить выбранные элементы", "Desktop User Guide": "Руководство пользователя для настольных ПК", "Duplicate Current Workflow": "Дублировать текущий рабочий процесс", @@ -752,6 +756,8 @@ "Move Selected Nodes Left": "Переместить выбранные узлы влево", "Move Selected Nodes Right": "Переместить выбранные узлы вправо", "Move Selected Nodes Up": "Переместить выбранные узлы вверх", + "Manager": "Менеджер", + "Manager Menu (Legacy)": "Меню менеджера (Устаревшее)", "Mute/Unmute Selected Nodes": "Отключить/включить звук для выбранных нод", "New": "Новый", "Next Opened Workflow": "Следующий открытый рабочий процесс", @@ -796,6 +802,8 @@ "Toggle the Custom Nodes Manager Progress Bar": "Переключить индикатор выполнения менеджера пользовательских узлов", "Undo": "Отменить", "Ungroup selected group nodes": "Разгруппировать выбранные групповые ноды", + "Unload Models": "Выгрузить модели", + "Unload Models and Execution Cache": "Выгрузить модели и кэш выполнения", "Workflow": "Рабочий процесс", "Zoom In": "Увеличить", "Zoom Out": "Уменьшить" diff --git a/src/locales/zh/commands.json b/src/locales/zh/commands.json index a0dfcc84c..2c8dad12f 100644 --- a/src/locales/zh/commands.json +++ b/src/locales/zh/commands.json @@ -152,8 +152,14 @@ "Comfy_LoadDefaultWorkflow": { "label": "加载默认工作流" }, - "Comfy_Manager_CustomNodesManager": { - "label": "自定义节点管理器" + "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { + "label": "自定义节点(测试版)" + }, + "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { + "label": "自定义节点(旧版)" + }, + "Comfy_Manager_ShowLegacyManagerMenu": { + "label": "管理菜单(旧版)" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "切换进度对话框" @@ -161,6 +167,12 @@ "Comfy_MaskEditor_OpenMaskEditor": { "label": "打开选中节点的遮罩编辑器" }, + "Comfy_Memory_UnloadModels": { + "label": "卸载模型" + }, + "Comfy_Memory_UnloadModelsAndExecutionCache": { + "label": "卸载模型和执行缓存" + }, "Comfy_NewBlankWorkflow": { "label": "新建空白工作流" }, diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index 8181ee0ed..2cf82dda4 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -270,6 +270,7 @@ "color": "颜色", "comingSoon": "即将推出", "command": "指令", + "commandProhibited": "命令 {command} 被禁止。请联系管理员获取更多信息。", "community": "社区", "completed": "已完成", "confirm": "确认", @@ -628,6 +629,7 @@ "installationQueue": "安装队列", "lastUpdated": "最后更新", "latestVersion": "最新", + "legacyMenuNotAvailable": "在此版本的ComfyUI中,不提供旧版的管理器菜单。请使用新的管理器菜单。", "license": "许可证", "loadingVersions": "正在加载版本...", "nightlyVersion": "每夜", @@ -734,6 +736,8 @@ "Contact Support": "联系支持", "Convert Selection to Subgraph": "将选中内容转换为子图", "Convert selected nodes to group node": "将选中节点转换为组节点", + "Custom Nodes (Beta)": "自定义节点(测试版)", + "Custom Nodes (Legacy)": "自定义节点(旧版)", "Delete Selected Items": "删除选定的项目", "Desktop User Guide": "桌面端用户指南", "Duplicate Current Workflow": "复制当前工作流", @@ -752,6 +756,8 @@ "Move Selected Nodes Left": "左移所选节点", "Move Selected Nodes Right": "右移所选节点", "Move Selected Nodes Up": "上移所选节点", + "Manager": "管理器", + "Manager Menu (Legacy)": "管理器菜单(旧版)", "Mute/Unmute Selected Nodes": "静音/取消静音选定节点", "New": "新建", "Next Opened Workflow": "下一个打开的工作流", @@ -796,6 +802,8 @@ "Toggle the Custom Nodes Manager Progress Bar": "切换自定义节点管理器进度条", "Undo": "撤销", "Ungroup selected group nodes": "解散选中组节点", + "Unload Models": "卸载模型", + "Unload Models and Execution Cache": "卸载模型和执行缓存", "Workflow": "工作流", "Zoom In": "放大画面", "Zoom Out": "缩小画面" From de6ed34836f5ff5859b57003ab2ebc9bdbf88211 Mon Sep 17 00:00:00 2001 From: bymyself Date: Wed, 9 Apr 2025 14:50:56 -0700 Subject: [PATCH 18/29] switch to v2 manager API endpoints --- src/services/comfyManagerService.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/services/comfyManagerService.ts b/src/services/comfyManagerService.ts index b98be5958..17fc9eb44 100644 --- a/src/services/comfyManagerService.ts +++ b/src/services/comfyManagerService.ts @@ -19,20 +19,20 @@ const GENERIC_SECURITY_ERR_MSG = * API routes for ComfyUI Manager */ enum ManagerRoute { - START_QUEUE = 'manager/queue/start', - RESET_QUEUE = 'manager/queue/reset', - QUEUE_STATUS = 'manager/queue/status', - INSTALL = 'manager/queue/install', - UPDATE = 'manager/queue/update', - UPDATE_ALL = 'manager/queue/update_all', - UNINSTALL = 'manager/queue/uninstall', - DISABLE = 'manager/queue/disable', - FIX_NODE = 'manager/queue/fix', - LIST_INSTALLED = 'customnode/installed', - GET_NODES = 'customnode/getmappings', - GET_PACKS = 'customnode/getlist', - IMPORT_FAIL_INFO = 'customnode/import_fail_info', - REBOOT = 'manager/reboot' + START_QUEUE = 'v2/manager/queue/start', + RESET_QUEUE = 'v2/manager/queue/reset', + QUEUE_STATUS = 'v2/manager/queue/status', + INSTALL = 'v2/manager/queue/install', + UPDATE = 'v2/manager/queue/update', + UPDATE_ALL = 'v2/manager/queue/update_all', + UNINSTALL = 'v2/manager/queue/uninstall', + DISABLE = 'v2/manager/queue/disable', + FIX_NODE = 'v2/manager/queue/fix', + LIST_INSTALLED = 'v2/customnode/installed', + GET_NODES = 'v2/customnode/getmappings', + GET_PACKS = 'v2/customnode/getlist', + IMPORT_FAIL_INFO = 'v2/customnode/import_fail_info', + REBOOT = 'v2/manager/reboot' } const managerApiClient = axios.create({ From 54a098103164da51720eddd3308c61da3ca80a44 Mon Sep 17 00:00:00 2001 From: bymyself Date: Thu, 10 Apr 2025 22:09:40 -0700 Subject: [PATCH 19/29] re-arrange menu items --- src/constants/coreMenuCommands.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/constants/coreMenuCommands.ts b/src/constants/coreMenuCommands.ts index 9fc0561eb..cd0911ce1 100644 --- a/src/constants/coreMenuCommands.ts +++ b/src/constants/coreMenuCommands.ts @@ -11,17 +11,22 @@ export const CORE_MENU_COMMANDS = [ ] ], [['Edit'], ['Comfy.Undo', 'Comfy.Redo']], - [['Edit'], ['Comfy.RefreshNodeDefinitions']], + [ + ['Edit'], + [ + 'Comfy.RefreshNodeDefinitions', + 'Comfy.Memory.UnloadModels', + 'Comfy.Memory.UnloadModelsAndExecutionCache' + ] + ], [['Edit'], ['Comfy.ClearWorkflow']], [['Edit'], ['Comfy.OpenClipspace']], [ ['Manager'], [ - 'Comfy.Manager.ShowLegacyManagerMenu', - 'Comfy.Manager.CustomNodesManager.ShowLegacyCustomNodesMenu', 'Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu', - 'Comfy.Memory.UnloadModels', - 'Comfy.Memory.UnloadModelsAndExecutionCache' + 'Comfy.Manager.ShowLegacyManagerMenu', + 'Comfy.Manager.CustomNodesManager.ShowLegacyCustomNodesMenu' ] ], [ From eb5c49f67bed2a1a9a7a0a284441c23c21f3d0de Mon Sep 17 00:00:00 2001 From: bymyself Date: Mon, 14 Apr 2025 10:07:03 -0700 Subject: [PATCH 20/29] await promises. update settings schema --- src/composables/useCoreCommands.ts | 8 ++++---- src/schemas/apiSchema.ts | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index e3627658b..69f415053 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -764,9 +764,9 @@ export function useCoreCommands(): ComfyCommand[] { icon: 'pi pi-bars', label: 'Custom Nodes (Legacy)', versionAdded: '1.16.4', - function: () => { + function: async () => { try { - useCommandStore().execute( + await useCommandStore().execute( 'Comfy.Manager.CustomNodesManager.ToggleVisibility' ) } catch (error) { @@ -784,9 +784,9 @@ export function useCoreCommands(): ComfyCommand[] { icon: 'mdi mdi-puzzle', label: 'Manager Menu (Legacy)', versionAdded: '1.16.4', - function: () => { + function: async () => { try { - useCommandStore().execute('Comfy.Manager.Menu.ToggleVisibility') + await useCommandStore().execute('Comfy.Manager.Menu.ToggleVisibility') } catch (error) { useToastStore().add({ severity: 'error', diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index 12fbe2981..0d2709d53 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -474,6 +474,7 @@ const zSettings = z.object({ 'Comfy.Load3D.LightIntensityMinimum': z.number(), 'Comfy.Load3D.LightAdjustmentIncrement': z.number(), 'Comfy.Load3D.CameraType': z.enum(['perspective', 'orthographic']), + 'Comfy.Memory.AllowManualUnload': z.boolean(), 'pysssss.SnapToGrid': z.boolean(), /** VHS setting is used for queue video preview support. */ 'VHS.AdvancedPreviews': z.string(), From aba2e5edfc8dfebc57a60377c4a3a3231acaaf9c Mon Sep 17 00:00:00 2001 From: bymyself Date: Mon, 14 Apr 2025 10:19:52 -0700 Subject: [PATCH 21/29] move legacy option to startup arg --- src/composables/useCoreCommands.ts | 23 +++++++++++++++++++++-- src/constants/coreMenuCommands.ts | 9 +-------- src/locales/en/main.json | 2 +- src/services/comfyManagerService.ts | 15 +++++++++++++-- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index 69f415053..3cd370a76 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -15,6 +15,7 @@ import { t } from '@/i18n' import { api } from '@/scripts/api' import { app } from '@/scripts/app' import { addFluxKontextGroupNode } from '@/scripts/fluxKontextEditNode' +import { useComfyManagerService } from '@/services/comfyManagerService' import { useDialogService } from '@/services/dialogService' import { useLitegraphService } from '@/services/litegraphService' import { useWorkflowService } from '@/services/workflowService' @@ -665,8 +666,26 @@ export function useCoreCommands(): ComfyCommand[] { icon: 'pi pi-objects-column', label: 'Custom Nodes (Beta)', versionAdded: '1.12.10', - function: () => { - dialogService.toggleManagerDialog() + function: async () => { + const isLegacyManagerUI = + await useComfyManagerService().isLegacyManagerUI() + if (isLegacyManagerUI) { + try { + await useCommandStore().execute( + 'Comfy.Manager.Menu.ToggleVisibility' // This command is registered by legacy manager FE extension + ) + } catch (error) { + useToastStore().add({ + severity: 'error', + summary: t('g.error'), + detail: t('manager.legacyMenuNotAvailable'), + life: 3000 + }) + dialogService.showManagerDialog() + } + } else { + dialogService.showManagerDialog() + } } }, { diff --git a/src/constants/coreMenuCommands.ts b/src/constants/coreMenuCommands.ts index cd0911ce1..725ad95c6 100644 --- a/src/constants/coreMenuCommands.ts +++ b/src/constants/coreMenuCommands.ts @@ -21,14 +21,7 @@ export const CORE_MENU_COMMANDS = [ ], [['Edit'], ['Comfy.ClearWorkflow']], [['Edit'], ['Comfy.OpenClipspace']], - [ - ['Manager'], - [ - 'Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu', - 'Comfy.Manager.ShowLegacyManagerMenu', - 'Comfy.Manager.CustomNodesManager.ShowLegacyCustomNodesMenu' - ] - ], + [['Manager'], ['Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu']], [ ['Help'], [ diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 90be6ca73..42dab3eb0 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -138,7 +138,7 @@ }, "manager": { "title": "Custom Nodes Manager", - "legacyMenuNotAvailable": "Legacy manager menu is not available in this version of ComfyUI. Please use the new manager menu instead.", + "legacyMenuNotAvailable": "Legacy manager menu is not available, defaulting to the new manager menu.", "failed": "Failed ({count})", "noNodesFound": "No nodes found", "noNodesFoundDescription": "The pack's nodes either could not be parsed, or the pack is a frontend extension only and doesn't have any nodes.", diff --git a/src/services/comfyManagerService.ts b/src/services/comfyManagerService.ts index 17fc9eb44..0e8f245ac 100644 --- a/src/services/comfyManagerService.ts +++ b/src/services/comfyManagerService.ts @@ -32,7 +32,8 @@ enum ManagerRoute { GET_NODES = 'v2/customnode/getmappings', GET_PACKS = 'v2/customnode/getlist', IMPORT_FAIL_INFO = 'v2/customnode/import_fail_info', - REBOOT = 'v2/manager/reboot' + REBOOT = 'v2/manager/reboot', + IS_LEGACY_MANAGER_UI = 'v2/manager/is_legacy_manager_ui' } const managerApiClient = axios.create({ @@ -247,6 +248,15 @@ export const useComfyManagerService = () => { ) } + const isLegacyManagerUI = async (signal?: AbortSignal) => { + const errorContext = 'Checking if user set Manager to use the legacy UI' + + return executeRequest( + () => managerApiClient.get(ManagerRoute.IS_LEGACY_MANAGER_UI, { signal }), + { errorContext } + ) + } + return { // State isLoading, @@ -268,6 +278,7 @@ export const useComfyManagerService = () => { updateAllPacks, // System operations - rebootComfyUI + rebootComfyUI, + isLegacyManagerUI } } From f8953a874d094c0b8dab25b500bd2c07bd45bd75 Mon Sep 17 00:00:00 2001 From: bymyself Date: Mon, 14 Apr 2025 10:33:11 -0700 Subject: [PATCH 22/29] Add banner indicating how to use legacy manager UI --- .../dialog/content/manager/ManagerHeader.vue | 10 ++++++++++ src/locales/en/main.json | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/components/dialog/content/manager/ManagerHeader.vue b/src/components/dialog/content/manager/ManagerHeader.vue index f6177c87b..bd438b704 100644 --- a/src/components/dialog/content/manager/ManagerHeader.vue +++ b/src/components/dialog/content/manager/ManagerHeader.vue @@ -4,6 +4,16 @@

{{ $t('manager.discoverCommunityContent') }}

+ diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 42dab3eb0..e97bc5727 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -139,6 +139,8 @@ "manager": { "title": "Custom Nodes Manager", "legacyMenuNotAvailable": "Legacy manager menu is not available, defaulting to the new manager menu.", + "legacyManagerUI": "Use Legacy UI", + "legacyManagerUIDescription": "To use the legacy Manager UI, start ComfyUI with --enable-manager-legacy-ui", "failed": "Failed ({count})", "noNodesFound": "No nodes found", "noNodesFoundDescription": "The pack's nodes either could not be parsed, or the pack is a frontend extension only and doesn't have any nodes.", From e780e1d6d6dedc561ed3a7d41cee7e12317d11db Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 14 Apr 2025 17:36:52 +0000 Subject: [PATCH 23/29] Update locales [skip ci] --- src/locales/en/commands.json | 6 ------ src/locales/es/commands.json | 6 ------ src/locales/es/main.json | 4 ++-- src/locales/fr/commands.json | 6 ------ src/locales/fr/main.json | 4 ++-- src/locales/ja/commands.json | 6 ------ src/locales/ja/main.json | 4 ++-- src/locales/ko/commands.json | 6 ------ src/locales/ko/main.json | 4 ++-- src/locales/ru/commands.json | 6 ------ src/locales/ru/main.json | 4 ++-- src/locales/zh/commands.json | 6 ------ src/locales/zh/main.json | 4 ++-- 13 files changed, 12 insertions(+), 54 deletions(-) diff --git a/src/locales/en/commands.json b/src/locales/en/commands.json index 65c49a504..c5180c1f2 100644 --- a/src/locales/en/commands.json +++ b/src/locales/en/commands.json @@ -155,12 +155,6 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "Custom Nodes (Beta)" }, - "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { - "label": "Custom Nodes (Legacy)" - }, - "Comfy_Manager_ShowLegacyManagerMenu": { - "label": "Manager Menu (Legacy)" - }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Toggle the Custom Nodes Manager Progress Bar" }, diff --git a/src/locales/es/commands.json b/src/locales/es/commands.json index e20d7bd55..670de6c3e 100644 --- a/src/locales/es/commands.json +++ b/src/locales/es/commands.json @@ -155,12 +155,6 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "Nodos personalizados (Beta)" }, - "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { - "label": "Nodos personalizados (Legacy)" - }, - "Comfy_Manager_ShowLegacyManagerMenu": { - "label": "Menú del administrador (Legacy)" - }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Alternar diálogo de progreso del administrador" }, diff --git a/src/locales/es/main.json b/src/locales/es/main.json index 17a39a237..1dfdce865 100644 --- a/src/locales/es/main.json +++ b/src/locales/es/main.json @@ -629,6 +629,8 @@ "installationQueue": "Cola de Instalación", "lastUpdated": "Última Actualización", "latestVersion": "Última", + "legacyManagerUI": "Usar UI antigua", + "legacyManagerUIDescription": "Para usar la UI antigua del Manager, inicia ComfyUI con --enable-manager-legacy-ui", "legacyMenuNotAvailable": "El menú del administrador antiguo no está disponible en esta versión de ComfyUI. Por favor, utiliza el nuevo menú del administrador en su lugar.", "license": "Licencia", "loadingVersions": "Cargando versiones...", @@ -737,7 +739,6 @@ "Convert Selection to Subgraph": "Convertir selección en subgrafo", "Convert selected nodes to group node": "Convertir nodos seleccionados en nodo de grupo", "Custom Nodes (Beta)": "Nodos personalizados (Beta)", - "Custom Nodes (Legacy)": "Nodos personalizados (Antiguo)", "Delete Selected Items": "Eliminar elementos seleccionados", "Desktop User Guide": "Guía de usuario de escritorio", "Duplicate Current Workflow": "Duplicar flujo de trabajo actual", @@ -757,7 +758,6 @@ "Move Selected Nodes Right": "Mover nodos seleccionados hacia la derecha", "Move Selected Nodes Up": "Mover nodos seleccionados hacia arriba", "Manager": "Administrador", - "Manager Menu (Legacy)": "Menú del administrador (Antiguo)", "Mute/Unmute Selected Nodes": "Silenciar/Activar sonido de nodos seleccionados", "New": "Nuevo", "Next Opened Workflow": "Siguiente flujo de trabajo abierto", diff --git a/src/locales/fr/commands.json b/src/locales/fr/commands.json index c77231f2b..e46d31737 100644 --- a/src/locales/fr/commands.json +++ b/src/locales/fr/commands.json @@ -155,12 +155,6 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "Nœuds personnalisés (Beta)" }, - "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { - "label": "Nœuds personnalisés (Legacy)" - }, - "Comfy_Manager_ShowLegacyManagerMenu": { - "label": "Menu du gestionnaire (Legacy)" - }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Basculer la boîte de dialogue de progression" }, diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index 6849ad805..d451f8284 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -629,6 +629,8 @@ "installationQueue": "File d'attente d'installation", "lastUpdated": "Dernière mise à jour", "latestVersion": "Dernière", + "legacyManagerUI": "Utiliser l'interface utilisateur héritée", + "legacyManagerUIDescription": "Pour utiliser l'interface utilisateur de gestion héritée, démarrez ComfyUI avec --enable-manager-legacy-ui", "legacyMenuNotAvailable": "Le menu du gestionnaire de l'ancienne version n'est pas disponible dans cette version de ComfyUI. Veuillez utiliser le nouveau menu du gestionnaire à la place.", "license": "Licence", "loadingVersions": "Chargement des versions...", @@ -737,7 +739,6 @@ "Convert Selection to Subgraph": "Convertir la sélection en sous-graphe", "Convert selected nodes to group node": "Convertir les nœuds sélectionnés en nœud de groupe", "Custom Nodes (Beta)": "Nœuds personnalisés (Beta)", - "Custom Nodes (Legacy)": "Nœuds personnalisés (Ancienne version)", "Delete Selected Items": "Supprimer les éléments sélectionnés", "Desktop User Guide": "Guide de l'utilisateur de bureau", "Duplicate Current Workflow": "Dupliquer le flux de travail actuel", @@ -757,7 +758,6 @@ "Move Selected Nodes Right": "Déplacer les nœuds sélectionnés vers la droite", "Move Selected Nodes Up": "Déplacer les nœuds sélectionnés vers le haut", "Manager": "Gestionnaire", - "Manager Menu (Legacy)": "Menu du gestionnaire (Ancienne version)", "Mute/Unmute Selected Nodes": "Mettre en sourdine/Activer le son des nœuds sélectionnés", "New": "Nouveau", "Next Opened Workflow": "Prochain flux de travail ouvert", diff --git a/src/locales/ja/commands.json b/src/locales/ja/commands.json index ce0d9848d..09f2ba802 100644 --- a/src/locales/ja/commands.json +++ b/src/locales/ja/commands.json @@ -155,12 +155,6 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "カスタムノード(ベータ版)" }, - "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { - "label": "カスタムノード(レガシー)" - }, - "Comfy_Manager_ShowLegacyManagerMenu": { - "label": "マネージャーメニュー(レガシー)" - }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "プログレスダイアログの切り替え" }, diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index 989664f5c..5cf89f94a 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -629,6 +629,8 @@ "installationQueue": "インストールキュー", "lastUpdated": "最終更新日", "latestVersion": "最新", + "legacyManagerUI": "レガシーUIを使用する", + "legacyManagerUIDescription": "レガシーManager UIを使用するには、--enable-manager-legacy-uiを付けてComfyUIを起動してください", "legacyMenuNotAvailable": "このバージョンのComfyUIでは、レガシーマネージャーメニューは利用できません。新しいマネージャーメニューを使用してください。", "license": "ライセンス", "loadingVersions": "バージョンを読み込んでいます...", @@ -737,7 +739,6 @@ "Convert Selection to Subgraph": "選択範囲をサブグラフに変換", "Convert selected nodes to group node": "選択したノードをグループノードに変換", "Custom Nodes (Beta)": "カスタムノード(ベータ)", - "Custom Nodes (Legacy)": "カスタムノード(レガシー)", "Delete Selected Items": "選択したアイテムを削除", "Desktop User Guide": "デスクトップユーザーガイド", "Duplicate Current Workflow": "現在のワークフローを複製", @@ -757,7 +758,6 @@ "Move Selected Nodes Right": "選択したノードを右へ移動", "Move Selected Nodes Up": "選択したノードを上へ移動", "Manager": "マネージャー", - "Manager Menu (Legacy)": "マネージャーメニュー(レガシー)", "Mute/Unmute Selected Nodes": "選択したノードのミュート/ミュート解除", "New": "新規", "Next Opened Workflow": "次に開いたワークフロー", diff --git a/src/locales/ko/commands.json b/src/locales/ko/commands.json index bb5dc7d51..d93a80904 100644 --- a/src/locales/ko/commands.json +++ b/src/locales/ko/commands.json @@ -155,12 +155,6 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "사용자 정의 노드 (베타)" }, - "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { - "label": "사용자 정의 노드 (레거시)" - }, - "Comfy_Manager_ShowLegacyManagerMenu": { - "label": "매니저 메뉴 (레거시)" - }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "진행 상황 대화 상자 전환" }, diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index cd1d1beab..cac430464 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -629,6 +629,8 @@ "installationQueue": "설치 대기열", "lastUpdated": "마지막 업데이트", "latestVersion": "최신", + "legacyManagerUI": "레거시 UI 사용", + "legacyManagerUIDescription": "레거시 매니저 UI를 사용하려면, ComfyUI를 --enable-manager-legacy-ui로 시작하세요", "legacyMenuNotAvailable": "이 버전의 ComfyUI에서는 레거시 매니저 메뉴를 사용할 수 없습니다. 대신 새로운 매니저 메뉴를 사용하십시오.", "license": "라이선스", "loadingVersions": "버전 로딩 중...", @@ -737,7 +739,6 @@ "Convert Selection to Subgraph": "선택 영역을 서브그래프로 변환", "Convert selected nodes to group node": "선택한 노드를 그룹 노드로 변환", "Custom Nodes (Beta)": "사용자 정의 노드 (베타)", - "Custom Nodes (Legacy)": "사용자 정의 노드 (레거시)", "Delete Selected Items": "선택한 항목 삭제", "Desktop User Guide": "데스크톱 사용자 가이드", "Duplicate Current Workflow": "현재 워크플로 복제", @@ -757,7 +758,6 @@ "Move Selected Nodes Right": "선택한 노드 오른쪽으로 이동", "Move Selected Nodes Up": "선택한 노드 위로 이동", "Manager": "매니저", - "Manager Menu (Legacy)": "매니저 메뉴 (레거시)", "Mute/Unmute Selected Nodes": "선택한 노드 활성화/비활성화", "New": "새로 만들기", "Next Opened Workflow": "다음 열린 워크플로", diff --git a/src/locales/ru/commands.json b/src/locales/ru/commands.json index 8e881eccd..0f070956a 100644 --- a/src/locales/ru/commands.json +++ b/src/locales/ru/commands.json @@ -155,12 +155,6 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "Пользовательские узлы (Бета)" }, - "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { - "label": "Пользовательские узлы (Устаревшие)" - }, - "Comfy_Manager_ShowLegacyManagerMenu": { - "label": "Меню менеджера (Устаревшее)" - }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Переключить диалоговое окно прогресса" }, diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index 11eab9c32..bab825d1f 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -629,6 +629,8 @@ "installationQueue": "Очередь установки", "lastUpdated": "Последнее обновление", "latestVersion": "Последняя", + "legacyManagerUI": "Использовать устаревший UI", + "legacyManagerUIDescription": "Чтобы использовать устаревший UI менеджера, запустите ComfyUI с --enable-manager-legacy-ui", "legacyMenuNotAvailable": "Устаревшее меню менеджера недоступно в этой версии ComfyUI. Пожалуйста, используйте новое меню менеджера.", "license": "Лицензия", "loadingVersions": "Загрузка версий...", @@ -737,7 +739,6 @@ "Convert Selection to Subgraph": "Преобразовать выделенное в подграф", "Convert selected nodes to group node": "Преобразовать выбранные ноды в групповую ноду", "Custom Nodes (Beta)": "Пользовательские узлы (Бета)", - "Custom Nodes (Legacy)": "Пользовательские узлы (Устаревшие)", "Delete Selected Items": "Удалить выбранные элементы", "Desktop User Guide": "Руководство пользователя для настольных ПК", "Duplicate Current Workflow": "Дублировать текущий рабочий процесс", @@ -757,7 +758,6 @@ "Move Selected Nodes Right": "Переместить выбранные узлы вправо", "Move Selected Nodes Up": "Переместить выбранные узлы вверх", "Manager": "Менеджер", - "Manager Menu (Legacy)": "Меню менеджера (Устаревшее)", "Mute/Unmute Selected Nodes": "Отключить/включить звук для выбранных нод", "New": "Новый", "Next Opened Workflow": "Следующий открытый рабочий процесс", diff --git a/src/locales/zh/commands.json b/src/locales/zh/commands.json index 2c8dad12f..af1ba868d 100644 --- a/src/locales/zh/commands.json +++ b/src/locales/zh/commands.json @@ -155,12 +155,6 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "自定义节点(测试版)" }, - "Comfy_Manager_CustomNodesManager_ShowLegacyCustomNodesMenu": { - "label": "自定义节点(旧版)" - }, - "Comfy_Manager_ShowLegacyManagerMenu": { - "label": "管理菜单(旧版)" - }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "切换进度对话框" }, diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index 2cf82dda4..4f96673fc 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -629,6 +629,8 @@ "installationQueue": "安装队列", "lastUpdated": "最后更新", "latestVersion": "最新", + "legacyManagerUI": "使用旧版UI", + "legacyManagerUIDescription": "要使用旧版的管理器UI,请启动ComfyUI并使用 --enable-manager-legacy-ui", "legacyMenuNotAvailable": "在此版本的ComfyUI中,不提供旧版的管理器菜单。请使用新的管理器菜单。", "license": "许可证", "loadingVersions": "正在加载版本...", @@ -737,7 +739,6 @@ "Convert Selection to Subgraph": "将选中内容转换为子图", "Convert selected nodes to group node": "将选中节点转换为组节点", "Custom Nodes (Beta)": "自定义节点(测试版)", - "Custom Nodes (Legacy)": "自定义节点(旧版)", "Delete Selected Items": "删除选定的项目", "Desktop User Guide": "桌面端用户指南", "Duplicate Current Workflow": "复制当前工作流", @@ -757,7 +758,6 @@ "Move Selected Nodes Right": "右移所选节点", "Move Selected Nodes Up": "上移所选节点", "Manager": "管理器", - "Manager Menu (Legacy)": "管理器菜单(旧版)", "Mute/Unmute Selected Nodes": "静音/取消静音选定节点", "New": "新建", "Next Opened Workflow": "下一个打开的工作流", From b01056e31fd2455f7c15110057fc3809487b4c02 Mon Sep 17 00:00:00 2001 From: bymyself Date: Mon, 14 Apr 2025 10:43:24 -0700 Subject: [PATCH 24/29] add "Check for Updates", "Install Missing" menu items --- src/composables/useCoreCommands.ts | 25 ++++++++++++++++++++++++- src/constants/coreMenuCommands.ts | 9 ++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index 3cd370a76..ec6ccc17d 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -31,6 +31,7 @@ import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore' import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore' import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore' import { useWorkspaceStore } from '@/stores/workspaceStore' +import { ManagerTab } from '@/types/comfyManagerTypes' const moveSelectedNodesVersionAdded = '1.22.2' @@ -664,7 +665,7 @@ export function useCoreCommands(): ComfyCommand[] { { id: 'Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu', icon: 'pi pi-objects-column', - label: 'Custom Nodes (Beta)', + label: 'Custom Nodes Manager', versionAdded: '1.12.10', function: async () => { const isLegacyManagerUI = @@ -688,6 +689,28 @@ export function useCoreCommands(): ComfyCommand[] { } } }, + { + id: 'Comfy.Manager.ShowUpdateAvailablePacks', + icon: 'pi pi-sync', + label: 'Check for Updates', + versionAdded: '1.17.0', + function: () => { + dialogService.showManagerDialog({ + initialTab: ManagerTab.UpdateAvailable + }) + } + }, + { + id: 'Comfy.Manager.ShowMissingPacks', + icon: 'pi pi-exclamation-circle', + label: 'Install Missing', + versionAdded: '1.17.0', + function: () => { + dialogService.showManagerDialog({ + initialTab: ManagerTab.Missing + }) + } + }, { id: 'Comfy.Manager.ToggleManagerProgressDialog', icon: 'pi pi-spinner', diff --git a/src/constants/coreMenuCommands.ts b/src/constants/coreMenuCommands.ts index 725ad95c6..46fb6265e 100644 --- a/src/constants/coreMenuCommands.ts +++ b/src/constants/coreMenuCommands.ts @@ -21,7 +21,14 @@ export const CORE_MENU_COMMANDS = [ ], [['Edit'], ['Comfy.ClearWorkflow']], [['Edit'], ['Comfy.OpenClipspace']], - [['Manager'], ['Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu']], + [ + ['Manager'], + [ + 'Comfy.Manager.CustomNodesManager.ShowCustomNodesMenu', + 'Comfy.Manager.ShowMissingPacks', + 'Comfy.Manager.ShowUpdateAvailablePacks' + ] + ], [ ['Help'], [ From bdd123090202712086ba83a507cea2822a699bdd Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 14 Apr 2025 17:47:46 +0000 Subject: [PATCH 25/29] Update locales [skip ci] --- src/locales/en/commands.json | 8 +++++++- src/locales/en/main.json | 2 ++ src/locales/es/commands.json | 6 ++++++ src/locales/es/main.json | 3 ++- src/locales/fr/commands.json | 6 ++++++ src/locales/fr/main.json | 5 +++-- src/locales/ja/commands.json | 6 ++++++ src/locales/ja/main.json | 5 +++-- src/locales/ko/commands.json | 6 ++++++ src/locales/ko/main.json | 3 ++- src/locales/ru/commands.json | 6 ++++++ src/locales/ru/main.json | 5 +++-- src/locales/zh/commands.json | 6 ++++++ src/locales/zh/main.json | 3 ++- 14 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/locales/en/commands.json b/src/locales/en/commands.json index c5180c1f2..74d739062 100644 --- a/src/locales/en/commands.json +++ b/src/locales/en/commands.json @@ -153,7 +153,13 @@ "label": "Load Default Workflow" }, "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { - "label": "Custom Nodes (Beta)" + "label": "Custom Nodes Manager" + }, + "Comfy_Manager_ShowMissingPacks": { + "label": "Install Missing" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "Check for Updates" }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Toggle the Custom Nodes Manager Progress Bar" diff --git a/src/locales/en/main.json b/src/locales/en/main.json index e97bc5727..025d18add 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -976,6 +976,8 @@ "Custom Nodes (Beta)": "Custom Nodes (Beta)", "Custom Nodes (Legacy)": "Custom Nodes (Legacy)", "Manager Menu (Legacy)": "Manager Menu (Legacy)", + "Custom Nodes Manager": "Custom Nodes Manager", + "Install Missing": "Install Missing", "Toggle Progress Dialog": "Toggle Progress Dialog", "Unload Models": "Unload Models", "Unload Models and Execution Cache": "Unload Models and Execution Cache", diff --git a/src/locales/es/commands.json b/src/locales/es/commands.json index 670de6c3e..3332d677e 100644 --- a/src/locales/es/commands.json +++ b/src/locales/es/commands.json @@ -155,6 +155,12 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "Nodos personalizados (Beta)" }, + "Comfy_Manager_ShowMissingPacks": { + "label": "Instalar faltantes" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "Buscar actualizaciones" + }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Alternar diálogo de progreso del administrador" }, diff --git a/src/locales/es/main.json b/src/locales/es/main.json index 1dfdce865..6c0bee2d2 100644 --- a/src/locales/es/main.json +++ b/src/locales/es/main.json @@ -738,7 +738,7 @@ "Contact Support": "Contactar soporte", "Convert Selection to Subgraph": "Convertir selección en subgrafo", "Convert selected nodes to group node": "Convertir nodos seleccionados en nodo de grupo", - "Custom Nodes (Beta)": "Nodos personalizados (Beta)", + "Custom Nodes Manager": "Administrador de Nodos Personalizados", "Delete Selected Items": "Eliminar elementos seleccionados", "Desktop User Guide": "Guía de usuario de escritorio", "Duplicate Current Workflow": "Duplicar flujo de trabajo actual", @@ -750,6 +750,7 @@ "Give Feedback": "Dar retroalimentación", "Group Selected Nodes": "Agrupar nodos seleccionados", "Help": "Ayuda", + "Install Missing": "Instalar Faltantes", "Interrupt": "Interrumpir", "Load Default Workflow": "Cargar flujo de trabajo predeterminado", "Manage group nodes": "Gestionar nodos de grupo", diff --git a/src/locales/fr/commands.json b/src/locales/fr/commands.json index e46d31737..b1ee2444f 100644 --- a/src/locales/fr/commands.json +++ b/src/locales/fr/commands.json @@ -155,6 +155,12 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "Nœuds personnalisés (Beta)" }, + "Comfy_Manager_ShowMissingPacks": { + "label": "Installer manquants" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "Vérifier les mises à jour" + }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Basculer la boîte de dialogue de progression" }, diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index d451f8284..790805057 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -725,7 +725,7 @@ "Bypass/Unbypass Selected Nodes": "Contourner/Ne pas contourner les nœuds sélectionnés", "Canvas Toggle Link Visibility": "Basculer la visibilité du lien de la toile", "Canvas Toggle Lock": "Basculer le verrouillage de la toile", - "Check for Updates": "Vérifier les mises à jour", + "Check for Updates": "Vérifier les Mises à Jour", "Clear Pending Tasks": "Effacer les tâches en attente", "Clear Workflow": "Effacer le flux de travail", "Clipspace": "Espace de clip", @@ -738,7 +738,7 @@ "Contact Support": "Contacter le support", "Convert Selection to Subgraph": "Convertir la sélection en sous-graphe", "Convert selected nodes to group node": "Convertir les nœuds sélectionnés en nœud de groupe", - "Custom Nodes (Beta)": "Nœuds personnalisés (Beta)", + "Custom Nodes Manager": "Gestionnaire de Nœuds Personnalisés", "Delete Selected Items": "Supprimer les éléments sélectionnés", "Desktop User Guide": "Guide de l'utilisateur de bureau", "Duplicate Current Workflow": "Dupliquer le flux de travail actuel", @@ -750,6 +750,7 @@ "Give Feedback": "Donnez votre avis", "Group Selected Nodes": "Grouper les nœuds sélectionnés", "Help": "Aide", + "Install Missing": "Installer Manquants", "Interrupt": "Interrompre", "Load Default Workflow": "Charger le flux de travail par défaut", "Manage group nodes": "Gérer les nœuds de groupe", diff --git a/src/locales/ja/commands.json b/src/locales/ja/commands.json index 09f2ba802..0dbc17341 100644 --- a/src/locales/ja/commands.json +++ b/src/locales/ja/commands.json @@ -155,6 +155,12 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "カスタムノード(ベータ版)" }, + "Comfy_Manager_ShowMissingPacks": { + "label": "不足しているパックをインストール" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "更新を確認" + }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "プログレスダイアログの切り替え" }, diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index 5cf89f94a..46d43b31a 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -725,7 +725,7 @@ "Bypass/Unbypass Selected Nodes": "選択したノードのバイパス/バイパス解除", "Canvas Toggle Link Visibility": "キャンバスのリンク表示を切り替え", "Canvas Toggle Lock": "キャンバスのロックを切り替え", - "Check for Updates": "更新を確認する", + "Check for Updates": "更新を確認", "Clear Pending Tasks": "保留中のタスクをクリア", "Clear Workflow": "ワークフローをクリア", "Clipspace": "クリップスペース", @@ -738,7 +738,7 @@ "Contact Support": "サポートに連絡", "Convert Selection to Subgraph": "選択範囲をサブグラフに変換", "Convert selected nodes to group node": "選択したノードをグループノードに変換", - "Custom Nodes (Beta)": "カスタムノード(ベータ)", + "Custom Nodes Manager": "カスタムノードマネージャ", "Delete Selected Items": "選択したアイテムを削除", "Desktop User Guide": "デスクトップユーザーガイド", "Duplicate Current Workflow": "現在のワークフローを複製", @@ -750,6 +750,7 @@ "Give Feedback": "フィードバックを送る", "Group Selected Nodes": "選択したノードをグループ化", "Help": "ヘルプ", + "Install Missing": "不足しているものをインストール", "Interrupt": "中断", "Load Default Workflow": "デフォルトワークフローを読み込む", "Manage group nodes": "グループノードを管理", diff --git a/src/locales/ko/commands.json b/src/locales/ko/commands.json index d93a80904..4aa99e5dc 100644 --- a/src/locales/ko/commands.json +++ b/src/locales/ko/commands.json @@ -155,6 +155,12 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "사용자 정의 노드 (베타)" }, + "Comfy_Manager_ShowMissingPacks": { + "label": "누락된 팩 설치" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "업데이트 확인" + }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "진행 상황 대화 상자 전환" }, diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index cac430464..20ca07bc5 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -738,7 +738,7 @@ "Contact Support": "고객 지원 문의", "Convert Selection to Subgraph": "선택 영역을 서브그래프로 변환", "Convert selected nodes to group node": "선택한 노드를 그룹 노드로 변환", - "Custom Nodes (Beta)": "사용자 정의 노드 (베타)", + "Custom Nodes Manager": "사용자 정의 노드 관리자", "Delete Selected Items": "선택한 항목 삭제", "Desktop User Guide": "데스크톱 사용자 가이드", "Duplicate Current Workflow": "현재 워크플로 복제", @@ -750,6 +750,7 @@ "Give Feedback": "피드백 제공", "Group Selected Nodes": "선택한 노드 그룹화", "Help": "도움말", + "Install Missing": "누락된 설치", "Interrupt": "중단", "Load Default Workflow": "기본 워크플로 불러오기", "Manage group nodes": "그룹 노드 관리", diff --git a/src/locales/ru/commands.json b/src/locales/ru/commands.json index 0f070956a..8ca8b4dfa 100644 --- a/src/locales/ru/commands.json +++ b/src/locales/ru/commands.json @@ -155,6 +155,12 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "Пользовательские узлы (Бета)" }, + "Comfy_Manager_ShowMissingPacks": { + "label": "Установить отсутствующие" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "Проверить наличие обновлений" + }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "Переключить диалоговое окно прогресса" }, diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index bab825d1f..199cd847d 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -725,7 +725,7 @@ "Bypass/Unbypass Selected Nodes": "Обойти/восстановить выбранные ноды", "Canvas Toggle Link Visibility": "Переключение видимости ссылки на холст", "Canvas Toggle Lock": "Переключение блокировки холста", - "Check for Updates": "Проверить наличие обновлений", + "Check for Updates": "Проверить Обновления", "Clear Pending Tasks": "Очистить ожидающие задачи", "Clear Workflow": "Очистить рабочий процесс", "Clipspace": "Клиппространство", @@ -738,7 +738,7 @@ "Contact Support": "Связаться с поддержкой", "Convert Selection to Subgraph": "Преобразовать выделенное в подграф", "Convert selected nodes to group node": "Преобразовать выбранные ноды в групповую ноду", - "Custom Nodes (Beta)": "Пользовательские узлы (Бета)", + "Custom Nodes Manager": "Менеджер Пользовательских Узлов", "Delete Selected Items": "Удалить выбранные элементы", "Desktop User Guide": "Руководство пользователя для настольных ПК", "Duplicate Current Workflow": "Дублировать текущий рабочий процесс", @@ -750,6 +750,7 @@ "Give Feedback": "Оставить отзыв", "Group Selected Nodes": "Сгруппировать выбранные ноды", "Help": "Помощь", + "Install Missing": "Установить Отсутствующие", "Interrupt": "Прервать", "Load Default Workflow": "Загрузить стандартный рабочий процесс", "Manage group nodes": "Управление групповыми нодами", diff --git a/src/locales/zh/commands.json b/src/locales/zh/commands.json index af1ba868d..a5bdd7182 100644 --- a/src/locales/zh/commands.json +++ b/src/locales/zh/commands.json @@ -155,6 +155,12 @@ "Comfy_Manager_CustomNodesManager_ShowCustomNodesMenu": { "label": "自定义节点(测试版)" }, + "Comfy_Manager_ShowMissingPacks": { + "label": "安装缺失的包" + }, + "Comfy_Manager_ShowUpdateAvailablePacks": { + "label": "检查更新" + }, "Comfy_Manager_ToggleManagerProgressDialog": { "label": "切换进度对话框" }, diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index 4f96673fc..ee112a555 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -738,7 +738,7 @@ "Contact Support": "联系支持", "Convert Selection to Subgraph": "将选中内容转换为子图", "Convert selected nodes to group node": "将选中节点转换为组节点", - "Custom Nodes (Beta)": "自定义节点(测试版)", + "Custom Nodes Manager": "自定义节点管理器", "Delete Selected Items": "删除选定的项目", "Desktop User Guide": "桌面端用户指南", "Duplicate Current Workflow": "复制当前工作流", @@ -750,6 +750,7 @@ "Give Feedback": "提供反馈", "Group Selected Nodes": "将选中节点转换为组节点", "Help": "帮助", + "Install Missing": "安装缺失", "Interrupt": "中断", "Load Default Workflow": "加载默认工作流", "Manage group nodes": "管理组节点", From 1aee43425a22581bff575a90b14ed689c879a7bb Mon Sep 17 00:00:00 2001 From: bymyself Date: Mon, 14 Apr 2025 16:22:45 -0700 Subject: [PATCH 26/29] use correct response shape --- src/composables/useCoreCommands.ts | 7 ++++--- src/services/comfyManagerService.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index ec6ccc17d..994be9f9e 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -668,9 +668,10 @@ export function useCoreCommands(): ComfyCommand[] { label: 'Custom Nodes Manager', versionAdded: '1.12.10', function: async () => { - const isLegacyManagerUI = - await useComfyManagerService().isLegacyManagerUI() - if (isLegacyManagerUI) { + const { is_legacy_manager_ui } = + (await useComfyManagerService().isLegacyManagerUI()) ?? {} + + if (is_legacy_manager_ui === true) { try { await useCommandStore().execute( 'Comfy.Manager.Menu.ToggleVisibility' // This command is registered by legacy manager FE extension diff --git a/src/services/comfyManagerService.ts b/src/services/comfyManagerService.ts index 0e8f245ac..79f2dd132 100644 --- a/src/services/comfyManagerService.ts +++ b/src/services/comfyManagerService.ts @@ -251,7 +251,7 @@ export const useComfyManagerService = () => { const isLegacyManagerUI = async (signal?: AbortSignal) => { const errorContext = 'Checking if user set Manager to use the legacy UI' - return executeRequest( + return executeRequest<{ is_legacy_manager_ui: boolean }>( () => managerApiClient.get(ManagerRoute.IS_LEGACY_MANAGER_UI, { signal }), { errorContext } ) From f1610db470548acf931ca4b9b909eb85dcc90823 Mon Sep 17 00:00:00 2001 From: bymyself Date: Mon, 14 Apr 2025 16:29:03 -0700 Subject: [PATCH 27/29] improve command names --- src/composables/useCoreCommands.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index 994be9f9e..c2c643a01 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -677,6 +677,7 @@ export function useCoreCommands(): ComfyCommand[] { 'Comfy.Manager.Menu.ToggleVisibility' // This command is registered by legacy manager FE extension ) } catch (error) { + console.error('error', error) useToastStore().add({ severity: 'error', summary: t('g.error'), @@ -693,7 +694,7 @@ export function useCoreCommands(): ComfyCommand[] { { id: 'Comfy.Manager.ShowUpdateAvailablePacks', icon: 'pi pi-sync', - label: 'Check for Updates', + label: 'Check for Custom Node Updates', versionAdded: '1.17.0', function: () => { dialogService.showManagerDialog({ @@ -704,7 +705,7 @@ export function useCoreCommands(): ComfyCommand[] { { id: 'Comfy.Manager.ShowMissingPacks', icon: 'pi pi-exclamation-circle', - label: 'Install Missing', + label: 'Install Missing Custom Nodes', versionAdded: '1.17.0', function: () => { dialogService.showManagerDialog({ From 4f9470751df0cd15e4d3bc073bba530ebff0a8c5 Mon Sep 17 00:00:00 2001 From: bymyself Date: Mon, 14 Apr 2025 17:46:03 -0700 Subject: [PATCH 28/29] dont show missing nodes button in legacy manager mode --- .../dialog/content/LoadWorkflowWarning.vue | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/components/dialog/content/LoadWorkflowWarning.vue b/src/components/dialog/content/LoadWorkflowWarning.vue index 201c6d242..2152aca50 100644 --- a/src/components/dialog/content/LoadWorkflowWarning.vue +++ b/src/components/dialog/content/LoadWorkflowWarning.vue @@ -31,7 +31,7 @@ -
+
import Button from 'primevue/button' import ListBox from 'primevue/listbox' -import { computed } from 'vue' +import { computed, onMounted, ref } from 'vue' import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue' -import MissingCoreNodesMessage from '@/components/dialog/content/MissingCoreNodesMessage.vue' -import PackInstallButton from '@/components/dialog/content/manager/button/PackInstallButton.vue' import { useMissingNodes } from '@/composables/nodePack/useMissingNodes' +import { useComfyManagerService } from '@/services/comfyManagerService' import { useDialogService } from '@/services/dialogService' -import { useAboutPanelStore } from '@/stores/aboutPanelStore' import type { MissingNodeType } from '@/types/comfy' import { ManagerTab } from '@/types/comfyManagerTypes' @@ -60,22 +58,11 @@ const props = defineProps<{ missingNodeTypes: MissingNodeType[] }>() -const aboutPanelStore = useAboutPanelStore() - // Get missing node packs from workflow with loading and error states const { missingNodePacks, isLoading, error, missingCoreNodes } = useMissingNodes() -// Determines if ComfyUI-Manager is installed by checking for its badge in the about panel -// This allows us to conditionally show the Manager button only when the extension is available -// TODO: Remove this check when Manager functionality is fully migrated into core -const isManagerInstalled = computed(() => { - return aboutPanelStore.badges.some( - (badge) => - badge.label.includes('ComfyUI-Manager') || - badge.url.includes('ComfyUI-Manager') - ) -}) +const isLegacyManager = ref(false) const uniqueNodes = computed(() => { const seenTypes = new Set() @@ -103,6 +90,13 @@ const openManager = () => { initialTab: ManagerTab.Missing }) } + +onMounted(async () => { + const isLegacyResponse = await useComfyManagerService().isLegacyManagerUI() + if (isLegacyResponse?.is_legacy_manager_ui) { + isLegacyManager.value = true + } +})