mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-20 20:39:30 +00:00
*PR Created by the Glary-Bot Agent*
---
## Summary
Adds a comfy.org page that lists every custom-node pack supported on
Comfy Cloud, with per-pack detail subpages. Data is fetched at build
time from `cloud.comfy.org/api/object_info` (gated by
`WEBSITE_CLOUD_API_KEY`), sanitized of user content, joined with public
registry metadata from `api.comfy.org/nodes`, and falls back to a
committed snapshot — mirroring the existing Ashby careers integration
pattern.
- Index: `/cloud/supported-nodes` (en) and
`/zh-CN/cloud/supported-nodes` (zh-CN)
- Detail: `/cloud/supported-nodes/[pack]` and
`/zh-CN/cloud/supported-nodes/[pack]`, generated via `getStaticPaths()`
from the same fetcher as the index so the two routes can never diverge.
## What's new
**Shared package (extracted)**
- `@comfyorg/object-info-parser` — Zod schemas (`zComfyNodeDef`,
`validateComfyNodeDef`), node-source classifier (`getNodeSource`,
`isCustomNode`, `CORE_NODE_MODULES`), and helpers (`groupNodesByPack`,
`sanitizeUserContent`). `src/schemas/nodeDefSchema.ts` and
`src/types/nodeSource.ts` become 1-line re-export shims; existing
imports keep compiling.
**Build-time pipeline**
- `apps/website/src/utils/cloudNodes.ts` — Ashby-style fetcher:
retry/backoff `[1s, 2s, 4s]`, 10 s timeout via AbortController, Zod
envelope + per-node validation, snapshot fallback, memoized via
module-level `inflight` promise.
- `apps/website/src/utils/cloudNodes.registry.ts` — Public registry
enrichment (no auth, batches of 50, single retry, soft-fail).
- `apps/website/src/utils/cloudNodes.ci.ts` — GitHub Actions annotations
+ step summary mirroring the Ashby reporter.
- `apps/website/src/utils/cloudNodes.build.ts` — Single
`loadPacksForBuild()` consumed by both index and detail pages so they
share one source of truth.
- `apps/website/scripts/refresh-cloud-nodes-snapshot.ts` — atomic-rename
refresh CLI that walks pack/node string fields with a user-content
extension regex *before* renaming the snapshot into place.
- Mandatory user-content sanitization strips uploaded filenames from
combo lists (`LoadImage`, `LoadImageMask`, `LoadImageOutput`,
`LoadVideo`, `LoadAudio` zeroed; any combo value matching
`/\.(png|jpe?g|webp|gif|mp4|mov|webm|wav|mp3|flac|ogg|safetensors|ckpt|pt)$/i`
filtered).
**Page + components**
- `apps/website/src/pages/cloud/supported-nodes.astro` (en) + zh-CN
twin.
- `apps/website/src/pages/cloud/supported-nodes/[pack].astro` detail
(en) + zh-CN twin, async `getStaticPaths` driven by
`loadPacksForBuild()`.
-
`apps/website/src/components/cloud-nodes/{HeroSection,PackGridSection,PackCard,PackBanner,NodeList,PackDetail}.vue`
— Vue 3.5 destructured props, `cn()` from `@comfyorg/tailwind-utils`,
design-system tokens only, no PrimeVue.
- Pack card name links to its detail page; banner uses the shared
`fallback-gradient-avatar.svg` asset (copied into
`apps/website/public/assets/images/`) when `banner_url` and `icon` are
missing.
- 25 new `cloudNodes.*` i18n keys in `en` + `zh-CN`.
**Tests**
- 33 unit tests in `@comfyorg/object-info-parser` (schemas, classifier,
sanitizer, grouping).
- 19 new website unit tests covering fetcher (10), CI reporter (6),
registry enrichment (3) — Ashby patterns mirrored.
- E2E: index smoke + search + banner + detail click-through + direct
visit + zh-CN parity.
## Required maintainer follow-up
GitHub Apps cannot push `.github/workflows/*` changes (push was rejected
with `refusing to allow a GitHub App to create or update workflow …
without workflows permission`), so the workflow edits prepared in this
branch were reverted in commit `9be2abce8`. The intended diffs are
documented as copy-paste-ready snippets in `apps/website/README.md`
under the new "Cloud nodes integration → CI wiring" section.
A maintainer must:
1. Provision `WEBSITE_CLOUD_API_KEY` in the repo secrets and the Vercel
project env.
2. Apply the `ci-website-build.yaml` and
`ci-vercel-website-preview.yaml` diffs documented in the README directly
to `main` (or as a follow-up commit on this branch with a maintainer
account).
The committed snapshot lets builds succeed without the secret while the
maintainer step is pending — pages render from
`apps/website/src/data/cloud-nodes.snapshot.json`.
## Self-review (Oracle)
Two warnings caught and fixed in commits `deba5ab02` and `99dfc3381`:
- Index/detail pages now share a single source of truth
(`loadPacksForBuild`), so a fresh fetch can't expose packs whose detail
routes weren't generated.
- Refresh script validates parsed snapshot fields *before* the atomic
rename, instead of regex-scanning the serialized JSON after the file is
already in place.
## Quality gates (local)
```
pnpm --filter @comfyorg/object-info-parser test → 33 passed
pnpm --filter @comfyorg/website test:unit → 42 passed
pnpm --filter @comfyorg/website typecheck → 0 errors
pnpm --filter @comfyorg/website build → 47 pages built (incl. 6 cloud-nodes routes)
pnpm lint → 0 errors (1 pre-existing warning in unrelated test file)
pnpm knip → 0 errors (1 pre-existing tag hint in unrelated file)
```
E2E (`pnpm --filter @comfyorg/website test:e2e`) is intended to be run
by the Vercel/CI pipelines.
## Manual verification
Built `dist/`, served locally on port 4321, drove with Playwright:
- `/cloud/supported-nodes` renders both pack cards, search input, sort
dropdown
- `/cloud/supported-nodes/comfyui-impact-pack` renders the metadata grid
(publisher, downloads, stars, version, license, last updated) and 3
categorized node sections with 5 nodes total
- `/zh-CN/cloud/supported-nodes` localizes hero (`Comfy Cloud 上的自定义节点`),
label (`云端节点目录`), search placeholder (`搜索节点包或节点名称`), sort
- `/zh-CN/cloud/supported-nodes/comfyui-controlnet-aux` localizes every
metadata label (`查看仓库`, `发布者`, `下载量`, `GitHub 星标`, `最新版本`, `许可证`,
`最后更新`) and renders dates with `Intl.DateTimeFormat('zh-CN')`
(`2026年4月27日`)
- Search input narrows pack count from 2 to 1 when typing `impact`
(verified via DOM count)
Banners render the shared `fallback-gradient-avatar.svg` when the
snapshot's image URL doesn't resolve — expected in the local sandbox.
## Preview URL (after CI completes)
`https://comfy-website-preview-pr-{N}.vercel.app/cloud/supported-nodes`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11903-feat-cloud-nodes-catalog-at-cloud-supported-nodes-3566d73d36508194afdec5f389897585)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
230 lines
8.7 KiB
JSON
230 lines
8.7 KiB
JSON
{
|
|
"name": "@comfyorg/comfyui-frontend",
|
|
"version": "1.45.5",
|
|
"private": true,
|
|
"description": "Official front-end implementation of ComfyUI",
|
|
"homepage": "https://comfy.org",
|
|
"license": "GPL-3.0-only",
|
|
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
|
|
"type": "module",
|
|
"scripts": {
|
|
"build:cloud": "cross-env DISTRIBUTION=cloud NODE_OPTIONS='--max-old-space-size=8192' nx build",
|
|
"build:desktop": "nx build @comfyorg/desktop-ui",
|
|
"build-storybook": "storybook build",
|
|
"build:types": "cross-env NODE_OPTIONS='--max-old-space-size=8192' nx build --config vite.types.config.mts && node scripts/prepare-types.js",
|
|
"build:analyze": "cross-env ANALYZE_BUNDLE=true pnpm build",
|
|
"build": "cross-env NODE_OPTIONS='--max-old-space-size=8192' pnpm typecheck && nx build",
|
|
"size:collect": "node scripts/size-collect.js",
|
|
"size:report": "node scripts/size-report.js",
|
|
"collect-i18n": "pnpm exec playwright test --config=playwright.i18n.config.ts",
|
|
"dev:cloud": "cross-env DEV_SERVER_COMFYUI_URL='https://testcloud.comfy.org/' nx serve",
|
|
"dev:desktop": "nx dev @comfyorg/desktop-ui",
|
|
"dev:electron": "cross-env DISTRIBUTION=desktop nx serve --config vite.electron.config.mts",
|
|
"dev:no-vue": "cross-env DISABLE_VUE_PLUGINS=true nx serve",
|
|
"dev": "nx serve",
|
|
"devtools:pycheck": "python3 -m compileall -q tools/devtools",
|
|
"format:check": "oxfmt --check",
|
|
"format": "oxfmt --write",
|
|
"json-schema": "tsx scripts/generate-json-schema.ts",
|
|
"knip:no-cache": "knip",
|
|
"knip": "knip --cache",
|
|
"lint:fix:no-cache": "oxlint src browser_tests --type-aware --fix && eslint src --fix",
|
|
"lint:fix": "oxlint src browser_tests --type-aware --fix && eslint src --cache --fix",
|
|
"lint:no-cache": "pnpm exec stylelint '{apps,packages,src}/**/*.{css,vue}' && oxlint src browser_tests --type-aware && eslint src",
|
|
"lint:unstaged:fix": "git diff --name-only HEAD | grep -E '\\.(js|ts|vue|mts)$' | xargs -r eslint --cache --fix",
|
|
"lint:unstaged": "git diff --name-only HEAD | grep -E '\\.(js|ts|vue|mts)$' | xargs -r eslint --cache",
|
|
"lint": "pnpm stylelint && oxlint src browser_tests --type-aware && eslint src --cache",
|
|
"lint:desktop": "nx run @comfyorg/desktop-ui:lint",
|
|
"locale": "lobe-i18n locale",
|
|
"oxlint": "oxlint src browser_tests --type-aware",
|
|
"prepare": "husky || true && git config blame.ignoreRevsFile .git-blame-ignore-revs || true",
|
|
"preview": "nx preview",
|
|
"storybook": "nx storybook",
|
|
"storybook:desktop": "nx run @comfyorg/desktop-ui:storybook",
|
|
"stylelint:fix": "stylelint --cache --fix '{apps,packages,src}/**/*.{css,vue}'",
|
|
"stylelint": "stylelint --cache '{apps,packages,src}/**/*.{css,vue}'",
|
|
"test:browser": "pnpm exec nx e2e",
|
|
"test:browser:coverage": "cross-env COLLECT_COVERAGE=true pnpm test:browser",
|
|
"test:browser:local": "cross-env PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:5173 pnpm test:browser",
|
|
"test:coverage": "vitest run --coverage",
|
|
"test:unit": "nx run test",
|
|
"typecheck": "vue-tsc --noEmit",
|
|
"typecheck:browser": "vue-tsc --project browser_tests/tsconfig.json",
|
|
"typecheck:desktop": "nx run @comfyorg/desktop-ui:typecheck",
|
|
"typecheck:website": "nx run @comfyorg/website:typecheck",
|
|
"zipdist": "node scripts/zipdist.js",
|
|
"clean": "nx reset"
|
|
},
|
|
"dependencies": {
|
|
"@alloc/quick-lru": "catalog:",
|
|
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
|
|
"@comfyorg/comfyui-electron-types": "catalog:",
|
|
"@comfyorg/design-system": "workspace:*",
|
|
"@comfyorg/object-info-parser": "workspace:*",
|
|
"@comfyorg/registry-types": "workspace:*",
|
|
"@comfyorg/shared-frontend-utils": "workspace:*",
|
|
"@comfyorg/tailwind-utils": "workspace:*",
|
|
"@formkit/auto-animate": "catalog:",
|
|
"@iconify/json": "catalog:",
|
|
"@primeuix/forms": "catalog:",
|
|
"@primeuix/styled": "catalog:",
|
|
"@primeuix/utils": "catalog:",
|
|
"@primevue/core": "catalog:",
|
|
"@primevue/forms": "catalog:",
|
|
"@primevue/icons": "catalog:",
|
|
"@primevue/themes": "catalog:",
|
|
"@sentry/vue": "catalog:",
|
|
"@sparkjsdev/spark": "catalog:",
|
|
"@tanstack/vue-virtual": "catalog:",
|
|
"@tiptap/core": "catalog:",
|
|
"@tiptap/extension-link": "catalog:",
|
|
"@tiptap/extension-table": "catalog:",
|
|
"@tiptap/extension-table-cell": "catalog:",
|
|
"@tiptap/extension-table-header": "catalog:",
|
|
"@tiptap/extension-table-row": "catalog:",
|
|
"@tiptap/pm": "catalog:",
|
|
"@tiptap/starter-kit": "catalog:",
|
|
"@vee-validate/zod": "catalog:",
|
|
"@vueuse/core": "catalog:",
|
|
"@vueuse/integrations": "catalog:",
|
|
"@vueuse/router": "^14.2.0",
|
|
"@xterm/addon-fit": "^0.10.0",
|
|
"@xterm/addon-serialize": "^0.13.0",
|
|
"@xterm/xterm": "^5.5.0",
|
|
"algoliasearch": "catalog:",
|
|
"axios": "catalog:",
|
|
"chart.js": "^4.5.0",
|
|
"cva": "catalog:",
|
|
"dompurify": "^3.2.5",
|
|
"dotenv": "catalog:",
|
|
"es-toolkit": "^1.39.9",
|
|
"extendable-media-recorder": "^9.2.27",
|
|
"extendable-media-recorder-wav-encoder": "^7.0.129",
|
|
"firebase": "catalog:",
|
|
"fuse.js": "^7.0.0",
|
|
"glob": "catalog:",
|
|
"jsonata": "catalog:",
|
|
"loglevel": "^1.9.2",
|
|
"marked": "^15.0.11",
|
|
"pinia": "catalog:",
|
|
"posthog-js": "catalog:",
|
|
"primeicons": "catalog:",
|
|
"primevue": "catalog:",
|
|
"reka-ui": "catalog:",
|
|
"semver": "^7.7.2",
|
|
"three": "^0.170.0",
|
|
"tiptap-markdown": "^0.8.10",
|
|
"typegpu": "catalog:",
|
|
"vee-validate": "catalog:",
|
|
"vue": "catalog:",
|
|
"vue-i18n": "catalog:",
|
|
"vue-router": "catalog:",
|
|
"vuefire": "catalog:",
|
|
"wwobjloader2": "catalog:",
|
|
"yjs": "catalog:",
|
|
"zod": "catalog:",
|
|
"zod-validation-error": "catalog:"
|
|
},
|
|
"devDependencies": {
|
|
"@comfyorg/ingest-types": "workspace:*",
|
|
"@eslint/js": "catalog:",
|
|
"@intlify/eslint-plugin-vue-i18n": "catalog:",
|
|
"@lobehub/i18n-cli": "catalog:",
|
|
"@nx/eslint": "catalog:",
|
|
"@nx/playwright": "catalog:",
|
|
"@nx/storybook": "catalog:",
|
|
"@nx/vite": "catalog:",
|
|
"@pinia/testing": "catalog:",
|
|
"@playwright/test": "catalog:",
|
|
"@sentry/vite-plugin": "catalog:",
|
|
"@storybook/addon-docs": "catalog:",
|
|
"@storybook/addon-mcp": "catalog:",
|
|
"@storybook/vue3": "catalog:",
|
|
"@storybook/vue3-vite": "catalog:",
|
|
"@tailwindcss/vite": "catalog:",
|
|
"@testing-library/jest-dom": "catalog:",
|
|
"@testing-library/user-event": "catalog:",
|
|
"@testing-library/vue": "catalog:",
|
|
"@total-typescript/shoehorn": "catalog:",
|
|
"@types/fs-extra": "catalog:",
|
|
"@types/jsdom": "catalog:",
|
|
"@types/node": "catalog:",
|
|
"@types/semver": "catalog:",
|
|
"@types/three": "catalog:",
|
|
"@vitejs/plugin-vue": "catalog:",
|
|
"@vitest/coverage-v8": "catalog:",
|
|
"@vitest/ui": "catalog:",
|
|
"@webgpu/types": "catalog:",
|
|
"cross-env": "catalog:",
|
|
"eslint": "catalog:",
|
|
"eslint-config-prettier": "catalog:",
|
|
"eslint-import-resolver-typescript": "catalog:",
|
|
"eslint-plugin-better-tailwindcss": "catalog:",
|
|
"eslint-plugin-import-x": "catalog:",
|
|
"eslint-plugin-oxlint": "catalog:",
|
|
"eslint-plugin-playwright": "catalog:",
|
|
"eslint-plugin-storybook": "catalog:",
|
|
"eslint-plugin-testing-library": "catalog:",
|
|
"eslint-plugin-unused-imports": "catalog:",
|
|
"eslint-plugin-vue": "catalog:",
|
|
"fast-check": "catalog:",
|
|
"fs-extra": "^11.2.0",
|
|
"globals": "catalog:",
|
|
"happy-dom": "catalog:",
|
|
"husky": "catalog:",
|
|
"jiti": "catalog:",
|
|
"jsdom": "catalog:",
|
|
"knip": "catalog:",
|
|
"lint-staged": "catalog:",
|
|
"markdown-table": "catalog:",
|
|
"mixpanel-browser": "catalog:",
|
|
"monocart-coverage-reports": "catalog:",
|
|
"nx": "catalog:",
|
|
"oxfmt": "catalog:",
|
|
"oxlint": "catalog:",
|
|
"oxlint-tsgolint": "catalog:",
|
|
"picocolors": "catalog:",
|
|
"postcss-html": "catalog:",
|
|
"pretty-bytes": "catalog:",
|
|
"rollup-plugin-visualizer": "catalog:",
|
|
"storybook": "catalog:",
|
|
"stylelint": "catalog:",
|
|
"tailwindcss": "catalog:",
|
|
"tsx": "catalog:",
|
|
"typescript": "catalog:",
|
|
"typescript-eslint": "catalog:",
|
|
"unplugin-icons": "catalog:",
|
|
"unplugin-typegpu": "catalog:",
|
|
"unplugin-vue-components": "catalog:",
|
|
"uuid": "^11.1.0",
|
|
"vite": "catalog:",
|
|
"vite-plugin-dts": "catalog:",
|
|
"vite-plugin-html": "catalog:",
|
|
"vite-plugin-vue-devtools": "catalog:",
|
|
"vitest": "catalog:",
|
|
"vue-component-type-helpers": "catalog:",
|
|
"vue-eslint-parser": "catalog:",
|
|
"vue-tsc": "catalog:",
|
|
"zip-dir": "^2.0.0",
|
|
"zod-to-json-schema": "catalog:"
|
|
},
|
|
"engines": {
|
|
"node": "24.x",
|
|
"pnpm": ">=10"
|
|
},
|
|
"packageManager": "pnpm@10.33.0",
|
|
"pnpm": {
|
|
"overrides": {
|
|
"vite": "catalog:"
|
|
},
|
|
"ignoredBuiltDependencies": [
|
|
"@firebase/util",
|
|
"core-js",
|
|
"protobufjs",
|
|
"sharp",
|
|
"unrs-resolver",
|
|
"vue-demi"
|
|
]
|
|
}
|
|
}
|