Compare commits
234 Commits
fix/typesc
...
drjkl/remo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78cbfd2a7a | ||
|
|
9a3545b42b | ||
|
|
493fe667b7 | ||
|
|
7636b9eaa1 | ||
|
|
b661412c5b | ||
|
|
ba4c3525f4 | ||
|
|
58024fb4f4 | ||
|
|
ac72999e26 | ||
|
|
e27b5311d1 | ||
|
|
35ddb19a68 | ||
|
|
aed342572c | ||
|
|
f16f5a0bc6 | ||
|
|
4fc7d6352f | ||
|
|
c47cedcec2 | ||
|
|
789617415d | ||
|
|
26cf45ed36 | ||
|
|
af10082134 | ||
|
|
28a779d41a | ||
|
|
ed5d258ab6 | ||
|
|
5d0aee59a6 | ||
|
|
d852bab617 | ||
|
|
dd93ac43d9 | ||
|
|
e0b1a6d212 | ||
|
|
bbd8d67f5f | ||
|
|
5c707a1b93 | ||
|
|
3077f4c4c4 | ||
|
|
9734466a13 | ||
|
|
5d62e01c11 | ||
|
|
8135b67f34 | ||
|
|
1215cb4670 | ||
|
|
a30705161d | ||
|
|
02b3b8ef5b | ||
|
|
aa212bea90 | ||
|
|
a2c7db9dc2 | ||
|
|
840f7f04fa | ||
|
|
042c2caa88 | ||
|
|
cd8b5b5d50 | ||
|
|
a25d89881b | ||
|
|
5b866b74b8 | ||
|
|
e8b9f8f4bc | ||
|
|
b0d2b44dad | ||
|
|
976b1a6bbd | ||
|
|
856eb446a5 | ||
|
|
992ba9d585 | ||
|
|
4b9d448b6c | ||
|
|
0ac1074a25 | ||
|
|
45f5ec15c2 | ||
|
|
a519e681dd | ||
|
|
fdb15e1296 | ||
|
|
d444b2c285 | ||
|
|
46ad1318e5 | ||
|
|
da332ed75d | ||
|
|
75d31f9d57 | ||
|
|
edcbcdfa84 | ||
|
|
1f4a52ca3e | ||
|
|
f2e925de62 | ||
|
|
5f7c7ca949 | ||
|
|
62441fa0f9 | ||
|
|
c96f719f91 | ||
|
|
9f19d8fb4b | ||
|
|
d954336973 | ||
|
|
49f373c46f | ||
|
|
9678a87846 | ||
|
|
3a9365af13 | ||
|
|
3ee0d394ca | ||
|
|
6a70152220 | ||
|
|
5c1e00ff8e | ||
|
|
78d585eca0 | ||
|
|
a6600aa109 | ||
|
|
c033e9e4d7 | ||
|
|
ac93a6ba3f | ||
|
|
961af8731e | ||
|
|
703de3e669 | ||
|
|
415ebfd67b | ||
|
|
97542efc9b | ||
|
|
13ce23399c | ||
|
|
a0c06bd723 | ||
|
|
3fc17ebdac | ||
|
|
0db2a2c03e | ||
|
|
b96bf3871c | ||
|
|
bc4549244e | ||
|
|
f3e68804e8 | ||
|
|
1749cfa678 | ||
|
|
0fea54c542 | ||
|
|
cd7666e3bc | ||
|
|
8d1261133a | ||
|
|
0919856a05 | ||
|
|
b0f81b2245 | ||
|
|
6449d26cee | ||
|
|
80cabc61ee | ||
|
|
76dd935b35 | ||
|
|
f7f3240100 | ||
|
|
e4022c455a | ||
|
|
c6e50d8f1b | ||
|
|
c004a2b8bd | ||
|
|
d0aee031e9 | ||
|
|
cec1de0147 | ||
|
|
b4976c1ddc | ||
|
|
1611c7a224 | ||
|
|
d01081dab4 | ||
|
|
e5d4d07d32 | ||
|
|
f086377307 | ||
|
|
687b9e659c | ||
|
|
da0d51311b | ||
|
|
e314d9cbd9 | ||
|
|
95baf8d2f1 | ||
|
|
f951e07cea | ||
|
|
023e466dba | ||
|
|
abd6823744 | ||
|
|
c4c0e52e64 | ||
|
|
295332dc46 | ||
|
|
5c498348b8 | ||
|
|
8133bd4b7b | ||
|
|
fd12591756 | ||
|
|
b3c939ff15 | ||
|
|
0801778f60 | ||
|
|
8ffe63f54e | ||
|
|
893409dfc8 | ||
|
|
df2fda6077 | ||
|
|
4f5bbe0605 | ||
|
|
a975e50f1b | ||
|
|
a17c74fa0c | ||
|
|
5e625a5002 | ||
|
|
002fac0232 | ||
|
|
7e115543fa | ||
|
|
80d75bb164 | ||
|
|
d59885839a | ||
|
|
cbb0f765b8 | ||
|
|
726a2fbbc9 | ||
|
|
553b5aa02b | ||
|
|
2ff0d951ed | ||
|
|
1f88925144 | ||
|
|
250433a91a | ||
|
|
eb664f47af | ||
|
|
bc85d4e87b | ||
|
|
7585444ce6 | ||
|
|
a886798a10 | ||
|
|
37975e4eac | ||
|
|
a41b8a6d4f | ||
|
|
b264685052 | ||
|
|
78d0ea6fa5 | ||
|
|
ea4e57b602 | ||
|
|
4789d86fe8 | ||
|
|
09e7d1040e | ||
|
|
dfa1cbba4f | ||
|
|
08220d50d9 | ||
|
|
045232a99b | ||
|
|
fb07941700 | ||
|
|
6866e1277a | ||
|
|
ff5d0923ca | ||
|
|
6b59f839e0 | ||
|
|
6a01b08ebf | ||
|
|
ede43c5e5c | ||
|
|
0483630f82 | ||
|
|
15cffe9d9e | ||
|
|
f5b949762d | ||
|
|
6786d8e4fb | ||
|
|
71ca28a46f | ||
|
|
4ff18fd7f0 | ||
|
|
45a46be513 | ||
|
|
631746939a | ||
|
|
4f8e820c51 | ||
|
|
b8d8193a38 | ||
|
|
2b57291756 | ||
|
|
62897c669b | ||
|
|
9918914b9d | ||
|
|
e601bcb300 | ||
|
|
853038859b | ||
|
|
186e065528 | ||
|
|
237e807fc9 | ||
|
|
27ab355f9c | ||
|
|
4c8c4a1ad4 | ||
|
|
e3bb29ceb8 | ||
|
|
ca312fd1ea | ||
|
|
bbff9c8217 | ||
|
|
6349ceee6c | ||
|
|
8c6ee026c0 | ||
|
|
668500cfa5 | ||
|
|
07fbe7267e | ||
|
|
fe09f88ea3 | ||
|
|
96a663704f | ||
|
|
11cb525545 | ||
|
|
4eb13ca17c | ||
|
|
6017fc43a6 | ||
|
|
6e2d86520b | ||
|
|
6e2a3a0d07 | ||
|
|
c7325c4da9 | ||
|
|
d146a7896a | ||
|
|
6b166a9d2f | ||
|
|
c145fd9df1 | ||
|
|
c04af09956 | ||
|
|
ac107b45ea | ||
|
|
f6a115e182 | ||
|
|
aa7a99f4e3 | ||
|
|
8804755ffa | ||
|
|
f28ebcac19 | ||
|
|
bfef8d7d4b | ||
|
|
90bf8dc74a | ||
|
|
6f878abea4 | ||
|
|
b1917c6469 | ||
|
|
bf054113f8 | ||
|
|
4604bbd669 | ||
|
|
77ee20597f | ||
|
|
8ae50d140d | ||
|
|
63f194b394 | ||
|
|
2b8abca6d8 | ||
|
|
48d01745cd | ||
|
|
90b1b47dd0 | ||
|
|
0e01ca0a98 | ||
|
|
cf093d02a7 | ||
|
|
1845708ddb | ||
|
|
c588f2f457 | ||
|
|
29ad47d20f | ||
|
|
9aeeab9a9f | ||
|
|
6645b3917f | ||
|
|
4ec6223189 | ||
|
|
dfcbbec2b9 | ||
|
|
c051c3a507 | ||
|
|
54cbf42a84 | ||
|
|
ef7575b8d6 | ||
|
|
68845ce33a | ||
|
|
46f4ce3890 | ||
|
|
5df037dfc4 | ||
|
|
3bc25b7aeb | ||
|
|
08fe2829d4 | ||
|
|
e70ddea684 | ||
|
|
038f86fe84 | ||
|
|
cb0dab6cdc | ||
|
|
568be0c44c | ||
|
|
6ea021d595 | ||
|
|
7245213ed6 | ||
|
|
b72e22f6be | ||
|
|
5f045b335d | ||
|
|
44e470488d |
@@ -67,9 +67,9 @@ This is critical for better file inspection:
|
||||
|
||||
Use git locally for much faster analysis:
|
||||
|
||||
1. Get list of changed files: `git diff --name-only "origin/$BASE_BRANCH" > changed_files.txt`
|
||||
2. Get the full diff: `git diff "origin/$BASE_BRANCH" > pr_diff.txt`
|
||||
3. Get detailed file changes with status: `git diff --name-status "origin/$BASE_BRANCH" > file_changes.txt`
|
||||
1. Get list of changed files: `git diff --name-only "$BASE_SHA" > changed_files.txt`
|
||||
2. Get the full diff: `git diff "$BASE_SHA" > pr_diff.txt`
|
||||
3. Get detailed file changes with status: `git diff --name-status "$BASE_SHA" > file_changes.txt`
|
||||
|
||||
### Step 1.5: Create Analysis Cache
|
||||
|
||||
|
||||
@@ -33,4 +33,3 @@ DISABLE_VUE_PLUGINS=false
|
||||
# Algolia credentials required for developing with the new custom node manager.
|
||||
ALGOLIA_APP_ID=4E0RO38HS8
|
||||
ALGOLIA_API_KEY=684d998c36b67a9a9fce8fc2d8860579
|
||||
|
||||
|
||||
@@ -7,3 +7,21 @@ c53f197de2a3e0fa66b16dedc65c131235c1c4b6
|
||||
|
||||
# Reorganize renderer components into domain-driven folder structure
|
||||
c8a83a9caede7bdb5f8598c5492b07d08c339d49
|
||||
|
||||
# Domain-driven design (DDD) refactors - September 2025
|
||||
# These commits reorganized the codebase into domain-driven architecture
|
||||
|
||||
# [refactor] Improve renderer domain organization (#5552)
|
||||
6349ceee6c0a57fc7992e85635def9b6e22eaeb2
|
||||
|
||||
# [refactor] Improve settings domain organization (#5550)
|
||||
4c8c4a1ad4f53354f700a33ea1b95262aeda2719
|
||||
|
||||
# [refactor] Improve workflow domain organization (#5584)
|
||||
ca312fd1eab540cc4ddc0e3d244d38b3858574f0
|
||||
|
||||
# [refactor] Move thumbnail functionality to renderer/core domain (#5586)
|
||||
e3bb29ceb8174b8bbca9e48ec7d42cd540f40efa
|
||||
|
||||
# [refactor] Improve updates/notifications domain organization (#5590)
|
||||
27ab355f9c73415dc39f4d3f512b02308f847801
|
||||
|
||||
4
.gitattributes
vendored
@@ -12,5 +12,5 @@
|
||||
*.yaml text eol=lf
|
||||
|
||||
# Generated files
|
||||
src/types/comfyRegistryTypes.ts linguist-generated=true
|
||||
src/types/generatedManagerTypes.ts linguist-generated=true
|
||||
packages/registry-types/src/comfyRegistryTypes.ts linguist-generated=true
|
||||
src/workbench/extensions/manager/types/generatedManagerTypes.ts linguist-generated=true
|
||||
|
||||
67
.github/actions/setup-frontend/action.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
name: Setup Frontend
|
||||
description: 'Setup ComfyUI frontend development environment'
|
||||
inputs:
|
||||
extra_server_params:
|
||||
description: 'Additional parameters to pass to ComfyUI server'
|
||||
required: false
|
||||
default: ''
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Checkout ComfyUI
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'comfyanonymous/ComfyUI'
|
||||
path: 'ComfyUI'
|
||||
|
||||
- name: Checkout ComfyUI_frontend
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'Comfy-Org/ComfyUI_frontend'
|
||||
path: 'ComfyUI_frontend'
|
||||
|
||||
- name: Copy ComfyUI_devtools from frontend repo
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
|
||||
cp -r ComfyUI_frontend/tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: 'ComfyUI_frontend/pnpm-lock.yaml'
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install Python requirements
|
||||
shell: bash
|
||||
working-directory: ComfyUI
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
pip install -r requirements.txt
|
||||
pip install wait-for-it
|
||||
|
||||
- name: Build & Install ComfyUI_frontend
|
||||
shell: bash
|
||||
working-directory: ComfyUI_frontend
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
|
||||
- name: Start ComfyUI server
|
||||
shell: bash
|
||||
working-directory: ComfyUI
|
||||
run: |
|
||||
python main.py --cpu --multi-user --front-end-root ../ComfyUI_frontend/dist ${{ inputs.extra_server_params }} &
|
||||
wait-for-it --service 127.0.0.1:8188 -t 600
|
||||
112
.github/workflows/backport.yaml
vendored
@@ -4,10 +4,25 @@ on:
|
||||
pull_request_target:
|
||||
types: [closed, labeled]
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to backport'
|
||||
required: true
|
||||
type: string
|
||||
force_rerun:
|
||||
description: 'Force rerun even if backports exist'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
backport:
|
||||
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'needs-backport')
|
||||
if: >
|
||||
(github.event_name == 'pull_request_target' &&
|
||||
github.event.pull_request.merged == true &&
|
||||
contains(github.event.pull_request.labels.*.name, 'needs-backport')) ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -15,8 +30,37 @@ jobs:
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Validate inputs for manual triggers
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
# Validate PR number format
|
||||
if ! [[ "${{ inputs.pr_number }}" =~ ^[0-9]+$ ]]; then
|
||||
echo "::error::Invalid PR number format. Must be a positive integer."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate PR exists and is merged
|
||||
if ! gh pr view "${{ inputs.pr_number }}" --json merged >/dev/null 2>&1; then
|
||||
echo "::error::PR #${{ inputs.pr_number }} not found or inaccessible."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MERGED=$(gh pr view "${{ inputs.pr_number }}" --json merged --jq '.merged')
|
||||
if [ "$MERGED" != "true" ]; then
|
||||
echo "::error::PR #${{ inputs.pr_number }} is not merged. Only merged PRs can be backported."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate PR has needs-backport label
|
||||
if ! gh pr view "${{ inputs.pr_number }}" --json labels --jq '.labels[].name' | grep -q "needs-backport"; then
|
||||
echo "::error::PR #${{ inputs.pr_number }} does not have 'needs-backport' label."
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -29,7 +73,7 @@ jobs:
|
||||
id: check-existing
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
||||
run: |
|
||||
# Check for existing backport PRs for this PR number
|
||||
EXISTING_BACKPORTS=$(gh pr list --state all --search "backport-${PR_NUMBER}-to" --json title,headRefName,baseRefName | jq -r '.[].headRefName')
|
||||
@@ -39,6 +83,13 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# For manual triggers with force_rerun, proceed anyway
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_rerun }}" = "true" ]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "::warning::Force rerun requested - existing backports will be updated"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Found existing backport PRs:"
|
||||
echo "$EXISTING_BACKPORTS"
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
@@ -50,8 +101,17 @@ jobs:
|
||||
run: |
|
||||
# Extract version labels (e.g., "1.24", "1.22")
|
||||
VERSIONS=""
|
||||
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
|
||||
for label in $(echo "$LABELS" | jq -r '.[].name'); do
|
||||
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
# For manual triggers, get labels from the PR
|
||||
LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name')
|
||||
else
|
||||
# For automatic triggers, extract from PR event
|
||||
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
|
||||
LABELS=$(echo "$LABELS" | jq -r '.[].name')
|
||||
fi
|
||||
|
||||
for label in $LABELS; do
|
||||
# Match version labels like "1.24" (major.minor only)
|
||||
if [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||
# Validate the branch exists before adding to list
|
||||
@@ -75,12 +135,20 @@ jobs:
|
||||
if: steps.check-existing.outputs.skip != 'true'
|
||||
id: backport
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
MERGE_COMMIT: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
||||
run: |
|
||||
FAILED=""
|
||||
SUCCESS=""
|
||||
|
||||
# Get PR data for manual triggers
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json title,mergeCommit)
|
||||
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
|
||||
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
|
||||
else
|
||||
PR_TITLE="${{ github.event.pull_request.title }}"
|
||||
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
||||
fi
|
||||
|
||||
for version in ${{ steps.versions.outputs.versions }}; do
|
||||
echo "::group::Backporting to core/${version}"
|
||||
@@ -133,11 +201,18 @@ jobs:
|
||||
if: steps.check-existing.outputs.skip != 'true' && steps.backport.outputs.success
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
||||
run: |
|
||||
PR_TITLE="${{ github.event.pull_request.title }}"
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
||||
|
||||
# Get PR data for manual triggers
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json title,author)
|
||||
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
|
||||
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
|
||||
else
|
||||
PR_TITLE="${{ github.event.pull_request.title }}"
|
||||
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
||||
fi
|
||||
|
||||
for backport in ${{ steps.backport.outputs.success }}; do
|
||||
IFS=':' read -r version branch <<< "${backport}"
|
||||
|
||||
@@ -166,9 +241,16 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
||||
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json author,mergeCommit)
|
||||
PR_NUMBER="${{ inputs.pr_number }}"
|
||||
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
|
||||
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
|
||||
else
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
||||
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
||||
fi
|
||||
|
||||
for failure in ${{ steps.backport.outputs.failed }}; do
|
||||
IFS=':' read -r version reason conflicts <<< "${failure}"
|
||||
|
||||
186
.github/workflows/chromatic.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: 'Chromatic'
|
||||
name: Storybook and Chromatic CI
|
||||
|
||||
# - [Automate Chromatic with GitHub Actions • Chromatic docs]( https://www.chromatic.com/docs/github-actions/ )
|
||||
|
||||
@@ -8,13 +8,100 @@ on:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
chromatic-deployment:
|
||||
# Post starting comment for non-forked PRs
|
||||
comment-on-pr-start:
|
||||
runs-on: ubuntu-latest
|
||||
# Only run for PRs from version-bump-* branches or manual triggers
|
||||
if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'version-bump-')
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Post starting comment
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
|
||||
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
|
||||
"${{ github.event.pull_request.number }}" \
|
||||
"${{ github.head_ref }}" \
|
||||
"starting" \
|
||||
"$(date -u '+%m/%d/%Y, %I:%M:%S %p')"
|
||||
|
||||
# Build Storybook for all PRs (free Cloudflare deployment)
|
||||
storybook-build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
outputs:
|
||||
conclusion: ${{ steps.job-status.outputs.conclusion }}
|
||||
workflow-url: ${{ steps.workflow-url.outputs.url }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
storybook-static
|
||||
tsconfig.tsbuildinfo
|
||||
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
|
||||
restore-keys: |
|
||||
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
storybook-cache-${{ runner.os }}-
|
||||
storybook-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build Storybook
|
||||
run: pnpm build-storybook
|
||||
|
||||
- name: Set job status
|
||||
id: job-status
|
||||
if: always()
|
||||
run: |
|
||||
echo "conclusion=${{ job.status }}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get workflow URL
|
||||
id: workflow-url
|
||||
if: always()
|
||||
run: |
|
||||
echo "url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Storybook build
|
||||
if: success() && github.event.pull_request.head.repo.fork == false
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: storybook-static
|
||||
path: storybook-static/
|
||||
retention-days: 7
|
||||
|
||||
# Chromatic deployment only for version-bump-* branches or manual triggers
|
||||
chromatic-deployment:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && startsWith(github.head_ref, 'version-bump-'))
|
||||
outputs:
|
||||
conclusion: ${{ steps.job-status.outputs.conclusion }}
|
||||
workflow-url: ${{ steps.workflow-url.outputs.url }}
|
||||
chromatic-build-url: ${{ steps.chromatic.outputs.buildUrl }}
|
||||
chromatic-storybook-url: ${{ steps.chromatic.outputs.storybookUrl }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0 # Required for Chromatic baseline
|
||||
|
||||
@@ -29,7 +116,6 @@ jobs:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@@ -54,4 +140,92 @@ jobs:
|
||||
buildScriptName: build-storybook
|
||||
autoAcceptChanges: 'main' # Auto-accept changes on main branch
|
||||
exitOnceUploaded: true # Don't wait for UI tests to complete
|
||||
onlyChanged: true # Only capture changed stories
|
||||
|
||||
- name: Set job status
|
||||
id: job-status
|
||||
if: always()
|
||||
run: |
|
||||
echo "conclusion=${{ job.status }}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get workflow URL
|
||||
id: workflow-url
|
||||
if: always()
|
||||
run: |
|
||||
echo "url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_OUTPUT
|
||||
|
||||
# Deploy and comment for non-forked PRs only
|
||||
deploy-and-comment:
|
||||
needs: [storybook-build]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false && always()
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Download Storybook build
|
||||
if: needs.storybook-build.outputs.conclusion == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: storybook-static
|
||||
path: storybook-static
|
||||
|
||||
- name: Make deployment script executable
|
||||
run: chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
|
||||
|
||||
- name: Deploy Storybook and comment on PR
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
WORKFLOW_CONCLUSION: ${{ needs.storybook-build.outputs.conclusion }}
|
||||
WORKFLOW_URL: ${{ needs.storybook-build.outputs.workflow-url }}
|
||||
run: |
|
||||
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
|
||||
"${{ github.event.pull_request.number }}" \
|
||||
"${{ github.head_ref }}" \
|
||||
"completed"
|
||||
|
||||
# Update comment with Chromatic URLs for version-bump branches
|
||||
update-comment-with-chromatic:
|
||||
needs: [chromatic-deployment, deploy-and-comment]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false && startsWith(github.head_ref, 'version-bump-') && needs.chromatic-deployment.outputs.chromatic-build-url != ''
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Update comment with Chromatic URLs
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const buildUrl = '${{ needs.chromatic-deployment.outputs.chromatic-build-url }}';
|
||||
const storybookUrl = '${{ needs.chromatic-deployment.outputs.chromatic-storybook-url }}';
|
||||
|
||||
// Find the existing Storybook comment
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: ${{ github.event.pull_request.number }}
|
||||
});
|
||||
|
||||
const storybookComment = comments.find(comment =>
|
||||
comment.body.includes('<!-- STORYBOOK_BUILD_STATUS -->')
|
||||
);
|
||||
|
||||
if (storybookComment && buildUrl && storybookUrl) {
|
||||
// Append Chromatic info to existing comment
|
||||
const updatedBody = storybookComment.body.replace(
|
||||
/---\n(.*)$/s,
|
||||
`---\n### 🎨 Chromatic Visual Tests\n- 📊 [View Chromatic Build](${buildUrl})\n- 📚 [View Chromatic Storybook](${storybookUrl})\n\n$1`
|
||||
);
|
||||
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: storybookComment.id,
|
||||
body: updatedBody
|
||||
});
|
||||
}
|
||||
|
||||
2
.github/workflows/claude-pr-review.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
|
||||
4
.github/workflows/dev-release.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
version: ${{ steps.current_version.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Download dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
||||
26
.github/workflows/devtools-python.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Devtools Python Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'tools/devtools/**'
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'tools/devtools/**'
|
||||
|
||||
jobs:
|
||||
syntax:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Validate Python syntax
|
||||
run: python3 -m compileall -q tools/devtools
|
||||
21
.github/workflows/i18n-custom-nodes.yaml
vendored
@@ -22,23 +22,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout ComfyUI
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: comfyanonymous/ComfyUI
|
||||
path: ComfyUI
|
||||
ref: master
|
||||
- name: Checkout ComfyUI_frontend
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: Comfy-Org/ComfyUI_frontend
|
||||
path: ComfyUI_frontend
|
||||
- name: Checkout ComfyUI_devtools
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: Comfy-Org/ComfyUI_devtools
|
||||
path: ComfyUI/custom_nodes/ComfyUI_devtools
|
||||
- name: Copy ComfyUI_devtools from frontend repo
|
||||
run: |
|
||||
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
|
||||
cp -r ComfyUI_frontend/tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
|
||||
- name: Checkout custom node repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ${{ inputs.owner }}/${{ inputs.repository }}
|
||||
path: 'ComfyUI/custom_nodes/${{ inputs.repository }}'
|
||||
@@ -79,7 +78,7 @@ jobs:
|
||||
wait-for-it --service 127.0.0.1:8188 -t 600
|
||||
working-directory: ComfyUI
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install chromium --with-deps
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Start dev server
|
||||
# Run electron dev server as it is a superset of the web dev server
|
||||
@@ -87,7 +86,7 @@ jobs:
|
||||
run: pnpm dev:electron &
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Capture base i18n
|
||||
run: npx tsx scripts/diff-i18n capture
|
||||
run: pnpm exec tsx scripts/diff-i18n capture
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Update en.json
|
||||
run: pnpm collect-i18n
|
||||
@@ -100,7 +99,7 @@ jobs:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Diff base vs updated i18n
|
||||
run: npx tsx scripts/diff-i18n diff
|
||||
run: pnpm exec tsx scripts/diff-i18n diff
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Update i18n in custom node repository
|
||||
run: |
|
||||
|
||||
5
.github/workflows/i18n-node-defs.yaml
vendored
@@ -13,9 +13,10 @@ jobs:
|
||||
update-locales:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v3
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install chromium --with-deps
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Start dev server
|
||||
# Run electron dev server as it is a superset of the web dev server
|
||||
|
||||
5
.github/workflows/i18n.yaml
vendored
@@ -14,7 +14,8 @@ jobs:
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.head.repo.full_name == github.repository && startsWith(github.head_ref, 'version-bump-'))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v3
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
@@ -33,7 +34,7 @@ jobs:
|
||||
restore-keys: |
|
||||
playwright-browsers-${{ runner.os }}-
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install chromium --with-deps
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Start dev server
|
||||
# Run electron dev server as it is a superset of the web dev server
|
||||
|
||||
15
.github/workflows/json-validate.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: Validate JSON
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
json-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Validate JSON syntax
|
||||
run: ./scripts/cicd/check-json.sh
|
||||
2
.github/workflows/lint-and-format.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
|
||||
2
.github/workflows/pr-playwright-deploy.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Get PR Number
|
||||
id: pr
|
||||
|
||||
126
.github/workflows/pr-storybook-comment.yaml
vendored
@@ -1,126 +0,0 @@
|
||||
name: PR Storybook Comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Chromatic']
|
||||
types: [requested, completed]
|
||||
|
||||
jobs:
|
||||
comment-storybook:
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.repository == 'Comfy-Org/ComfyUI_frontend'
|
||||
&& github.event.workflow_run.event == 'pull_request'
|
||||
&& startsWith(github.event.workflow_run.head_branch, 'version-bump-')
|
||||
permissions:
|
||||
pull-requests: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Get PR number
|
||||
id: pr
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { data: pullRequests } = await github.rest.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`,
|
||||
});
|
||||
|
||||
if (pullRequests.length === 0) {
|
||||
console.log('No open PR found for this branch');
|
||||
return null;
|
||||
}
|
||||
|
||||
return pullRequests[0].number;
|
||||
|
||||
- name: Log when no PR found
|
||||
if: steps.pr.outputs.result == 'null'
|
||||
run: |
|
||||
echo "⚠️ No open PR found for branch: ${{ github.event.workflow_run.head_branch }}"
|
||||
echo "Workflow run ID: ${{ github.event.workflow_run.id }}"
|
||||
echo "Repository: ${{ github.event.workflow_run.repository.full_name }}"
|
||||
echo "Event: ${{ github.event.workflow_run.event }}"
|
||||
|
||||
- name: Get workflow run details
|
||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
|
||||
id: workflow-run
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const run = await github.rest.actions.getWorkflowRun({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: context.payload.workflow_run.id,
|
||||
});
|
||||
|
||||
return {
|
||||
conclusion: run.data.conclusion,
|
||||
html_url: run.data.html_url
|
||||
};
|
||||
|
||||
- name: Get completion time
|
||||
id: completion-time
|
||||
run: echo "time=$(date -u '+%m/%d/%Y, %I:%M:%S %p')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Comment PR - Storybook Started
|
||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'requested'
|
||||
uses: edumserrano/find-create-or-update-comment@82880b65c8a3a6e4c70aa05a204995b6c9696f53 # v3.0.0
|
||||
with:
|
||||
issue-number: ${{ steps.pr.outputs.result }}
|
||||
body-includes: '<!-- STORYBOOK_BUILD_STATUS -->'
|
||||
comment-author: 'github-actions[bot]'
|
||||
edit-mode: replace
|
||||
body: |
|
||||
<!-- STORYBOOK_BUILD_STATUS -->
|
||||
## 🎨 Storybook Build Status
|
||||
|
||||
<img alt='comfy-loading-gif' src='https://github.com/user-attachments/assets/755c86ee-e445-4ea8-bc2c-cca85df48686' width='14px' height='14px' style='vertical-align: middle; margin-right: 4px;' /> **Build is starting...**
|
||||
|
||||
⏰ Started at: ${{ steps.completion-time.outputs.time }} UTC
|
||||
|
||||
### 🚀 Building Storybook
|
||||
- 📦 Installing dependencies...
|
||||
- 🔧 Building Storybook components...
|
||||
- 🎨 Running Chromatic visual tests...
|
||||
|
||||
---
|
||||
⏱️ Please wait while the Storybook build is in progress...
|
||||
|
||||
- name: Comment PR - Storybook Complete
|
||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
|
||||
uses: edumserrano/find-create-or-update-comment@82880b65c8a3a6e4c70aa05a204995b6c9696f53 # v3.0.0
|
||||
with:
|
||||
issue-number: ${{ steps.pr.outputs.result }}
|
||||
body-includes: '<!-- STORYBOOK_BUILD_STATUS -->'
|
||||
comment-author: 'github-actions[bot]'
|
||||
edit-mode: replace
|
||||
body: |
|
||||
<!-- STORYBOOK_BUILD_STATUS -->
|
||||
## 🎨 Storybook Build Status
|
||||
|
||||
${{
|
||||
fromJSON(steps.workflow-run.outputs.result).conclusion == 'success' && '✅'
|
||||
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'skipped' && '⏭️'
|
||||
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'cancelled' && '🚫'
|
||||
|| '❌'
|
||||
}} **${{
|
||||
fromJSON(steps.workflow-run.outputs.result).conclusion == 'success' && 'Build completed successfully!'
|
||||
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'skipped' && 'Build skipped.'
|
||||
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'cancelled' && 'Build cancelled.'
|
||||
|| 'Build failed!'
|
||||
}}**
|
||||
|
||||
⏰ Completed at: ${{ steps.completion-time.outputs.time }} UTC
|
||||
|
||||
### 🔗 Links
|
||||
- [📊 View Workflow Run](${{ fromJSON(steps.workflow-run.outputs.result).html_url }})
|
||||
|
||||
---
|
||||
${{
|
||||
fromJSON(steps.workflow-run.outputs.result).conclusion == 'success' && '🎉 Your Storybook is ready for review!'
|
||||
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'skipped' && 'ℹ️ Chromatic was skipped for this PR.'
|
||||
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'cancelled' && 'ℹ️ The Chromatic run was cancelled.'
|
||||
|| '⚠️ Please check the workflow logs for error details.'
|
||||
}}
|
||||
90
.github/workflows/pr-storybook-deploy.yaml
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
name: PR Storybook Deploy (Forks)
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Storybook and Chromatic CI']
|
||||
types: [requested, completed]
|
||||
|
||||
env:
|
||||
DATE_FORMAT: '+%m/%d/%Y, %I:%M:%S %p'
|
||||
|
||||
jobs:
|
||||
deploy-and-comment-forked-pr:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.head_repository != null &&
|
||||
github.event.workflow_run.repository != null &&
|
||||
github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name
|
||||
permissions:
|
||||
pull-requests: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Log workflow trigger info
|
||||
run: |
|
||||
echo "Repository: ${{ github.repository }}"
|
||||
echo "Event: ${{ github.event.workflow_run.event }}"
|
||||
echo "Head repo: ${{ github.event.workflow_run.head_repository.full_name || 'null' }}"
|
||||
echo "Base repo: ${{ github.event.workflow_run.repository.full_name || 'null' }}"
|
||||
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Get PR Number
|
||||
id: pr
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { data: prs } = await github.rest.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
});
|
||||
|
||||
const pr = prs.find(p => p.head.sha === context.payload.workflow_run.head_sha);
|
||||
|
||||
if (!pr) {
|
||||
console.log('No PR found for SHA:', context.payload.workflow_run.head_sha);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(`Found PR #${pr.number} from fork: ${context.payload.workflow_run.head_repository.full_name}`);
|
||||
return pr.number;
|
||||
|
||||
- name: Handle Storybook Start
|
||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'requested'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
|
||||
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
|
||||
"${{ steps.pr.outputs.result }}" \
|
||||
"${{ github.event.workflow_run.head_branch }}" \
|
||||
"starting" \
|
||||
"$(date -u '${{ env.DATE_FORMAT }}')"
|
||||
|
||||
- name: Download and Deploy Storybook
|
||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' && github.event.workflow_run.conclusion == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
name: storybook-static
|
||||
path: storybook-static
|
||||
|
||||
- name: Handle Storybook Completion
|
||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
WORKFLOW_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
|
||||
WORKFLOW_URL: ${{ github.event.workflow_run.html_url }}
|
||||
run: |
|
||||
chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
|
||||
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
|
||||
"${{ steps.pr.outputs.result }}" \
|
||||
"${{ github.event.workflow_run.head_branch }}" \
|
||||
"completed"
|
||||
139
.github/workflows/publish-frontend-types.yaml
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
name: Publish Frontend Types
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to publish (e.g., 1.26.7)'
|
||||
required: true
|
||||
type: string
|
||||
dist_tag:
|
||||
description: 'npm dist-tag to use'
|
||||
required: true
|
||||
default: latest
|
||||
type: string
|
||||
ref:
|
||||
description: 'Git ref to checkout (commit SHA, tag, or branch)'
|
||||
required: false
|
||||
type: string
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
dist_tag:
|
||||
required: false
|
||||
type: string
|
||||
default: latest
|
||||
ref:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: publish-frontend-types-${{ github.workflow }}-${{ inputs.version }}-${{ inputs.dist_tag }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
publish_types_manual:
|
||||
name: Publish @comfyorg/comfyui-frontend-types
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Validate inputs
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${{ inputs.version }}"
|
||||
SEMVER_REGEX='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$'
|
||||
if [[ ! "$VERSION" =~ $SEMVER_REGEX ]]; then
|
||||
echo "::error title=Invalid version::Version '$VERSION' must follow semantic versioning (x.y.z[-suffix][+build])" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Determine ref to checkout
|
||||
id: resolve_ref
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
REF="${{ inputs.ref }}"
|
||||
VERSION="${{ inputs.version }}"
|
||||
if [ -n "$REF" ]; then
|
||||
if ! git check-ref-format --allow-onelevel "$REF"; then
|
||||
echo "::error title=Invalid ref::Ref '$REF' fails git check-ref-format validation." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "ref=$REF" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "ref=refs/tags/v$VERSION" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ steps.resolve_ref.outputs.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
||||
|
||||
- name: Build types
|
||||
run: pnpm build:types
|
||||
|
||||
- name: Verify version matches input
|
||||
id: verify
|
||||
shell: bash
|
||||
run: |
|
||||
PKG_VERSION=$(node -p "require('./package.json').version")
|
||||
TYPES_PKG_VERSION=$(node -p "require('./dist/package.json').version")
|
||||
if [ "$PKG_VERSION" != "${{ inputs.version }}" ]; then
|
||||
echo "Error: package.json version $PKG_VERSION does not match input ${{ inputs.version }}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$TYPES_PKG_VERSION" != "${{ inputs.version }}" ]; then
|
||||
echo "Error: dist/package.json version $TYPES_PKG_VERSION does not match input ${{ inputs.version }}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check if version already on npm
|
||||
id: check_npm
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
NAME=$(node -p "require('./dist/package.json').name")
|
||||
VER="${{ steps.verify.outputs.version }}"
|
||||
STATUS=0
|
||||
OUTPUT=$(npm view "${NAME}@${VER}" --json 2>&1) || STATUS=$?
|
||||
if [ "$STATUS" -eq 0 ]; then
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
echo "::warning title=Already published::${NAME}@${VER} already exists on npm. Skipping publish."
|
||||
else
|
||||
if echo "$OUTPUT" | grep -q "E404"; then
|
||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "::error title=Registry lookup failed::$OUTPUT" >&2
|
||||
exit "$STATUS"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Publish package
|
||||
if: steps.check_npm.outputs.exists == 'false'
|
||||
run: pnpm publish --access public --tag "${{ inputs.dist_tag }}" --no-git-checks
|
||||
working-directory: dist
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
42
.github/workflows/release.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
is_prerelease: ${{ steps.check_prerelease.outputs.is_prerelease }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Download dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Download dist artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -126,34 +126,8 @@ jobs:
|
||||
|
||||
publish_types:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
tsconfig.tsbuildinfo
|
||||
dist
|
||||
key: types-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
types-tools-cache-${{ runner.os }}-
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm build:types
|
||||
- name: Publish package
|
||||
run: pnpm publish --access public
|
||||
working-directory: dist
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
uses: ./.github/workflows/publish-frontend-types.yaml
|
||||
with:
|
||||
version: ${{ needs.build.outputs.version }}
|
||||
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
secrets: inherit
|
||||
|
||||
9
.github/workflows/test-browser-exp.yaml
vendored
@@ -10,7 +10,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.label.name == 'New Browser Test Expectations'
|
||||
steps:
|
||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v3
|
||||
- name: Checkout workflow repo
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
- name: Cache Playwright browsers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@@ -19,11 +22,11 @@ jobs:
|
||||
restore-keys: |
|
||||
playwright-browsers-${{ runner.os }}-
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install chromium --with-deps
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Run Playwright tests and update snapshots
|
||||
id: playwright-tests
|
||||
run: npx playwright test --update-snapshots
|
||||
run: pnpm exec playwright test --update-snapshots
|
||||
continue-on-error: true
|
||||
working-directory: ComfyUI_frontend
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
||||
37
.github/workflows/test-ui.yaml
vendored
@@ -15,24 +15,22 @@ jobs:
|
||||
playwright-version: ${{ steps.playwright-version.outputs.PLAYWRIGHT_VERSION }}
|
||||
steps:
|
||||
- name: Checkout ComfyUI
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: 'comfyanonymous/ComfyUI'
|
||||
path: 'ComfyUI'
|
||||
ref: master
|
||||
|
||||
- name: Checkout ComfyUI_frontend
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: 'Comfy-Org/ComfyUI_frontend'
|
||||
path: 'ComfyUI_frontend'
|
||||
|
||||
- name: Checkout ComfyUI_devtools
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'Comfy-Org/ComfyUI_devtools'
|
||||
path: 'ComfyUI/custom_nodes/ComfyUI_devtools'
|
||||
ref: 'd05fd48dd787a4192e16802d4244cfcc0e2f9684'
|
||||
- name: Copy ComfyUI_devtools from frontend repo
|
||||
run: |
|
||||
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
|
||||
cp -r ComfyUI_frontend/tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -150,7 +148,7 @@ jobs:
|
||||
|
||||
- name: Run Playwright tests (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
|
||||
id: playwright
|
||||
run: npx playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
|
||||
run: pnpm exec playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
|
||||
working-directory: ComfyUI_frontend
|
||||
env:
|
||||
PLAYWRIGHT_BLOB_OUTPUT_DIR: ../blob-report
|
||||
@@ -229,7 +227,13 @@ jobs:
|
||||
|
||||
- name: Run Playwright tests (${{ matrix.browser }})
|
||||
id: playwright
|
||||
run: npx playwright test --project=${{ matrix.browser }} --reporter=html
|
||||
run: |
|
||||
# Run tests with both HTML and JSON reporters
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
||||
pnpm exec playwright test --project=${{ matrix.browser }} \
|
||||
--reporter=list \
|
||||
--reporter=html \
|
||||
--reporter=json
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
@@ -246,7 +250,7 @@ jobs:
|
||||
if: ${{ !cancelled() }}
|
||||
steps:
|
||||
- name: Checkout ComfyUI_frontend
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: 'Comfy-Org/ComfyUI_frontend'
|
||||
path: 'ComfyUI_frontend'
|
||||
@@ -275,7 +279,12 @@ jobs:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Merge into HTML Report
|
||||
run: npx playwright merge-reports --reporter html ./all-blob-reports
|
||||
run: |
|
||||
# Generate HTML report
|
||||
pnpm exec playwright merge-reports --reporter=html ./all-blob-reports
|
||||
# Generate JSON report separately with explicit output path
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
||||
pnpm exec playwright merge-reports --reporter=json ./all-blob-reports
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- name: Upload HTML report
|
||||
@@ -297,7 +306,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Get start time
|
||||
id: start-time
|
||||
@@ -324,7 +333,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Download all playwright reports
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
4
.github/workflows/update-electron-types.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
- name: Get new version
|
||||
id: get-version
|
||||
run: |
|
||||
NEW_VERSION=$(pnpm list @comfyorg/comfyui-electron-types --json --depth=0 | jq -r '.[0].version')
|
||||
NEW_VERSION=$(pnpm list @comfyorg/comfyui-electron-types --json --depth=0 | jq -r '.[0].dependencies."@comfyorg/comfyui-electron-types".version')
|
||||
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Pull Request
|
||||
|
||||
6
.github/workflows/update-manager-types.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
comfyui-manager-repo-${{ runner.os }}-
|
||||
|
||||
- name: Checkout ComfyUI-Manager repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: Comfy-Org/ComfyUI-Manager
|
||||
path: ComfyUI-Manager
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
- name: Generate Manager API types
|
||||
run: |
|
||||
echo "Generating TypeScript types from ComfyUI-Manager@${{ steps.manager-info.outputs.commit }}..."
|
||||
npx openapi-typescript ./ComfyUI-Manager/openapi.yaml --output ./src/types/generatedManagerTypes.ts
|
||||
pnpm dlx openapi-typescript ./ComfyUI-Manager/openapi.yaml --output ./src/types/generatedManagerTypes.ts
|
||||
|
||||
- name: Validate generated types
|
||||
run: |
|
||||
|
||||
17
.github/workflows/update-registry-types.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
comfy-api-repo-${{ runner.os }}-
|
||||
|
||||
- name: Checkout comfy-api repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: Comfy-Org/comfy-api
|
||||
path: comfy-api
|
||||
@@ -68,17 +68,18 @@ jobs:
|
||||
- name: Generate API types
|
||||
run: |
|
||||
echo "Generating TypeScript types from comfy-api@${{ steps.api-info.outputs.commit }}..."
|
||||
npx openapi-typescript ./comfy-api/openapi.yml --output ./src/types/comfyRegistryTypes.ts
|
||||
mkdir -p ./packages/registry-types/src
|
||||
pnpm dlx openapi-typescript ./comfy-api/openapi.yml --output ./packages/registry-types/src/comfyRegistryTypes.ts
|
||||
|
||||
- name: Validate generated types
|
||||
run: |
|
||||
if [ ! -f ./src/types/comfyRegistryTypes.ts ]; then
|
||||
if [ ! -f ./packages/registry-types/src/comfyRegistryTypes.ts ]; then
|
||||
echo "Error: Types file was not generated."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if file is not empty
|
||||
if [ ! -s ./src/types/comfyRegistryTypes.ts ]; then
|
||||
if [ ! -s ./packages/registry-types/src/comfyRegistryTypes.ts ]; then
|
||||
echo "Error: Generated types file is empty."
|
||||
exit 1
|
||||
fi
|
||||
@@ -86,12 +87,12 @@ jobs:
|
||||
- name: Lint generated types
|
||||
run: |
|
||||
echo "Linting generated Comfy Registry API types..."
|
||||
pnpm lint:fix:no-cache -- ./src/types/comfyRegistryTypes.ts
|
||||
pnpm lint:fix:no-cache -- ./packages/registry-types/src/comfyRegistryTypes.ts
|
||||
|
||||
- name: Check for changes
|
||||
id: check-changes
|
||||
run: |
|
||||
if [[ -z $(git status --porcelain ./src/types/comfyRegistryTypes.ts) ]]; then
|
||||
if [[ -z $(git status --porcelain ./packages/registry-types/src/comfyRegistryTypes.ts) ]]; then
|
||||
echo "No changes to Comfy Registry API types detected."
|
||||
echo "changed=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
@@ -121,4 +122,4 @@ jobs:
|
||||
labels: CNR
|
||||
delete-branch: true
|
||||
add-paths: |
|
||||
src/types/comfyRegistryTypes.ts
|
||||
packages/registry-types/src/comfyRegistryTypes.ts
|
||||
|
||||
2
.github/workflows/version-bump.yaml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
2
.github/workflows/vitest.yaml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
8
.gitignore
vendored
@@ -22,6 +22,8 @@ dist-ssr
|
||||
*.local
|
||||
# Claude configuration
|
||||
.claude/*.local.json
|
||||
.claude/*.local.md
|
||||
.claude/*.local.txt
|
||||
CLAUDE.local.md
|
||||
|
||||
# Editor directories and files
|
||||
@@ -44,6 +46,7 @@ components.d.ts
|
||||
tests-ui/data/*
|
||||
tests-ui/ComfyUI_examples
|
||||
tests-ui/workflows/examples
|
||||
coverage/
|
||||
|
||||
# Browser tests
|
||||
/test-results/
|
||||
@@ -51,6 +54,7 @@ tests-ui/workflows/examples
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
browser_tests/**/*-win32.png
|
||||
browser_tests/local/
|
||||
|
||||
.env
|
||||
|
||||
@@ -77,8 +81,8 @@ vite.config.mts.timestamp-*.mjs
|
||||
*storybook.log
|
||||
storybook-static
|
||||
|
||||
|
||||
|
||||
# MCP Servers
|
||||
.playwright-mcp/*
|
||||
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
pnpm exec lint-staged
|
||||
pnpm exec tsx scripts/check-unused-i18n-keys.ts
|
||||
pnpm exec tsx scripts/check-unused-i18n-keys.ts
|
||||
@@ -9,7 +9,7 @@ module.exports = defineConfig({
|
||||
entry: 'src/locales/en',
|
||||
entryLocale: 'en',
|
||||
output: 'src/locales',
|
||||
outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar'],
|
||||
outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar', 'tr'],
|
||||
reference: `Special names to keep untranslated: flux, photomaker, clip, vae, cfg, stable audio, stable cascade, stable zero, controlnet, lora, HiDream.
|
||||
'latent' is the short form of 'latent space'.
|
||||
'mask' is in the context of image processing.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
src/types/comfyRegistryTypes.ts
|
||||
src/types/generatedManagerTypes.ts
|
||||
packages/registry-types/src/comfyRegistryTypes.ts
|
||||
src/types/generatedManagerTypes.ts
|
||||
|
||||
@@ -15,26 +15,37 @@ const config: StorybookConfig = {
|
||||
async viteFinal(config) {
|
||||
// Use dynamic import to avoid CJS deprecation warning
|
||||
const { mergeConfig } = await import('vite')
|
||||
const { default: tailwindcss } = await import('@tailwindcss/vite')
|
||||
|
||||
// Filter out any plugins that might generate import maps
|
||||
if (config.plugins) {
|
||||
config.plugins = config.plugins.filter((plugin: any) => {
|
||||
if (plugin && plugin.name && plugin.name.includes('import-map')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
config.plugins = config.plugins
|
||||
// Type guard: ensure we have valid plugin objects with names
|
||||
.filter(
|
||||
(plugin): plugin is NonNullable<typeof plugin> & { name: string } => {
|
||||
return (
|
||||
plugin !== null &&
|
||||
plugin !== undefined &&
|
||||
typeof plugin === 'object' &&
|
||||
'name' in plugin &&
|
||||
typeof plugin.name === 'string'
|
||||
)
|
||||
}
|
||||
)
|
||||
// Business logic: filter out import-map plugins
|
||||
.filter((plugin) => !plugin.name.includes('import-map'))
|
||||
}
|
||||
|
||||
return mergeConfig(config, {
|
||||
// Replace plugins entirely to avoid inheritance issues
|
||||
plugins: [
|
||||
// Only include plugins we explicitly need for Storybook
|
||||
tailwindcss(),
|
||||
Icons({
|
||||
compiler: 'vue3',
|
||||
customCollections: {
|
||||
comfy: FileSystemIconLoader(
|
||||
process.cwd() + '/src/assets/icons/custom'
|
||||
process.cwd() + '/packages/design-system/src/icons'
|
||||
)
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -57,9 +57,8 @@
|
||||
|
||||
/* Override Storybook's problematic & selector styles */
|
||||
/* Reset only the specific properties that Storybook injects */
|
||||
#storybook-root li+li,
|
||||
#storybook-docs li+li {
|
||||
margin: inherit;
|
||||
padding: inherit;
|
||||
li+li {
|
||||
margin: 0;
|
||||
padding: revert-layer;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
import { definePreset } from '@primevue/themes'
|
||||
import Aura from '@primevue/themes/aura'
|
||||
import { setup } from '@storybook/vue3'
|
||||
import type { Preview } from '@storybook/vue3-vite'
|
||||
import type { Preview, StoryContext, StoryFn } from '@storybook/vue3-vite'
|
||||
import { createPinia } from 'pinia'
|
||||
import 'primeicons/primeicons.css'
|
||||
import PrimeVue from 'primevue/config'
|
||||
@@ -9,11 +9,9 @@ import ConfirmationService from 'primevue/confirmationservice'
|
||||
import ToastService from 'primevue/toastservice'
|
||||
import Tooltip from 'primevue/tooltip'
|
||||
|
||||
import '../src/assets/css/style.css'
|
||||
import { i18n } from '../src/i18n'
|
||||
import '../src/lib/litegraph/public/css/litegraph.css'
|
||||
import { useWidgetStore } from '../src/stores/widgetStore'
|
||||
import { useColorPaletteStore } from '../src/stores/workspace/colorPaletteStore'
|
||||
import '@/assets/css/style.css'
|
||||
import { i18n } from '@/i18n'
|
||||
import '@/lib/litegraph/public/css/litegraph.css'
|
||||
|
||||
const ComfyUIPreset = definePreset(Aura, {
|
||||
semantic: {
|
||||
@@ -25,13 +23,11 @@ const ComfyUIPreset = definePreset(Aura, {
|
||||
// Setup Vue app for Storybook
|
||||
setup((app) => {
|
||||
app.directive('tooltip', Tooltip)
|
||||
|
||||
// Create Pinia instance
|
||||
const pinia = createPinia()
|
||||
|
||||
app.use(pinia)
|
||||
|
||||
// Initialize stores
|
||||
useColorPaletteStore(pinia)
|
||||
useWidgetStore(pinia)
|
||||
|
||||
app.use(i18n)
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
@@ -50,8 +46,8 @@ setup((app) => {
|
||||
app.use(ToastService)
|
||||
})
|
||||
|
||||
// Dark theme decorator
|
||||
export const withTheme = (Story: any, context: any) => {
|
||||
// Theme and dialog decorator
|
||||
export const withTheme = (Story: StoryFn, context: StoryContext) => {
|
||||
const theme = context.globals.theme || 'light'
|
||||
|
||||
// Apply theme class to document root
|
||||
@@ -63,7 +59,7 @@ export const withTheme = (Story: any, context: any) => {
|
||||
document.body.classList.remove('dark-theme')
|
||||
}
|
||||
|
||||
return Story()
|
||||
return Story(context.args, context)
|
||||
}
|
||||
|
||||
const preview: Preview = {
|
||||
|
||||
@@ -31,10 +31,9 @@
|
||||
- Playwright: place tests in `browser_tests/`; optional tags like `@mobile`, `@2x` are respected by config.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
- Commits: Prefer Conventional Commits (e.g., `feat(ui): add sidebar`), `refactor(litegraph): …`. Use `[skip ci]` for locale-only updates when appropriate.
|
||||
- PRs: Include clear description, linked issues (`Fixes #123`), and screenshots/GIFs for UI changes. Add/adjust tests and i18n strings when applicable.
|
||||
- Commits: Use `[skip ci]` for locale-only updates when appropriate.
|
||||
- PRs: Include clear description, linked issues (`- Fixes #123`), and screenshots/GIFs for UI changes.
|
||||
- Quality gates: `pnpm lint`, `pnpm typecheck`, and relevant tests must pass. Keep PRs focused and small.
|
||||
|
||||
## Security & Configuration Tips
|
||||
- Secrets: Use `.env` (see `.env_example`); do not commit secrets.
|
||||
- Backend: Dev server expects ComfyUI backend at `localhost:8188` by default; configure via `.env`.
|
||||
|
||||
70
CODEOWNERS
@@ -1,17 +1,61 @@
|
||||
# Admins
|
||||
* @Comfy-Org/comfy_frontend_devs
|
||||
# Desktop/Electron
|
||||
/src/types/desktop/ @webfiltered
|
||||
/src/constants/desktopDialogs.ts @webfiltered
|
||||
/src/constants/desktopMaintenanceTasks.ts @webfiltered
|
||||
/src/stores/electronDownloadStore.ts @webfiltered
|
||||
/src/extensions/core/electronAdapter.ts @webfiltered
|
||||
/src/views/DesktopDialogView.vue @webfiltered
|
||||
/src/components/install/ @webfiltered
|
||||
/src/components/maintenance/ @webfiltered
|
||||
/vite.electron.config.mts @webfiltered
|
||||
|
||||
# Maintainers
|
||||
*.md @Comfy-Org/comfy_maintainer
|
||||
/tests-ui/ @Comfy-Org/comfy_maintainer
|
||||
/browser_tests/ @Comfy-Org/comfy_maintainer
|
||||
/.env_example @Comfy-Org/comfy_maintainer
|
||||
# Common UI Components
|
||||
/src/components/chip/ @viva-jinyi
|
||||
/src/components/card/ @viva-jinyi
|
||||
/src/components/button/ @viva-jinyi
|
||||
/src/components/input/ @viva-jinyi
|
||||
|
||||
# Translations (AIGODLIKE team + shinshin86)
|
||||
/src/locales/ @Yorha4D @KarryCharon @DorotaLuna @shinshin86 @Comfy-Org/comfy_maintainer
|
||||
# Topbar
|
||||
/src/components/topbar/ @pythongosssss
|
||||
|
||||
# Load 3D extension
|
||||
/src/extensions/core/load3d.ts @jtydhr88 @Comfy-Org/comfy_frontend_devs
|
||||
# Thumbnail
|
||||
/src/renderer/core/thumbnail/ @pythongosssss
|
||||
|
||||
# Mask Editor extension
|
||||
/src/extensions/core/maskeditor.ts @brucew4yn3rp @trsommer @Comfy-Org/comfy_frontend_devs
|
||||
# Legacy UI
|
||||
/scripts/ui/ @pythongosssss
|
||||
|
||||
# Link rendering
|
||||
/src/renderer/core/canvas/links/ @benceruleanlu
|
||||
|
||||
# Node help system
|
||||
/src/utils/nodeHelpUtil.ts @benceruleanlu
|
||||
/src/stores/workspace/nodeHelpStore.ts @benceruleanlu
|
||||
/src/services/nodeHelpService.ts @benceruleanlu
|
||||
|
||||
# Selection toolbox
|
||||
/src/components/graph/selectionToolbox/ @Myestery
|
||||
|
||||
# Minimap
|
||||
/src/renderer/extensions/minimap/ @jtydhr88
|
||||
|
||||
# Assets
|
||||
/src/platform/assets/ @arjansingh
|
||||
|
||||
# Workflow Templates
|
||||
/src/platform/workflow/templates/ @Myestery @christian-byrne @comfyui-wiki
|
||||
/src/components/templates/ @Myestery @christian-byrne @comfyui-wiki
|
||||
|
||||
# Mask Editor
|
||||
/src/extensions/core/maskeditor.ts @trsommer @brucew4yn3rp
|
||||
/src/extensions/core/maskEditorLayerFilenames.ts @trsommer @brucew4yn3rp
|
||||
/src/extensions/core/maskEditorOld.ts @trsommer @brucew4yn3rp
|
||||
|
||||
# 3D
|
||||
/src/extensions/core/load3d.ts @jtydhr88
|
||||
/src/components/load3d/ @jtydhr88
|
||||
|
||||
# Manager
|
||||
/src/workbench/extensions/manager/ @viva-jinyi @christian-byrne @ltdrdata
|
||||
|
||||
# Translations
|
||||
/src/locales/ @Yorha4D @KarryCharon @shinshin86 @Comfy-Org/comfy_maintainer
|
||||
|
||||
@@ -265,9 +265,9 @@ The project supports three types of icons, all with automatic imports (no manual
|
||||
2. **Iconify Icons** - 200,000+ icons from various libraries: `<i-lucide:settings />`, `<i-mdi:folder />`
|
||||
3. **Custom Icons** - Your own SVG icons: `<i-comfy:workflow />`
|
||||
|
||||
Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Custom icons are stored in `src/assets/icons/custom/` and processed by `build/customIconCollection.ts` with automatic validation.
|
||||
Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Custom icons are stored in `packages/design-system/src/icons/` and processed by `packages/design-system/src/iconCollection.ts` with automatic validation.
|
||||
|
||||
For detailed instructions and code examples, see [src/assets/icons/README.md](src/assets/icons/README.md).
|
||||
For detailed instructions and code examples, see [packages/design-system/src/icons/README.md](packages/design-system/src/icons/README.md).
|
||||
|
||||
## Working with litegraph.js
|
||||
|
||||
|
||||
@@ -16,15 +16,20 @@ Without this flag, parallel tests will conflict and fail randomly.
|
||||
|
||||
### ComfyUI devtools
|
||||
|
||||
Clone <https://github.com/Comfy-Org/ComfyUI_devtools> to your `custom_nodes` directory.
|
||||
ComfyUI_devtools is now included in this repository under `tools/devtools/`. During CI/CD, these files are automatically copied to the `custom_nodes` directory.
|
||||
_ComfyUI_devtools adds additional API endpoints and nodes to ComfyUI for browser testing._
|
||||
|
||||
For local development, copy the devtools files to your ComfyUI installation:
|
||||
```bash
|
||||
cp -r tools/devtools/* /path/to/your/ComfyUI/custom_nodes/ComfyUI_devtools/
|
||||
```
|
||||
|
||||
### Node.js & Playwright Prerequisites
|
||||
|
||||
Ensure you have Node.js v20 or v22 installed. Then, set up the Chromium test driver:
|
||||
|
||||
```bash
|
||||
npx playwright install chromium --with-deps
|
||||
pnpm exec playwright install chromium --with-deps
|
||||
```
|
||||
|
||||
### Environment Configuration
|
||||
@@ -51,14 +56,6 @@ TEST_COMFYUI_DIR=/path/to/your/ComfyUI
|
||||
|
||||
### Common Setup Issues
|
||||
|
||||
**Most tests require the new menu system** - Add to your test:
|
||||
|
||||
```typescript
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
```
|
||||
|
||||
### Release API Mocking
|
||||
|
||||
By default, all tests mock the release API (`api.comfy.org/releases`) to prevent release notification popups from interfering with test execution. This is necessary because the release notifications can appear over UI elements and block test interactions.
|
||||
@@ -76,7 +73,7 @@ For tests that specifically need to test release functionality, see the example
|
||||
**Always use UI mode for development:**
|
||||
|
||||
```bash
|
||||
npx playwright test --ui
|
||||
pnpm exec playwright test --ui
|
||||
```
|
||||
|
||||
UI mode features:
|
||||
@@ -87,13 +84,13 @@ UI mode features:
|
||||
- **Console/Network Tabs**: View logs and API calls at each step
|
||||
- **Attachments Tab**: View all snapshots with expected and actual images
|
||||
|
||||

|
||||

|
||||
|
||||
For CI or headless testing:
|
||||
|
||||
```bash
|
||||
npx playwright test # Run all tests
|
||||
npx playwright test widget.spec.ts # Run specific test file
|
||||
pnpm exec playwright test # Run all tests
|
||||
pnpm exec playwright test widget.spec.ts # Run specific test file
|
||||
```
|
||||
|
||||
### Local Development Config
|
||||
@@ -389,7 +386,7 @@ export default defineConfig({
|
||||
Option 2 - Generate local baselines for comparison:
|
||||
|
||||
```bash
|
||||
npx playwright test --update-snapshots
|
||||
pnpm exec playwright test --update-snapshots
|
||||
```
|
||||
|
||||
### Creating New Screenshot Baselines
|
||||
|
||||
1
browser_tests/assets/vueNodes/simple-triple.json
Normal file
@@ -0,0 +1 @@
|
||||
{"id":"4412323e-2509-4258-8abc-68ddeea8f9e1","revision":0,"last_node_id":39,"last_link_id":29,"nodes":[{"id":37,"type":"KSampler","pos":[3635.923095703125,870.237548828125],"size":[428,437],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"model","name":"model","type":"MODEL","link":null},{"localized_name":"positive","name":"positive","type":"CONDITIONING","link":null},{"localized_name":"negative","name":"negative","type":"CONDITIONING","link":null},{"localized_name":"latent_image","name":"latent_image","type":"LATENT","link":null},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"steps","name":"steps","type":"INT","widget":{"name":"steps"},"link":null},{"localized_name":"cfg","name":"cfg","type":"FLOAT","widget":{"name":"cfg"},"link":null},{"localized_name":"sampler_name","name":"sampler_name","type":"COMBO","widget":{"name":"sampler_name"},"link":null},{"localized_name":"scheduler","name":"scheduler","type":"COMBO","widget":{"name":"scheduler"},"link":null},{"localized_name":"denoise","name":"denoise","type":"FLOAT","widget":{"name":"denoise"},"link":null}],"outputs":[{"localized_name":"LATENT","name":"LATENT","type":"LATENT","links":null}],"properties":{"Node name for S&R":"KSampler"},"widgets_values":[0,"randomize",20,8,"euler","simple",1]},{"id":38,"type":"VAEDecode","pos":[4164.01611328125,925.5230712890625],"size":[193.25,107],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"samples","name":"samples","type":"LATENT","link":null},{"localized_name":"vae","name":"vae","type":"VAE","link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":null}],"properties":{"Node name for S&R":"VAEDecode"}},{"id":39,"type":"CLIPTextEncode","pos":[3259.289794921875,927.2508544921875],"size":[239.9375,155],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"clip","name":"clip","type":"CLIP","link":null},{"localized_name":"text","name":"text","type":"STRING","widget":{"name":"text"},"link":null}],"outputs":[{"localized_name":"CONDITIONING","name":"CONDITIONING","type":"CONDITIONING","links":null}],"properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":[""]}],"links":[],"groups":[],"config":{},"extra":{"ds":{"scale":1.1576250000000001,"offset":[-2808.366467322067,-478.34316506594797]}},"version":0.4}
|
||||
221
browser_tests/assets/widgets/all_load_widgets.json
Normal file
@@ -0,0 +1,221 @@
|
||||
{
|
||||
"id": "e74f5af9-b886-4a21-abbf-ed535d12e2fb",
|
||||
"revision": 0,
|
||||
"last_node_id": 8,
|
||||
"last_link_id": 0,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "LoadAudio",
|
||||
"pos": [
|
||||
41.52964782714844,
|
||||
16.930862426757812
|
||||
],
|
||||
"size": [
|
||||
444,
|
||||
125
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "AUDIO",
|
||||
"type": "AUDIO",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadAudio"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
null,
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LoadVideo",
|
||||
"pos": [
|
||||
502.28570556640625,
|
||||
16.857147216796875
|
||||
],
|
||||
"size": [
|
||||
444,
|
||||
525
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "VIDEO",
|
||||
"type": "VIDEO",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadVideo"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "DevToolsLoadAnimatedImageTest",
|
||||
"pos": [
|
||||
41.71427917480469,
|
||||
188.0000457763672
|
||||
],
|
||||
"size": [
|
||||
444,
|
||||
553
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "DevToolsLoadAnimatedImageTest"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
958.285888671875,
|
||||
16.57145118713379
|
||||
],
|
||||
"size": [
|
||||
444,
|
||||
553
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "LoadImageMask",
|
||||
"pos": [
|
||||
503.4285888671875,
|
||||
588
|
||||
],
|
||||
"size": [
|
||||
444,
|
||||
563
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImageMask"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
"alpha",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "LoadImageOutput",
|
||||
"pos": [
|
||||
965.1429443359375,
|
||||
612
|
||||
],
|
||||
"size": [
|
||||
444,
|
||||
553
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImageOutput"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
false,
|
||||
"refresh",
|
||||
"image"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1,
|
||||
"offset": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.28.3"
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -5,13 +5,14 @@ import dotenv from 'dotenv'
|
||||
import * as fs from 'fs'
|
||||
|
||||
import type { LGraphNode } from '../../src/lib/litegraph/src/litegraph'
|
||||
import type { NodeId } from '../../src/schemas/comfyWorkflowSchema'
|
||||
import type { NodeId } from '../../src/platform/workflow/validation/schemas/workflowSchema'
|
||||
import type { KeyCombo } from '../../src/schemas/keyBindingSchema'
|
||||
import type { useWorkspaceStore } from '../../src/stores/workspaceStore'
|
||||
import { NodeBadgeMode } from '../../src/types/nodeSource'
|
||||
import { ComfyActionbar } from '../helpers/actionbar'
|
||||
import { ComfyTemplates } from '../helpers/templates'
|
||||
import { ComfyMouse } from './ComfyMouse'
|
||||
import { VueNodeHelpers } from './VueNodeHelpers'
|
||||
import { ComfyNodeSearchBox } from './components/ComfyNodeSearchBox'
|
||||
import { SettingDialog } from './components/SettingDialog'
|
||||
import {
|
||||
@@ -144,6 +145,7 @@ export class ComfyPage {
|
||||
public readonly templates: ComfyTemplates
|
||||
public readonly settingDialog: SettingDialog
|
||||
public readonly confirmDialog: ConfirmDialog
|
||||
public readonly vueNodes: VueNodeHelpers
|
||||
|
||||
/** Worker index to test user ID */
|
||||
public readonly userIds: string[] = []
|
||||
@@ -172,6 +174,7 @@ export class ComfyPage {
|
||||
this.templates = new ComfyTemplates(page)
|
||||
this.settingDialog = new SettingDialog(page, this)
|
||||
this.confirmDialog = new ConfirmDialog(page)
|
||||
this.vueNodes = new VueNodeHelpers(page)
|
||||
}
|
||||
|
||||
convertLeafToContent(structure: FolderStructure): FolderStructure {
|
||||
@@ -1421,7 +1424,7 @@ export class ComfyPage {
|
||||
}
|
||||
|
||||
async closeDialog() {
|
||||
await this.page.locator('.p-dialog-close-button').click()
|
||||
await this.page.locator('.p-dialog-close-button').click({ force: true })
|
||||
await expect(this.page.locator('.p-dialog')).toBeHidden()
|
||||
}
|
||||
|
||||
@@ -1640,7 +1643,7 @@ export const comfyPageFixture = base.extend<{
|
||||
|
||||
try {
|
||||
await comfyPage.setupSettings({
|
||||
'Comfy.UseNewMenu': 'Disabled',
|
||||
'Comfy.UseNewMenu': 'Top',
|
||||
// Hide canvas menu/info/selection toolbox by default.
|
||||
'Comfy.Graph.CanvasInfo': false,
|
||||
'Comfy.Graph.CanvasMenu': false,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Page, test as base } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
import { test as base } from '@playwright/test'
|
||||
|
||||
export class UserSelectPage {
|
||||
constructor(
|
||||
|
||||
117
browser_tests/fixtures/VueNodeHelpers.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Vue Node Test Helpers
|
||||
*/
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
export class VueNodeHelpers {
|
||||
constructor(private page: Page) {}
|
||||
|
||||
/**
|
||||
* Get locator for all Vue node components in the DOM
|
||||
*/
|
||||
get nodes(): Locator {
|
||||
return this.page.locator('[data-node-id]')
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locator for selected Vue node components (using visual selection indicators)
|
||||
*/
|
||||
get selectedNodes(): Locator {
|
||||
return this.page.locator(
|
||||
'[data-node-id].outline-black, [data-node-id].outline-white'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locator for a Vue node by the node's title (displayed name in the header)
|
||||
*/
|
||||
getNodeByTitle(title: string): Locator {
|
||||
return this.page.locator(`[data-node-id]`).filter({ hasText: title })
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total count of Vue nodes in the DOM
|
||||
*/
|
||||
async getNodeCount(): Promise<number> {
|
||||
return await this.nodes.count()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of selected Vue nodes
|
||||
*/
|
||||
async getSelectedNodeCount(): Promise<number> {
|
||||
return await this.selectedNodes.count()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Vue node IDs currently in the DOM
|
||||
*/
|
||||
async getNodeIds(): Promise<string[]> {
|
||||
return await this.nodes.evaluateAll((nodes) =>
|
||||
nodes
|
||||
.map((n) => n.getAttribute('data-node-id'))
|
||||
.filter((id): id is string => id !== null)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a specific Vue node by ID
|
||||
*/
|
||||
async selectNode(nodeId: string): Promise<void> {
|
||||
await this.page.locator(`[data-node-id="${nodeId}"]`).click()
|
||||
}
|
||||
|
||||
/**
|
||||
* Select multiple Vue nodes by IDs using Ctrl+click
|
||||
*/
|
||||
async selectNodes(nodeIds: string[]): Promise<void> {
|
||||
if (nodeIds.length === 0) return
|
||||
|
||||
// Select first node normally
|
||||
await this.selectNode(nodeIds[0])
|
||||
|
||||
// Add additional nodes with Ctrl+click
|
||||
for (let i = 1; i < nodeIds.length; i++) {
|
||||
await this.page.locator(`[data-node-id="${nodeIds[i]}"]`).click({
|
||||
modifiers: ['Control']
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all selections by clicking empty space
|
||||
*/
|
||||
async clearSelection(): Promise<void> {
|
||||
await this.page.mouse.click(50, 50)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete selected Vue nodes using Delete key
|
||||
*/
|
||||
async deleteSelected(): Promise<void> {
|
||||
await this.page.locator('#graph-canvas').focus()
|
||||
await this.page.keyboard.press('Delete')
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete selected Vue nodes using Backspace key
|
||||
*/
|
||||
async deleteSelectedWithBackspace(): Promise<void> {
|
||||
await this.page.locator('#graph-canvas').focus()
|
||||
await this.page.keyboard.press('Backspace')
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for Vue nodes to be rendered
|
||||
*/
|
||||
async waitForNodes(expectedCount?: number): Promise<void> {
|
||||
if (expectedCount !== undefined) {
|
||||
await this.page.waitForFunction(
|
||||
(count) => document.querySelectorAll('[data-node-id]').length >= count,
|
||||
expectedCount
|
||||
)
|
||||
} else {
|
||||
await this.page.waitForSelector('[data-node-id]')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Locator, Page } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
export class ComfyNodeSearchFilterSelectionPanel {
|
||||
constructor(public readonly page: Page) {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Page } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
import { ComfyPage } from '../ComfyPage'
|
||||
import type { ComfyPage } from '../ComfyPage'
|
||||
|
||||
export class SettingDialog {
|
||||
constructor(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Locator, Page } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
class SidebarTab {
|
||||
constructor(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Locator, Page, expect } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
export class Topbar {
|
||||
private readonly menuLocator: Locator
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
import type { NodeId } from '../../../src/schemas/comfyWorkflowSchema'
|
||||
import type { NodeId } from '../../../src/platform/workflow/validation/schemas/workflowSchema'
|
||||
import { ManageGroupNode } from '../../helpers/manageGroupNode'
|
||||
import type { ComfyPage } from '../ComfyPage'
|
||||
import type { Position, Size } from '../types'
|
||||
|
||||
@@ -12,9 +12,10 @@ export const webSocketFixture = base.extend<{
|
||||
// so we can look it up to trigger messages
|
||||
const store: Record<string, WebSocket> = ((window as any).__ws__ = {})
|
||||
window.WebSocket = class extends window.WebSocket {
|
||||
constructor() {
|
||||
// @ts-expect-error
|
||||
super(...arguments)
|
||||
constructor(
|
||||
...rest: ConstructorParameters<typeof window.WebSocket>
|
||||
) {
|
||||
super(...rest)
|
||||
store[this.url] = this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FullConfig } from '@playwright/test'
|
||||
import type { FullConfig } from '@playwright/test'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
import { backupPath } from './utils/backupUtils'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FullConfig } from '@playwright/test'
|
||||
import type { FullConfig } from '@playwright/test'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
import { restorePath } from './utils/backupUtils'
|
||||
|
||||
104
browser_tests/helpers/fitToView.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import type { ReadOnlyRect } from '../../src/lib/litegraph/src/interfaces'
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
|
||||
interface FitToViewOptions {
|
||||
selectionOnly?: boolean
|
||||
zoom?: number
|
||||
padding?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantly fits the canvas view to graph content without waiting for UI animation.
|
||||
*
|
||||
* Lives outside the shared fixture to keep the default ComfyPage interactions user-oriented.
|
||||
*/
|
||||
export async function fitToViewInstant(
|
||||
comfyPage: ComfyPage,
|
||||
options: FitToViewOptions = {}
|
||||
) {
|
||||
const { selectionOnly = false, zoom = 0.75, padding = 10 } = options
|
||||
|
||||
const rectangles = await comfyPage.page.evaluate<
|
||||
ReadOnlyRect[] | null,
|
||||
{ selectionOnly: boolean }
|
||||
>(
|
||||
({ selectionOnly }) => {
|
||||
const app = window['app']
|
||||
if (!app?.canvas) return null
|
||||
|
||||
const canvas = app.canvas
|
||||
const items = (() => {
|
||||
if (selectionOnly && canvas.selectedItems?.size) {
|
||||
return Array.from(canvas.selectedItems)
|
||||
}
|
||||
try {
|
||||
return Array.from(canvas.positionableItems ?? [])
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
})()
|
||||
|
||||
if (!items.length) return null
|
||||
|
||||
const rects: ReadOnlyRect[] = []
|
||||
|
||||
for (const item of items) {
|
||||
const rect = item?.boundingRect
|
||||
if (!rect) continue
|
||||
|
||||
const x = Number(rect[0])
|
||||
const y = Number(rect[1])
|
||||
const width = Number(rect[2])
|
||||
const height = Number(rect[3])
|
||||
|
||||
rects.push([x, y, width, height] as const)
|
||||
}
|
||||
|
||||
return rects.length ? rects : null
|
||||
},
|
||||
{ selectionOnly }
|
||||
)
|
||||
|
||||
if (!rectangles || rectangles.length === 0) return
|
||||
|
||||
let minX = Infinity
|
||||
let minY = Infinity
|
||||
let maxX = -Infinity
|
||||
let maxY = -Infinity
|
||||
|
||||
for (const [x, y, width, height] of rectangles) {
|
||||
minX = Math.min(minX, Number(x))
|
||||
minY = Math.min(minY, Number(y))
|
||||
maxX = Math.max(maxX, Number(x) + Number(width))
|
||||
maxY = Math.max(maxY, Number(y) + Number(height))
|
||||
}
|
||||
|
||||
const hasFiniteBounds =
|
||||
Number.isFinite(minX) &&
|
||||
Number.isFinite(minY) &&
|
||||
Number.isFinite(maxX) &&
|
||||
Number.isFinite(maxY)
|
||||
|
||||
if (!hasFiniteBounds) return
|
||||
|
||||
const bounds: ReadOnlyRect = [
|
||||
minX - padding,
|
||||
minY - padding,
|
||||
maxX - minX + 2 * padding,
|
||||
maxY - minY + 2 * padding
|
||||
]
|
||||
|
||||
await comfyPage.page.evaluate(
|
||||
({ bounds, zoom }) => {
|
||||
const app = window['app']
|
||||
if (!app?.canvas) return
|
||||
|
||||
const canvas = app.canvas
|
||||
canvas.ds.fitToBounds(bounds, { zoom })
|
||||
canvas.setDirty(true, true)
|
||||
},
|
||||
{ bounds, zoom }
|
||||
)
|
||||
|
||||
await comfyPage.nextFrame()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Locator, Page } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
export class ManageGroupNode {
|
||||
footer: Locator
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Locator, Page } from '@playwright/test'
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
import path from 'path'
|
||||
|
||||
import {
|
||||
import type {
|
||||
TemplateInfo,
|
||||
WorkflowTemplates
|
||||
} from '../../src/types/workflowTemplateTypes'
|
||||
} from '../../src/platform/workflow/templates/types/template'
|
||||
|
||||
export class ComfyTemplates {
|
||||
readonly content: Locator
|
||||
|
||||
@@ -29,9 +29,9 @@ test.describe('Actionbar', () => {
|
||||
|
||||
// Intercept the prompt queue endpoint
|
||||
let promptNumber = 0
|
||||
comfyPage.page.route('**/api/prompt', async (route, req) => {
|
||||
await comfyPage.page.route('**/api/prompt', async (route, req) => {
|
||||
await new Promise((r) => setTimeout(r, 100))
|
||||
route.fulfill({
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify({
|
||||
prompt_id: promptNumber,
|
||||
|
||||
@@ -2,6 +2,10 @@ import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Background Image Upload', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
// Reset the background image setting before each test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import {
|
||||
ComfyPage,
|
||||
comfyExpect as expect,
|
||||
comfyPageFixture as test
|
||||
} from '../fixtures/ComfyPage'
|
||||
@@ -15,6 +15,10 @@ async function afterChange(comfyPage: ComfyPage) {
|
||||
})
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Change Tracker', () => {
|
||||
test.describe('Undo/Redo', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { Page, expect } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
interface ChatHistoryEntry {
|
||||
prompt: string
|
||||
response: string
|
||||
|
||||
@@ -3,6 +3,10 @@ import { expect } from '@playwright/test'
|
||||
import type { Palette } from '../../src/schemas/colorPaletteSchema'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
const customColorPalettes: Record<string, Palette> = {
|
||||
obsidian: {
|
||||
version: 102,
|
||||
|
||||
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 165 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 133 KiB |
@@ -2,6 +2,10 @@ import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Keybindings', () => {
|
||||
test('Should execute command', async ({ comfyPage }) => {
|
||||
await comfyPage.registerCommand('TestCommand', () => {
|
||||
|
||||
@@ -2,6 +2,10 @@ import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Copy Paste', () => {
|
||||
test('Can copy and paste node', async ({ comfyPage }) => {
|
||||
await comfyPage.clickEmptyLatentNode()
|
||||
|
||||
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 100 KiB |
@@ -1,8 +1,13 @@
|
||||
import { Locator, expect } from '@playwright/test'
|
||||
import type { Locator } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import type { Keybinding } from '../../src/schemas/keyBindingSchema'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Load workflow warning', () => {
|
||||
test('Should display a warning when loading a workflow with missing nodes', async ({
|
||||
comfyPage
|
||||
@@ -36,6 +41,10 @@ test('Does not report warning on undo/redo', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('missing/missing_nodes')
|
||||
await comfyPage.closeDialog()
|
||||
|
||||
// Wait for any async operations to complete after dialog closes
|
||||
await comfyPage.nextFrame()
|
||||
await comfyPage.page.waitForTimeout(100)
|
||||
|
||||
// Make a change to the graph
|
||||
await comfyPage.doubleClickCanvas()
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
||||
|
||||
@@ -2,6 +2,10 @@ import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('DOM Widget', () => {
|
||||
test('Collapsed multiline textarea is not visible', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('widgets/collapsed_multiline')
|
||||
|
||||
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 88 KiB |
@@ -2,6 +2,10 @@ import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Execution', () => {
|
||||
test('Report error on unconnected slot', async ({ comfyPage }) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
|
||||
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 95 KiB |
@@ -1,6 +1,6 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { SettingParams } from '../../src/types/settingTypes'
|
||||
import type { SettingParams } from '../../src/platform/settings/types'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.describe('Topbar commands', () => {
|
||||
@@ -247,7 +247,7 @@ test.describe('Topbar commands', () => {
|
||||
test.describe('Dialog', () => {
|
||||
test('Should allow showing a prompt dialog', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate(() => {
|
||||
window['app'].extensionManager.dialog
|
||||
void window['app'].extensionManager.dialog
|
||||
.prompt({
|
||||
title: 'Test Prompt',
|
||||
message: 'Test Prompt Message'
|
||||
@@ -267,7 +267,7 @@ test.describe('Topbar commands', () => {
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page.evaluate(() => {
|
||||
window['app'].extensionManager.dialog
|
||||
void window['app'].extensionManager.dialog
|
||||
.confirm({
|
||||
title: 'Test Confirm',
|
||||
message: 'Test Confirm Message'
|
||||
@@ -284,7 +284,7 @@ test.describe('Topbar commands', () => {
|
||||
test('Should allow dismissing a dialog', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate(() => {
|
||||
window['value'] = 'foo'
|
||||
window['app'].extensionManager.dialog
|
||||
void window['app'].extensionManager.dialog
|
||||
.confirm({
|
||||
title: 'Test Confirm',
|
||||
message: 'Test Confirm Message'
|
||||
|
||||
@@ -2,6 +2,10 @@ import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Feature Flags', () => {
|
||||
test('Client and server exchange feature flags on connection', async ({
|
||||
comfyPage
|
||||
|
||||
@@ -2,6 +2,10 @@ import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Graph', () => {
|
||||
// Should be able to fix link input slot index after swap the input order
|
||||
// Ref: https://github.com/Comfy-Org/ComfyUI_frontend/issues/3348
|
||||
|
||||
@@ -2,6 +2,10 @@ import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Graph Canvas Menu', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
// Set link render mode to spline to make sure it's not affected by other tests'
|
||||
|
||||
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 100 KiB |
@@ -1,8 +1,13 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { ComfyPage, comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Group Node', () => {
|
||||
test.describe('Node library sidebar', () => {
|
||||
const groupNodeName = 'DefautWorkflowGroupNode'
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Locator, expect } from '@playwright/test'
|
||||
import { Position } from '@vueuse/core'
|
||||
import type { Locator } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
import type { Position } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
type ComfyPage,
|
||||
comfyPageFixture as test,
|
||||
testComfySnapToGridGridSize
|
||||
} from '../fixtures/ComfyPage'
|
||||
import { type NodeReference } from '../fixtures/utils/litegraphUtils'
|
||||
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Item Interaction', () => {
|
||||
test('Can select/delete all items', async ({ comfyPage }) => {
|
||||
@@ -1012,6 +1017,8 @@ test.describe('Canvas Navigation', () => {
|
||||
test('Shift + mouse wheel should pan canvas horizontally', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.MouseWheelScroll', 'panning')
|
||||
|
||||
await comfyPage.page.click('canvas')
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 92 KiB |