Compare commits
299 Commits
node-group
...
flash-boar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb3c415065 | ||
|
|
d3ab23a532 | ||
|
|
08a6867c00 | ||
|
|
dbbe67dfcd | ||
|
|
40fa1d37bc | ||
|
|
0d6bc669f5 | ||
|
|
e4444d4074 | ||
|
|
cbf5dff633 | ||
|
|
9de8450deb | ||
|
|
3b0e3d635b | ||
|
|
d1a682bc01 | ||
|
|
01ffc9e4eb | ||
|
|
54e42178f7 | ||
|
|
25e5ab3a36 | ||
|
|
28dd6a2702 | ||
|
|
3b3df250cd | ||
|
|
6441a86619 | ||
|
|
79db202925 | ||
|
|
f7556e0015 | ||
|
|
141e64354c | ||
|
|
79452ce267 | ||
|
|
4d8a5eacba | ||
|
|
8f5a9a50aa | ||
|
|
7bc48c5074 | ||
|
|
e04ea07774 | ||
|
|
75af956279 | ||
|
|
434a2307a2 | ||
|
|
336b3caf9a | ||
|
|
c757fbaeb4 | ||
|
|
fd27b3d580 | ||
|
|
0658698a13 | ||
|
|
b2375a150c | ||
|
|
9ebb5b2a0c | ||
|
|
d6a5deccd8 | ||
|
|
3f4d11c63a | ||
|
|
44498739fc | ||
|
|
764ec9f7d0 | ||
|
|
e3234aa0aa | ||
|
|
df11c99393 | ||
|
|
317ea8b932 | ||
|
|
108884a304 | ||
|
|
9f1992ca59 | ||
|
|
39f245fd97 | ||
|
|
2d2fa5bfe9 | ||
|
|
bfb1b80cd7 | ||
|
|
0c8bfb4650 | ||
|
|
f69180cd84 | ||
|
|
2ac177caeb | ||
|
|
77d3e0c45e | ||
|
|
00dceb880a | ||
|
|
acea173ba0 | ||
|
|
bcedd5f4ed | ||
|
|
168ea05f81 | ||
|
|
370ad7a4f9 | ||
|
|
b9cfa70dcd | ||
|
|
c15201bfe2 | ||
|
|
0e2ce5e1ca | ||
|
|
5dc4a1b9cd | ||
|
|
acfb95f8d4 | ||
|
|
f2065777b5 | ||
|
|
b8b1e58172 | ||
|
|
2e86393378 | ||
|
|
530ca75dd0 | ||
|
|
f9c2db5908 | ||
|
|
166ad432f3 | ||
|
|
174754e646 | ||
|
|
43dd457bf5 | ||
|
|
625aa9bd11 | ||
|
|
f791322ddb | ||
|
|
89812ce7d0 | ||
|
|
c7aaa2a45d | ||
|
|
8bb785c5e4 | ||
|
|
a861a070d0 | ||
|
|
108e37deca | ||
|
|
9082903956 | ||
|
|
2cb9d4dd1c | ||
|
|
46f0733ae7 | ||
|
|
150b4341b2 | ||
|
|
054f8f6838 | ||
|
|
9fd73873b6 | ||
|
|
bfec9b692b | ||
|
|
29cd693335 | ||
|
|
4f6891a5ad | ||
|
|
ca2aee296a | ||
|
|
9017513979 | ||
|
|
8cfe814daa | ||
|
|
c901d5f659 | ||
|
|
1263fbb4ad | ||
|
|
8db101c1cb | ||
|
|
efe7843469 | ||
|
|
cfa46ebacb | ||
|
|
ab305059bc | ||
|
|
cd8c0d2865 | ||
|
|
6a9d309818 | ||
|
|
e3f226e483 | ||
|
|
8822edaf24 | ||
|
|
44b9a477b1 | ||
|
|
e4f8d4b8d0 | ||
|
|
a93f57eeb2 | ||
|
|
0c2879b6f4 | ||
|
|
d8d46f8cf6 | ||
|
|
1a06c91ed1 | ||
|
|
d4122a7510 | ||
|
|
b4c59ffae1 | ||
|
|
46428cbf7d | ||
|
|
2d759aa9e3 | ||
|
|
08e613e468 | ||
|
|
8052b2a02a | ||
|
|
0479b112c1 | ||
|
|
d7a0ee8703 | ||
|
|
9051ab8d7a | ||
|
|
aaca5191ab | ||
|
|
9707a30d0e | ||
|
|
e100041db4 | ||
|
|
21718d9da2 | ||
|
|
2b4c594b21 | ||
|
|
00abd885c9 | ||
|
|
550a9d04c5 | ||
|
|
eeb1c34ada | ||
|
|
83cc49a42b | ||
|
|
91a3d1228e | ||
|
|
3d59d478b6 | ||
|
|
4dd292252e | ||
|
|
0d307ff587 | ||
|
|
88a969df07 | ||
|
|
9e37738dc8 | ||
|
|
9b97abad57 | ||
|
|
67fcb4fed4 | ||
|
|
a914456827 | ||
|
|
340513e27f | ||
|
|
117c8be3a0 | ||
|
|
68f6d51ad2 | ||
|
|
2da23fd373 | ||
|
|
7ddcac88d7 | ||
|
|
40a817bb0f | ||
|
|
774ed4178f | ||
|
|
78e4161c51 | ||
|
|
dda9a72966 | ||
|
|
d7673af8f5 | ||
|
|
2b18949615 | ||
|
|
629ac63f06 | ||
|
|
1061620783 | ||
|
|
af7a6601e0 | ||
|
|
0e0c4b1302 | ||
|
|
fb170c9ee9 | ||
|
|
7ef304b381 | ||
|
|
50833341bb | ||
|
|
f5c5a95bdc | ||
|
|
b700cc1824 | ||
|
|
a6031ec2be | ||
|
|
a4d99d9d28 | ||
|
|
5a7465a907 | ||
|
|
6525ae7cf4 | ||
|
|
c6ef107111 | ||
|
|
3fbccd20ff | ||
|
|
fa0682d66e | ||
|
|
f7b613c6cb | ||
|
|
292af3fe3f | ||
|
|
2c12df12ab | ||
|
|
d0e99beaa7 | ||
|
|
6b64b74f6c | ||
|
|
0af4768dd2 | ||
|
|
5f850ddaa4 | ||
|
|
9fb3235df4 | ||
|
|
628facaa75 | ||
|
|
eb5a4b65ab | ||
|
|
98c197e8b1 | ||
|
|
90914a40ba | ||
|
|
821816955f | ||
|
|
b4121008cd | ||
|
|
3730c2b36f | ||
|
|
77be5ac514 | ||
|
|
83759b9a4a | ||
|
|
b8f187713e | ||
|
|
b8088ad782 | ||
|
|
a37671b154 | ||
|
|
57bc7ad312 | ||
|
|
5f59fbdead | ||
|
|
4eed9c7e53 | ||
|
|
ee6197785a | ||
|
|
f5d6ad07e8 | ||
|
|
45207dabbc | ||
|
|
4699360147 | ||
|
|
8ef3b87e59 | ||
|
|
d3a6baf8cd | ||
|
|
b4d679d31f | ||
|
|
7afc1baf7d | ||
|
|
7bdad335ca | ||
|
|
94065b6c21 | ||
|
|
a205a5cca5 | ||
|
|
788d6cf514 | ||
|
|
766710cf37 | ||
|
|
e019277ba0 | ||
|
|
97e5c9c6d2 | ||
|
|
52e42b5339 | ||
|
|
c42cdf5cd9 | ||
|
|
c07ec659a7 | ||
|
|
cbcbeab9d9 | ||
|
|
bf9d2affb4 | ||
|
|
2c8c8718e9 | ||
|
|
475e38ddb4 | ||
|
|
430f051c64 | ||
|
|
29b5f606b0 | ||
|
|
55d63a8aef | ||
|
|
99009a18f7 | ||
|
|
e3ab0e4d68 | ||
|
|
0e1ae41c0c | ||
|
|
f12d4a2d6f | ||
|
|
7bd8527bca | ||
|
|
cb356d50b8 | ||
|
|
d2e9943e79 | ||
|
|
27e4bd2592 | ||
|
|
82e0c3a8b6 | ||
|
|
2852720b2c | ||
|
|
38b8a68e50 | ||
|
|
44321e4692 | ||
|
|
e992bd6571 | ||
|
|
e971ba31e0 | ||
|
|
28b163cdd5 | ||
|
|
652125de1f | ||
|
|
c5d153cf16 | ||
|
|
9459f599b6 | ||
|
|
326839db88 | ||
|
|
30fdc70218 | ||
|
|
9c42c31968 | ||
|
|
44aa1bf8c3 | ||
|
|
caad27e28d | ||
|
|
e8136ff0ae | ||
|
|
157475cb2e | ||
|
|
93dc50a95a | ||
|
|
3f787e2dbf | ||
|
|
b54e270b10 | ||
|
|
0ab1d974c0 | ||
|
|
95ff01a67b | ||
|
|
6f05ce6cc2 | ||
|
|
b8bef57522 | ||
|
|
46500bf3dd | ||
|
|
1bcc00cd33 | ||
|
|
08d2322817 | ||
|
|
8cfc1c4682 | ||
|
|
c7bce87b8d | ||
|
|
8f6b594a9f | ||
|
|
1c9b300396 | ||
|
|
cd5283c4b7 | ||
|
|
0b69d3cbfe | ||
|
|
8257e848c6 | ||
|
|
a07b7693b6 | ||
|
|
26ddf69451 | ||
|
|
ed6ece2099 | ||
|
|
b42516d39c | ||
|
|
ef24efe5a3 | ||
|
|
34c267c755 | ||
|
|
8b9f0ddd1d | ||
|
|
af658b7792 | ||
|
|
9c53bbd53d | ||
|
|
f9be20fa78 | ||
|
|
87fc7a2c5d | ||
|
|
1f266e826e | ||
|
|
911adfe9f8 | ||
|
|
654d72b4cc | ||
|
|
a1ed67fc74 | ||
|
|
78bc635518 | ||
|
|
f49ec175e9 | ||
|
|
e4c60e7e18 | ||
|
|
37cb2cb0a5 | ||
|
|
141825e988 | ||
|
|
78f43b1e06 | ||
|
|
a6105eb8c7 | ||
|
|
79ed598d5d | ||
|
|
816574e0ab | ||
|
|
1a4e77a3ab | ||
|
|
de570712df | ||
|
|
44612e8f97 | ||
|
|
3df911c1bf | ||
|
|
af26b9ad6d | ||
|
|
d503873980 | ||
|
|
842a9f74fc | ||
|
|
29551a36b3 | ||
|
|
d6e5c8950c | ||
|
|
ad1c1ce9c2 | ||
|
|
cb9d2c6bae | ||
|
|
7fd41eeaba | ||
|
|
79fee6ac72 | ||
|
|
edd58cd153 | ||
|
|
e153508955 | ||
|
|
237fca0bf1 | ||
|
|
65542b885a | ||
|
|
f739f704af | ||
|
|
37abdbe35d | ||
|
|
ff445f5c95 | ||
|
|
84b652a281 | ||
|
|
184291d21b | ||
|
|
d7fb25a36a | ||
|
|
c039a60fcc | ||
|
|
3b6108c26e | ||
|
|
49bb247526 | ||
|
|
dd005f5fa5 | ||
|
|
bf90b458d3 | ||
|
|
7e78c5b1dc |
43
.cursorrules
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Vue 3 Composition API .cursorrules
|
||||||
|
|
||||||
|
// Vue 3 Composition API best practices
|
||||||
|
const vue3CompositionApiBestPractices = [
|
||||||
|
"Use setup() function for component logic",
|
||||||
|
"Utilize ref and reactive for reactive state",
|
||||||
|
"Implement computed properties with computed()",
|
||||||
|
"Use watch and watchEffect for side effects",
|
||||||
|
"Implement lifecycle hooks with onMounted, onUpdated, etc.",
|
||||||
|
"Utilize provide/inject for dependency injection",
|
||||||
|
]
|
||||||
|
|
||||||
|
// Folder structure
|
||||||
|
const folderStructure = `
|
||||||
|
src/
|
||||||
|
components/
|
||||||
|
constants/
|
||||||
|
composables/
|
||||||
|
views/
|
||||||
|
stores/
|
||||||
|
services/
|
||||||
|
App.vue
|
||||||
|
main.ts
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Tailwind CSS best practices
|
||||||
|
const tailwindCssBestPractices = [
|
||||||
|
"Use Tailwind CSS for styling",
|
||||||
|
"Implement responsive design with Tailwind CSS",
|
||||||
|
]
|
||||||
|
|
||||||
|
// Additional instructions
|
||||||
|
const additionalInstructions = `
|
||||||
|
1. Leverage VueUse functions for performance-enhancing styles
|
||||||
|
2. Use lodash for utility functions
|
||||||
|
3. Use TypeScript for type safety
|
||||||
|
4. Implement proper props and emits definitions
|
||||||
|
5. Utilize Vue 3's Teleport component when needed
|
||||||
|
6. Use Suspense for async components
|
||||||
|
7. Implement proper error handling
|
||||||
|
8. Follow Vue 3 style guide and naming conventions
|
||||||
|
9. Use Vite for fast development and building
|
||||||
|
`;
|
||||||
11
.github/workflows/eslint.yaml
vendored
@@ -2,10 +2,7 @@ name: ESLint
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches: [ main, master, dev* ]
|
||||||
- main
|
|
||||||
- master
|
|
||||||
- 'dev*'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
eslint:
|
eslint:
|
||||||
@@ -13,8 +10,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: 'lts/*'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run lint
|
- run: npm run lint
|
||||||
|
|||||||
6
.github/workflows/format.yaml
vendored
@@ -12,12 +12,12 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: 'lts/*'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Run Prettier check
|
- name: Run Prettier check
|
||||||
run: npm run format:check
|
run: npm run format:check
|
||||||
|
|||||||
23
.github/workflows/i18n-custom-nodes.yaml
vendored
@@ -1,4 +1,5 @@
|
|||||||
name: Update Locales for given custom node repository
|
name: Update Locales for given custom node repository
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
@@ -23,27 +24,27 @@ jobs:
|
|||||||
- name: Checkout ComfyUI
|
- name: Checkout ComfyUI
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'comfyanonymous/ComfyUI'
|
repository: comfyanonymous/ComfyUI
|
||||||
path: 'ComfyUI'
|
path: ComfyUI
|
||||||
ref: master
|
ref: master
|
||||||
- name: Checkout ComfyUI_frontend
|
- name: Checkout ComfyUI_frontend
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'Comfy-Org/ComfyUI_frontend'
|
repository: Comfy-Org/ComfyUI_frontend
|
||||||
path: 'ComfyUI_frontend'
|
path: ComfyUI_frontend
|
||||||
- name: Checkout ComfyUI_devtools
|
- name: Checkout ComfyUI_devtools
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'Comfy-Org/ComfyUI_devtools'
|
repository: Comfy-Org/ComfyUI_devtools
|
||||||
path: 'ComfyUI/custom_nodes/ComfyUI_devtools'
|
path: ComfyUI/custom_nodes/ComfyUI_devtools
|
||||||
- name: Checkout custom node repository
|
- name: Checkout custom node repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: ${{ inputs.owner }}/${{ inputs.repository }}
|
repository: ${{ inputs.owner }}/${{ inputs.repository }}
|
||||||
path: 'ComfyUI/custom_nodes/${{ inputs.repository }}'
|
path: 'ComfyUI/custom_nodes/${{ inputs.repository }}'
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: 'lts/*'
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
@@ -53,14 +54,12 @@ jobs:
|
|||||||
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
|
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
pip install wait-for-it
|
pip install wait-for-it
|
||||||
shell: bash
|
|
||||||
working-directory: ComfyUI
|
working-directory: ComfyUI
|
||||||
- name: Install custom node requirements
|
- name: Install custom node requirements
|
||||||
run: |
|
run: |
|
||||||
if [ -f "requirements.txt" ]; then
|
if [ -f "requirements.txt" ]; then
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
fi
|
fi
|
||||||
shell: bash
|
|
||||||
working-directory: ComfyUI/custom_nodes/${{ inputs.repository }}
|
working-directory: ComfyUI/custom_nodes/${{ inputs.repository }}
|
||||||
- name: Build & Install ComfyUI_frontend
|
- name: Build & Install ComfyUI_frontend
|
||||||
run: |
|
run: |
|
||||||
@@ -68,14 +67,12 @@ jobs:
|
|||||||
npm run build
|
npm run build
|
||||||
rm -rf ../ComfyUI/web/*
|
rm -rf ../ComfyUI/web/*
|
||||||
mv dist/* ../ComfyUI/web/
|
mv dist/* ../ComfyUI/web/
|
||||||
shell: bash
|
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
- name: Start ComfyUI server
|
- name: Start ComfyUI server
|
||||||
run: |
|
run: |
|
||||||
python main.py --cpu --multi-user &
|
python main.py --cpu --multi-user &
|
||||||
wait-for-it --service 127.0.0.1:8188 -t 600
|
wait-for-it --service 127.0.0.1:8188 -t 600
|
||||||
working-directory: ComfyUI
|
working-directory: ComfyUI
|
||||||
shell: bash
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install chromium --with-deps
|
run: npx playwright install chromium --with-deps
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
@@ -153,7 +150,7 @@ jobs:
|
|||||||
echo "Pushing changes to ${{ inputs.fork_owner }}/${{ inputs.repository }}"
|
echo "Pushing changes to ${{ inputs.fork_owner }}/${{ inputs.repository }}"
|
||||||
git push -f git@github.com:${{ inputs.fork_owner }}/${{ inputs.repository }}.git update-locales
|
git push -f git@github.com:${{ inputs.fork_owner }}/${{ inputs.repository }}.git update-locales
|
||||||
|
|
||||||
- name: Create PR
|
- name: Create PR
|
||||||
working-directory: ComfyUI/custom_nodes/${{ inputs.repository }}
|
working-directory: ComfyUI/custom_nodes/${{ inputs.repository }}
|
||||||
run: |
|
run: |
|
||||||
# Create PR using gh cli
|
# Create PR using gh cli
|
||||||
|
|||||||
7
.github/workflows/i18n-node-defs.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
update-locales:
|
update-locales:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
|
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.2
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install chromium --with-deps
|
run: npx playwright install chromium --with-deps
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
@@ -42,6 +42,7 @@ jobs:
|
|||||||
Automated PR to update locales for node definitions
|
Automated PR to update locales for node definitions
|
||||||
|
|
||||||
This PR was created automatically by the frontend update workflow.
|
This PR was created automatically by the frontend update workflow.
|
||||||
branch: update-locales-node-defs-{{ github.event.inputs.trigger_type }}-{{ github.run_id }}
|
branch: update-locales-node-defs-${{ github.event.inputs.trigger_type }}-${{ github.run_id }}
|
||||||
base: main
|
base: main
|
||||||
labels: dependencies
|
labels: dependencies
|
||||||
|
path: ComfyUI_frontend
|
||||||
3
.github/workflows/i18n.yaml
vendored
@@ -1,4 +1,5 @@
|
|||||||
name: Update Locales
|
name: Update Locales
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main, master, dev* ]
|
branches: [ main, master, dev* ]
|
||||||
@@ -9,7 +10,7 @@ jobs:
|
|||||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
|
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.2
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install chromium --with-deps
|
run: npx playwright install chromium --with-deps
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
|
|||||||
25
.github/workflows/release.yaml
vendored
@@ -2,12 +2,10 @@ name: Create Release Draft
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [closed]
|
types: [ closed ]
|
||||||
branches:
|
branches: [ main, master ]
|
||||||
- main
|
|
||||||
- master
|
|
||||||
paths:
|
paths:
|
||||||
- "package.json"
|
- 'package.json'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
draft_release:
|
draft_release:
|
||||||
@@ -18,9 +16,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: 'lts/*'
|
||||||
- name: Get current version
|
- name: Get current version
|
||||||
id: current_version
|
id: current_version
|
||||||
run: echo ::set-output name=version::$(node -p "require('./package.json').version")
|
run: echo ::set-output name=version::$(node -p "require('./package.json').version")
|
||||||
@@ -40,9 +38,10 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
dist.zip
|
dist.zip
|
||||||
tag_name: v${{ steps.current_version.outputs.version }}
|
tag_name: v${{ steps.current_version.outputs.version }}
|
||||||
draft: true
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
make_latest: "true"
|
make_latest: true
|
||||||
|
generate_release_notes: true
|
||||||
publish_types:
|
publish_types:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: >
|
if: >
|
||||||
@@ -50,14 +49,14 @@ jobs:
|
|||||||
contains(github.event.pull_request.labels.*.name, 'Release')
|
contains(github.event.pull_request.labels.*.name, 'Release')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: 'lts/*'
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: https://registry.npmjs.org
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build:types
|
- run: npm run build:types
|
||||||
- name: Publish package
|
- name: Publish package
|
||||||
run: npm publish --access public
|
run: npm publish --access public
|
||||||
working-directory: ./dist
|
working-directory: dist
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|||||||
3
.github/workflows/test-browser-exp.yaml
vendored
@@ -1,5 +1,4 @@
|
|||||||
# Setting test expectation screenshots for Playwright
|
# Setting test expectation screenshots for Playwright
|
||||||
|
|
||||||
name: Update Playwright Expectations
|
name: Update Playwright Expectations
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -11,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.label.name == 'New Browser Test Expectations'
|
if: github.event.label.name == 'New Browser Test Expectations'
|
||||||
steps:
|
steps:
|
||||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
|
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.2
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install chromium --with-deps
|
run: npx playwright install chromium --with-deps
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
|
|||||||
25
.github/workflows/test-ui.yaml
vendored
@@ -2,20 +2,17 @@ name: Tests CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [ main, master ]
|
||||||
- main
|
|
||||||
- master
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches: [ main, master, dev* ]
|
||||||
- main
|
|
||||||
- master
|
|
||||||
- 'dev*'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
jest-tests:
|
jest-tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
|
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.2
|
||||||
|
with:
|
||||||
|
devtools_ref: 7b81139e904519db8e5481899ef36bbb4393cb6b
|
||||||
- name: Run Jest tests
|
- name: Run Jest tests
|
||||||
run: |
|
run: |
|
||||||
npm run test:generate
|
npm run test:generate
|
||||||
@@ -25,7 +22,9 @@ jobs:
|
|||||||
playwright-tests-chromium:
|
playwright-tests-chromium:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
|
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.2
|
||||||
|
with:
|
||||||
|
devtools_ref: 7b81139e904519db8e5481899ef36bbb4393cb6b
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install chromium --with-deps
|
run: npx playwright install chromium --with-deps
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
@@ -42,7 +41,9 @@ jobs:
|
|||||||
playwright-tests-chromium-2x:
|
playwright-tests-chromium-2x:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
|
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.2
|
||||||
|
with:
|
||||||
|
devtools_ref: 7b81139e904519db8e5481899ef36bbb4393cb6b
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install chromium --with-deps
|
run: npx playwright install chromium --with-deps
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
@@ -59,7 +60,9 @@ jobs:
|
|||||||
playwright-tests-mobile-chrome:
|
playwright-tests-mobile-chrome:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
|
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.2
|
||||||
|
with:
|
||||||
|
devtools_ref: 7b81139e904519db8e5481899ef36bbb4393cb6b
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install chromium --with-deps
|
run: npx playwright install chromium --with-deps
|
||||||
working-directory: ComfyUI_frontend
|
working-directory: ComfyUI_frontend
|
||||||
|
|||||||
14
.github/workflows/vitest.yaml
vendored
@@ -2,15 +2,9 @@ name: Vitest Tests
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [ main, master, dev* ]
|
||||||
- main
|
|
||||||
- master
|
|
||||||
- 'dev*'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches: [ main, master, dev* ]
|
||||||
- main
|
|
||||||
- master
|
|
||||||
- 'dev*'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@@ -20,9 +14,9 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: 'lts/*'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
3
.gitignore
vendored
@@ -16,6 +16,9 @@ dist-ssr
|
|||||||
.vscode/*
|
.vscode/*
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
!.vscode/tailwind.json
|
||||||
|
!.vscode/settings.json.default
|
||||||
|
!.vscode/launch.json.default
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.suo
|
*.suo
|
||||||
|
|||||||
16
.vscode/launch.json.default
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Chrome on frontend dev",
|
||||||
|
"url": "http://localhost:5173",
|
||||||
|
"webRoot": "${workspaceFolder}/src",
|
||||||
|
"sourceMaps": true,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
5
.vscode/settings.json.default
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"css.customData": [
|
||||||
|
".vscode/tailwind.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
55
.vscode/tailwind.json
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"version": 1.1,
|
||||||
|
"atDirectives": [
|
||||||
|
{
|
||||||
|
"name": "@tailwind",
|
||||||
|
"description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@apply",
|
||||||
|
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#apply"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@responsive",
|
||||||
|
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@screen",
|
||||||
|
"description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#screen"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@variants",
|
||||||
|
"description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n",
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"name": "Tailwind Documentation",
|
||||||
|
"url": "https://tailwindcss.com/docs/functions-and-directives#variants"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
37
browser_tests/assets/collapsed_multiline.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 1,
|
||||||
|
"last_link_id": 0,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [20, 50],
|
||||||
|
"size": [400, 200],
|
||||||
|
"flags": { "collapsed": true },
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": null,
|
||||||
|
"localized_name": "clip"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": null,
|
||||||
|
"localized_name": "CONDITIONING"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {},
|
||||||
|
"widgets_values": ["Should not be displayed."]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
{ "name": "VAE", "type": "VAE", "links": [8], "slot_index": 2 }
|
{ "name": "VAE", "type": "VAE", "links": [8], "slot_index": 2 }
|
||||||
],
|
],
|
||||||
"properties": {},
|
"properties": {},
|
||||||
"widgets_values": ["v1-5-pruned-emaonly.ckpt"]
|
"widgets_values": ["v1-5-pruned-emaonly-fp16.safetensors"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [
|
"links": [
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 19,
|
||||||
|
"last_link_id": 14,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 19,
|
||||||
|
"type": "workflow>two_VAE_decode",
|
||||||
|
"pos": [
|
||||||
|
1368.800048828125,
|
||||||
|
768.7999877929688
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
418.1999816894531,
|
||||||
|
86
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "samples",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VAE",
|
||||||
|
"link": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "VAEDecode IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {
|
||||||
|
"ds": {
|
||||||
|
"scale": 1,
|
||||||
|
"offset": [
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_versions": {},
|
||||||
|
"ue_links": [],
|
||||||
|
"groupNodes": {
|
||||||
|
"two_VAE_decode": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": -1,
|
||||||
|
"type": "VAEDecode",
|
||||||
|
"pos": [
|
||||||
|
1368.800048828125,
|
||||||
|
768.7999877929688
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
210,
|
||||||
|
46
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "samples",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": null,
|
||||||
|
"localized_name": "samples"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VAE",
|
||||||
|
"link": null,
|
||||||
|
"localized_name": "vae"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": null,
|
||||||
|
"localized_name": "IMAGE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAEDecode"
|
||||||
|
},
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": -1,
|
||||||
|
"type": "VAEDecode",
|
||||||
|
"pos": [
|
||||||
|
1368.800048828125,
|
||||||
|
873.7999877929688
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
210,
|
||||||
|
46
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 1,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "samples",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": null,
|
||||||
|
"localized_name": "samples"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VAE",
|
||||||
|
"link": null,
|
||||||
|
"localized_name": "vae"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": null,
|
||||||
|
"localized_name": "IMAGE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAEDecode"
|
||||||
|
},
|
||||||
|
"index": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"external": [],
|
||||||
|
"config": {
|
||||||
|
"1": {
|
||||||
|
"input": {
|
||||||
|
"samples": {
|
||||||
|
"visible": false
|
||||||
|
},
|
||||||
|
"vae": {
|
||||||
|
"visible": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
BIN
browser_tests/assets/image32x32.webp
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
browser_tests/assets/image64x64.webp
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
@@ -18,7 +18,7 @@
|
|||||||
{
|
{
|
||||||
"name": "fake_model.safetensors",
|
"name": "fake_model.safetensors",
|
||||||
"url": "http://localhost:8188/api/devtools/fake_model.safetensors",
|
"url": "http://localhost:8188/api/devtools/fake_model.safetensors",
|
||||||
"directory": "clip"
|
"directory": "text_encoders"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version": 0.4
|
"version": 0.4
|
||||||
|
|||||||
63
browser_tests/assets/missing_nodes_converted_widget.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 1,
|
||||||
|
"last_link_id": 0,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "UNKNOWN NODE",
|
||||||
|
"pos": [
|
||||||
|
48,
|
||||||
|
86
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 358.80780029296875,
|
||||||
|
"1": 314.7989501953125
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "image",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": null,
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": "STRING",
|
||||||
|
"link": null,
|
||||||
|
"slot_index": 1,
|
||||||
|
"widget": {
|
||||||
|
"name": "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "STRING",
|
||||||
|
"type": "STRING",
|
||||||
|
"links": [],
|
||||||
|
"slot_index": 0,
|
||||||
|
"shape": 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "UNKNOWN NODE"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"wd-v1-4-moat-tagger-v2",
|
||||||
|
0.35,
|
||||||
|
0.85,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
48
browser_tests/assets/remote_widget.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 15,
|
||||||
|
"last_link_id": 10,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"type": "DevToolsRemoteWidgetNode",
|
||||||
|
"pos": [
|
||||||
|
495,
|
||||||
|
735
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
315,
|
||||||
|
58
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "STRING",
|
||||||
|
"type": "STRING",
|
||||||
|
"links": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "DevToolsRemoteWidgetNode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"v1-5-pruned-emaonly-fp16.safetensors"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {
|
||||||
|
"ds": {
|
||||||
|
"scale": 0.8008869919566275,
|
||||||
|
"offset": [
|
||||||
|
538.9801226576359,
|
||||||
|
-55.24554581806672
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
35
browser_tests/assets/widgets/boolean_widget.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 11,
|
||||||
|
"last_link_id": 9,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"type": "DevToolsNodeWithBooleanInput",
|
||||||
|
"pos": [
|
||||||
|
0,
|
||||||
|
30
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
315,
|
||||||
|
58
|
||||||
|
],
|
||||||
|
"flags": {
|
||||||
|
"collapsed": false
|
||||||
|
},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "DevToolsNodeWithBooleanInput"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
43
browser_tests/assets/widgets/seed_widget.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 10,
|
||||||
|
"last_link_id": 9,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"type": "DevToolsNodeWithSeedInput",
|
||||||
|
"pos": [
|
||||||
|
20,
|
||||||
|
50
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
315,
|
||||||
|
82
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 1,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "DevToolsNodeWithSeedInput"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
0,
|
||||||
|
"randomize"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {
|
||||||
|
"ds": {
|
||||||
|
"scale": 1,
|
||||||
|
"offset": [
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
@@ -152,9 +152,10 @@ test.describe('Color Palette', () => {
|
|||||||
// doesn't update the store immediately.
|
// doesn't update the store immediately.
|
||||||
await comfyPage.setup()
|
await comfyPage.setup()
|
||||||
|
|
||||||
|
await comfyPage.loadWorkflow('every_node_color')
|
||||||
await comfyPage.setSetting('Comfy.ColorPalette', 'obsidian_dark')
|
await comfyPage.setSetting('Comfy.ColorPalette', 'obsidian_dark')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'custom-color-palette-obsidian-dark.png'
|
'custom-color-palette-obsidian-dark-all-colors.png'
|
||||||
)
|
)
|
||||||
await comfyPage.setSetting('Comfy.ColorPalette', 'light_red')
|
await comfyPage.setSetting('Comfy.ColorPalette', 'light_red')
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
@@ -232,7 +233,7 @@ test.describe('Node Color Adjustments', () => {
|
|||||||
const workflow = await comfyPage.page.evaluate(() => {
|
const workflow = await comfyPage.page.evaluate(() => {
|
||||||
return localStorage.getItem('workflow')
|
return localStorage.getItem('workflow')
|
||||||
})
|
})
|
||||||
for (const node of JSON.parse(workflow).nodes) {
|
for (const node of JSON.parse(workflow ?? '{}').nodes) {
|
||||||
if (node.bgcolor) expect(node.bgcolor).not.toMatch(/hsla/)
|
if (node.bgcolor) expect(node.bgcolor).not.toMatch(/hsla/)
|
||||||
if (node.color) expect(node.color).not.toMatch(/hsla/)
|
if (node.color) expect(node.color).not.toMatch(/hsla/)
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 102 KiB |
@@ -1,4 +1,4 @@
|
|||||||
import { expect } from '@playwright/test'
|
import { Locator, expect } from '@playwright/test'
|
||||||
|
|
||||||
import { Keybinding } from '../src/types/keyBindingTypes'
|
import { Keybinding } from '../src/types/keyBindingTypes'
|
||||||
import { comfyPageFixture as test } from './fixtures/ComfyPage'
|
import { comfyPageFixture as test } from './fixtures/ComfyPage'
|
||||||
@@ -66,9 +66,71 @@ test.describe('Missing models warning', () => {
|
|||||||
}, comfyPage.url)
|
}, comfyPage.url)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Should display a warning when missing models are found', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.loadWorkflow('missing_models')
|
||||||
|
|
||||||
|
const missingModelsWarning = comfyPage.page.locator('.comfy-missing-models')
|
||||||
|
await expect(missingModelsWarning).toBeVisible()
|
||||||
|
|
||||||
|
const downloadButton = missingModelsWarning.getByLabel('Download')
|
||||||
|
await expect(downloadButton).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Should not display a warning when no missing models are found', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
const modelFoldersRes = {
|
||||||
|
status: 200,
|
||||||
|
body: JSON.stringify([
|
||||||
|
{
|
||||||
|
name: 'text_encoders',
|
||||||
|
folders: ['ComfyUI/models/text_encoders']
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
comfyPage.page.route(
|
||||||
|
'**/api/experiment/models',
|
||||||
|
(route) => route.fulfill(modelFoldersRes),
|
||||||
|
{ times: 1 }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reload page to trigger indexing of model folders
|
||||||
|
await comfyPage.setup()
|
||||||
|
|
||||||
|
const clipModelsRes = {
|
||||||
|
status: 200,
|
||||||
|
body: JSON.stringify([
|
||||||
|
{
|
||||||
|
name: 'fake_model.safetensors',
|
||||||
|
pathIndex: 0
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
comfyPage.page.route(
|
||||||
|
'**/api/experiment/models/text_encoders',
|
||||||
|
(route) => route.fulfill(clipModelsRes),
|
||||||
|
{ times: 1 }
|
||||||
|
)
|
||||||
|
|
||||||
|
await comfyPage.loadWorkflow('missing_models')
|
||||||
|
|
||||||
|
const missingModelsWarning = comfyPage.page.locator('.comfy-missing-models')
|
||||||
|
await expect(missingModelsWarning).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should show on tutorial workflow', async ({ comfyPage }) => {
|
||||||
|
await comfyPage.setSetting('Comfy.TutorialCompleted', false)
|
||||||
|
await comfyPage.setup({ clearStorage: true })
|
||||||
|
const missingModelsWarning = comfyPage.page.locator('.comfy-missing-models')
|
||||||
|
await expect(missingModelsWarning).toBeVisible()
|
||||||
|
expect(await comfyPage.getSetting('Comfy.TutorialCompleted')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
// Flaky test after parallelization
|
// Flaky test after parallelization
|
||||||
// https://github.com/Comfy-Org/ComfyUI_frontend/pull/1400
|
// https://github.com/Comfy-Org/ComfyUI_frontend/pull/1400
|
||||||
test.skip('Should display a warning when missing models are found', async ({
|
test.skip('Should download missing model when clicking download button', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
// The fake_model.safetensors is served by
|
// The fake_model.safetensors is served by
|
||||||
@@ -86,6 +148,49 @@ test.describe('Missing models warning', () => {
|
|||||||
const download = await downloadPromise
|
const download = await downloadPromise
|
||||||
expect(download.suggestedFilename()).toBe('fake_model.safetensors')
|
expect(download.suggestedFilename()).toBe('fake_model.safetensors')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.describe('Do not show again checkbox', () => {
|
||||||
|
let checkbox: Locator
|
||||||
|
let closeButton: Locator
|
||||||
|
|
||||||
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
|
await comfyPage.setSetting(
|
||||||
|
'Comfy.Workflow.ShowMissingModelsWarning',
|
||||||
|
true
|
||||||
|
)
|
||||||
|
await comfyPage.loadWorkflow('missing_models')
|
||||||
|
|
||||||
|
checkbox = comfyPage.page.getByLabel("Don't show this again")
|
||||||
|
closeButton = comfyPage.page.getByLabel('Close')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Should disable warning dialog when checkbox is checked', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await checkbox.click()
|
||||||
|
const changeSettingPromise = comfyPage.page.waitForRequest(
|
||||||
|
'**/api/settings/Comfy.Workflow.ShowMissingModelsWarning'
|
||||||
|
)
|
||||||
|
await closeButton.click()
|
||||||
|
await changeSettingPromise
|
||||||
|
|
||||||
|
const settingValue = await comfyPage.getSetting(
|
||||||
|
'Comfy.Workflow.ShowMissingModelsWarning'
|
||||||
|
)
|
||||||
|
expect(settingValue).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Should keep warning dialog enabled when checkbox is unchecked', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await closeButton.click()
|
||||||
|
|
||||||
|
const settingValue = await comfyPage.getSetting(
|
||||||
|
'Comfy.Workflow.ShowMissingModelsWarning'
|
||||||
|
)
|
||||||
|
expect(settingValue).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Settings', () => {
|
test.describe('Settings', () => {
|
||||||
@@ -148,7 +253,7 @@ test.describe('Settings', () => {
|
|||||||
|
|
||||||
// Save keybinding
|
// Save keybinding
|
||||||
const saveButton = comfyPage.page
|
const saveButton = comfyPage.page
|
||||||
.getByLabel('Comfy.NewBlankWorkflow')
|
.getByLabel('New Blank Workflow')
|
||||||
.getByLabel('Save')
|
.getByLabel('Save')
|
||||||
await saveButton.click()
|
await saveButton.click()
|
||||||
|
|
||||||
@@ -165,3 +270,37 @@ test.describe('Settings', () => {
|
|||||||
expect(request.postData()).toContain(JSON.stringify(expectedSetting))
|
expect(request.postData()).toContain(JSON.stringify(expectedSetting))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.describe('Feedback dialog', () => {
|
||||||
|
test('Should open from topmenu help command', async ({ comfyPage }) => {
|
||||||
|
// Open feedback dialog from top menu
|
||||||
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
|
await comfyPage.menu.topbar.triggerTopbarCommand(['Help', 'Feedback'])
|
||||||
|
|
||||||
|
// Verify feedback dialog content is visible
|
||||||
|
const feedbackHeader = comfyPage.page.getByRole('heading', {
|
||||||
|
name: 'Feedback'
|
||||||
|
})
|
||||||
|
await expect(feedbackHeader).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Should close when close button clicked', async ({ comfyPage }) => {
|
||||||
|
// Open feedback dialog
|
||||||
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
|
await comfyPage.menu.topbar.triggerTopbarCommand(['Help', 'Feedback'])
|
||||||
|
|
||||||
|
const feedbackHeader = comfyPage.page.getByRole('heading', {
|
||||||
|
name: 'Feedback'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Close feedback dialog
|
||||||
|
await comfyPage.page
|
||||||
|
.getByLabel('', { exact: true })
|
||||||
|
.getByLabel('Close')
|
||||||
|
.click()
|
||||||
|
await feedbackHeader.waitFor({ state: 'hidden' })
|
||||||
|
|
||||||
|
// Verify dialog is closed
|
||||||
|
await expect(feedbackHeader).not.toBeVisible()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
27
browser_tests/domWidget.spec.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
|
import { comfyPageFixture as test } from './fixtures/ComfyPage'
|
||||||
|
|
||||||
|
test.describe('DOM Widget', () => {
|
||||||
|
test('Collapsed multiline textarea is not visible', async ({ comfyPage }) => {
|
||||||
|
await comfyPage.loadWorkflow('collapsed_multiline')
|
||||||
|
|
||||||
|
expect(comfyPage.page.locator('.comfy-multiline-input')).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Multiline textarea correctly collapses', async ({ comfyPage }) => {
|
||||||
|
const multilineTextAreas = comfyPage.page.locator('.comfy-multiline-input')
|
||||||
|
const firstMultiline = multilineTextAreas.first()
|
||||||
|
const lastMultiline = multilineTextAreas.last()
|
||||||
|
|
||||||
|
await expect(firstMultiline).toBeVisible()
|
||||||
|
await expect(lastMultiline).toBeVisible()
|
||||||
|
|
||||||
|
const nodes = await comfyPage.getNodeRefsByType('CLIPTextEncode')
|
||||||
|
for (const node of nodes) {
|
||||||
|
await node.click('collapse')
|
||||||
|
}
|
||||||
|
await expect(firstMultiline).not.toBeVisible()
|
||||||
|
await expect(lastMultiline).not.toBeVisible()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
|
import { SettingParams } from '../src/types/settingTypes'
|
||||||
import { comfyPageFixture as test } from './fixtures/ComfyPage'
|
import { comfyPageFixture as test } from './fixtures/ComfyPage'
|
||||||
|
|
||||||
test.describe('Topbar commands', () => {
|
test.describe('Topbar commands', () => {
|
||||||
@@ -134,6 +135,90 @@ test.describe('Topbar commands', () => {
|
|||||||
expect(await comfyPage.getSetting('Comfy.TestSetting')).toBe(true)
|
expect(await comfyPage.getSetting('Comfy.TestSetting')).toBe(true)
|
||||||
expect(await comfyPage.page.evaluate(() => window['changeCount'])).toBe(2)
|
expect(await comfyPage.page.evaluate(() => window['changeCount'])).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.describe('Passing through attrs to setting components', () => {
|
||||||
|
const testCases: Array<{
|
||||||
|
config: Partial<SettingParams>
|
||||||
|
selector: string
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
type: 'boolean',
|
||||||
|
defaultValue: true
|
||||||
|
},
|
||||||
|
selector: '.p-toggleswitch.p-component'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
type: 'number',
|
||||||
|
defaultValue: 10
|
||||||
|
},
|
||||||
|
selector: '.p-inputnumber input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
type: 'slider',
|
||||||
|
defaultValue: 10
|
||||||
|
},
|
||||||
|
selector: '.p-slider.p-component'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
type: 'combo',
|
||||||
|
defaultValue: 'foo',
|
||||||
|
options: ['foo', 'bar', 'baz']
|
||||||
|
},
|
||||||
|
selector: '.p-select.p-component'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: 'Hello'
|
||||||
|
},
|
||||||
|
selector: '.p-inputtext'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
type: 'color',
|
||||||
|
defaultValue: '#000000'
|
||||||
|
},
|
||||||
|
selector: '.p-colorpicker-preview'
|
||||||
|
}
|
||||||
|
] as const
|
||||||
|
|
||||||
|
for (const { config, selector } of testCases) {
|
||||||
|
test(`${config.type} component should respect disabled attr`, async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.page.evaluate((config) => {
|
||||||
|
window['app'].registerExtension({
|
||||||
|
name: 'TestExtension1',
|
||||||
|
settings: [
|
||||||
|
{
|
||||||
|
id: 'Comfy.TestSetting',
|
||||||
|
name: 'Test',
|
||||||
|
// The `disabled` attr is common to all settings components
|
||||||
|
attrs: { disabled: true },
|
||||||
|
...config
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}, config)
|
||||||
|
|
||||||
|
await comfyPage.settingDialog.open()
|
||||||
|
const component = comfyPage.settingDialog.root
|
||||||
|
.getByText('TestSetting Test')
|
||||||
|
.locator(selector)
|
||||||
|
|
||||||
|
const isDisabled = await component.evaluate((el) =>
|
||||||
|
el.tagName === 'INPUT'
|
||||||
|
? (el as HTMLInputElement).disabled
|
||||||
|
: el.classList.contains('p-disabled')
|
||||||
|
)
|
||||||
|
expect(isDisabled).toBe(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('About panel', () => {
|
test.describe('About panel', () => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { LGraphNode } from '@comfyorg/litegraph'
|
||||||
import type { APIRequestContext, Locator, Page } from '@playwright/test'
|
import type { APIRequestContext, Locator, Page } from '@playwright/test'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
import { test as base } from '@playwright/test'
|
import { test as base } from '@playwright/test'
|
||||||
@@ -278,8 +279,8 @@ export class ComfyPage {
|
|||||||
await this.page.addStyleTag({
|
await this.page.addStyleTag({
|
||||||
content: `
|
content: `
|
||||||
* {
|
* {
|
||||||
font-family: 'Roboto Mono', 'Noto Color Emoji';
|
font-family: 'Roboto Mono', 'Noto Color Emoji';
|
||||||
}`
|
}`
|
||||||
})
|
})
|
||||||
await this.page.waitForFunction(() => document.fonts.ready)
|
await this.page.waitForFunction(() => document.fonts.ready)
|
||||||
await this.page.waitForFunction(
|
await this.page.waitForFunction(
|
||||||
@@ -646,6 +647,18 @@ export class ComfyPage {
|
|||||||
await this.nextFrame()
|
await this.nextFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async selectNodes(nodeTitles: string[]) {
|
||||||
|
await this.page.keyboard.down('Control')
|
||||||
|
for (const nodeTitle of nodeTitles) {
|
||||||
|
const nodes = await this.getNodeRefsByTitle(nodeTitle)
|
||||||
|
for (const node of nodes) {
|
||||||
|
await node.click('title')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.page.keyboard.up('Control')
|
||||||
|
await this.nextFrame()
|
||||||
|
}
|
||||||
|
|
||||||
async select2Nodes() {
|
async select2Nodes() {
|
||||||
// Select 2 CLIP nodes.
|
// Select 2 CLIP nodes.
|
||||||
await this.page.keyboard.down('Control')
|
await this.page.keyboard.down('Control')
|
||||||
@@ -835,12 +848,24 @@ export class ComfyPage {
|
|||||||
(
|
(
|
||||||
await this.page.evaluate((type) => {
|
await this.page.evaluate((type) => {
|
||||||
return window['app'].graph.nodes
|
return window['app'].graph.nodes
|
||||||
.filter((n) => n.type === type)
|
.filter((n: LGraphNode) => n.type === type)
|
||||||
.map((n) => n.id)
|
.map((n: LGraphNode) => n.id)
|
||||||
}, type)
|
}, type)
|
||||||
).map((id: NodeId) => this.getNodeRefById(id))
|
).map((id: NodeId) => this.getNodeRefById(id))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
async getNodeRefsByTitle(title: string): Promise<NodeReference[]> {
|
||||||
|
return Promise.all(
|
||||||
|
(
|
||||||
|
await this.page.evaluate((title) => {
|
||||||
|
return window['app'].graph.nodes
|
||||||
|
.filter((n: LGraphNode) => n.title === title)
|
||||||
|
.map((n: LGraphNode) => n.id)
|
||||||
|
}, title)
|
||||||
|
).map((id: NodeId) => this.getNodeRefById(id))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async getFirstNodeRef(): Promise<NodeReference | null> {
|
async getFirstNodeRef(): Promise<NodeReference | null> {
|
||||||
const id = await this.page.evaluate(() => {
|
const id = await this.page.evaluate(() => {
|
||||||
return window['app'].graph.nodes[0]?.id
|
return window['app'].graph.nodes[0]?.id
|
||||||
@@ -885,10 +910,10 @@ export class ComfyPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const comfyPageFixture = base.extend<{ comfyPage: ComfyPage }>({
|
export const comfyPageFixture = base.extend<{ comfyPage: ComfyPage }>({
|
||||||
comfyPage: async ({ page, request }, use) => {
|
comfyPage: async ({ page, request }, use, testInfo) => {
|
||||||
const comfyPage = new ComfyPage(page, request)
|
const comfyPage = new ComfyPage(page, request)
|
||||||
|
|
||||||
const { parallelIndex } = comfyPageFixture.info()
|
const { parallelIndex } = testInfo
|
||||||
const username = `playwright-test-${parallelIndex}`
|
const username = `playwright-test-${parallelIndex}`
|
||||||
const userId = await comfyPage.setupUser(username)
|
const userId = await comfyPage.setupUser(username)
|
||||||
comfyPage.userIds[parallelIndex] = userId
|
comfyPage.userIds[parallelIndex] = userId
|
||||||
@@ -896,15 +921,18 @@ export const comfyPageFixture = base.extend<{ comfyPage: ComfyPage }>({
|
|||||||
try {
|
try {
|
||||||
await comfyPage.setupSettings({
|
await comfyPage.setupSettings({
|
||||||
'Comfy.UseNewMenu': 'Disabled',
|
'Comfy.UseNewMenu': 'Disabled',
|
||||||
// Hide canvas menu/info by default.
|
// Hide canvas menu/info/selection toolbox by default.
|
||||||
'Comfy.Graph.CanvasInfo': false,
|
'Comfy.Graph.CanvasInfo': false,
|
||||||
'Comfy.Graph.CanvasMenu': false,
|
'Comfy.Graph.CanvasMenu': false,
|
||||||
|
'Comfy.Canvas.SelectionToolbox': false,
|
||||||
// Hide all badges by default.
|
// Hide all badges by default.
|
||||||
'Comfy.NodeBadge.NodeIdBadgeMode': NodeBadgeMode.None,
|
'Comfy.NodeBadge.NodeIdBadgeMode': NodeBadgeMode.None,
|
||||||
'Comfy.NodeBadge.NodeSourceBadgeMode': NodeBadgeMode.None,
|
'Comfy.NodeBadge.NodeSourceBadgeMode': NodeBadgeMode.None,
|
||||||
// Disable tooltips by default to avoid flakiness.
|
// Disable tooltips by default to avoid flakiness.
|
||||||
'Comfy.EnableTooltips': false,
|
'Comfy.EnableTooltips': false,
|
||||||
'Comfy.userId': userId
|
'Comfy.userId': userId,
|
||||||
|
// Set tutorial completed to true to avoid loading the tutorial workflow.
|
||||||
|
'Comfy.TutorialCompleted': true
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import { Page } from '@playwright/test'
|
|||||||
export class SettingDialog {
|
export class SettingDialog {
|
||||||
constructor(public readonly page: Page) {}
|
constructor(public readonly page: Page) {}
|
||||||
|
|
||||||
|
get root() {
|
||||||
|
return this.page.locator('div.settings-container')
|
||||||
|
}
|
||||||
|
|
||||||
async open() {
|
async open() {
|
||||||
const button = this.page.locator('button.comfy-settings-btn:visible')
|
const button = this.page.locator('button.comfy-settings-btn:visible')
|
||||||
await button.click()
|
await button.click()
|
||||||
|
|||||||
@@ -194,6 +194,10 @@ export class QueueSidebarTab extends SidebarTab {
|
|||||||
return this.root.locator('.no-results-placeholder')
|
return this.root.locator('.no-results-placeholder')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get galleryImage() {
|
||||||
|
return this.page.locator('.galleria-image')
|
||||||
|
}
|
||||||
|
|
||||||
private getToggleExpandButton(isExpanded: boolean) {
|
private getToggleExpandButton(isExpanded: boolean) {
|
||||||
const iconSelector = isExpanded ? '.pi-image' : '.pi-images'
|
const iconSelector = isExpanded ? '.pi-image' : '.pi-images'
|
||||||
return this.root.locator(`.toggle-expanded-button ${iconSelector}`)
|
return this.root.locator(`.toggle-expanded-button ${iconSelector}`)
|
||||||
@@ -256,14 +260,24 @@ export class QueueSidebarTab extends SidebarTab {
|
|||||||
|
|
||||||
async openTaskPreview(taskIndex: number) {
|
async openTaskPreview(taskIndex: number) {
|
||||||
const previewButton = this.getTaskPreviewButton(taskIndex)
|
const previewButton = this.getTaskPreviewButton(taskIndex)
|
||||||
await previewButton.hover()
|
|
||||||
await previewButton.click()
|
await previewButton.click()
|
||||||
return this.getGalleryImage(taskIndex).waitFor({ state: 'visible' })
|
return this.galleryImage.waitFor({ state: 'visible' })
|
||||||
}
|
}
|
||||||
|
|
||||||
getGalleryImage(galleryItemIndex: number) {
|
getGalleryImage(imageFilename: string) {
|
||||||
// Aria labels of Galleria items are 1-based indices
|
return this.galleryImage.and(this.page.getByAltText(imageFilename))
|
||||||
const galleryLabel = `${galleryItemIndex + 1}`
|
}
|
||||||
return this.page.getByLabel(galleryLabel).locator('.galleria-image')
|
|
||||||
|
getTaskImage(imageFilename: string) {
|
||||||
|
return this.tasks.getByAltText(imageFilename)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Trigger the queue store and tasks to update */
|
||||||
|
async triggerTasksUpdate() {
|
||||||
|
await this.page.evaluate(() => {
|
||||||
|
window['app']['api'].dispatchCustomEvent('status', {
|
||||||
|
exec_info: { queue_remaining: 0 }
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ export class NodeWidgetReference {
|
|||||||
readonly node: NodeReference
|
readonly node: NodeReference
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns The position of the widget's center
|
||||||
|
*/
|
||||||
async getPosition(): Promise<Position> {
|
async getPosition(): Promise<Position> {
|
||||||
const pos: [number, number] = await this.node.comfyPage.page.evaluate(
|
const pos: [number, number] = await this.node.comfyPage.page.evaluate(
|
||||||
([id, index]) => {
|
([id, index]) => {
|
||||||
@@ -83,6 +86,28 @@ export class NodeWidgetReference {
|
|||||||
y: pos[1]
|
y: pos[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async click() {
|
||||||
|
await this.node.comfyPage.canvas.click({
|
||||||
|
position: await this.getPosition()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async dragHorizontal(delta: number) {
|
||||||
|
const pos = await this.getPosition()
|
||||||
|
const canvas = this.node.comfyPage.canvas
|
||||||
|
const canvasPos = (await canvas.boundingBox())!
|
||||||
|
this.node.comfyPage.dragAndDrop(
|
||||||
|
{
|
||||||
|
x: canvasPos.x + pos.x,
|
||||||
|
y: canvasPos.y + pos.y
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: canvasPos.x + pos.x + delta,
|
||||||
|
y: canvasPos.y + pos.y
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NodeReference {
|
export class NodeReference {
|
||||||
|
|||||||
@@ -132,11 +132,12 @@ export default class TaskHistory {
|
|||||||
private addTask(task: HistoryTaskItem) {
|
private addTask(task: HistoryTaskItem) {
|
||||||
setPromptId(task)
|
setPromptId(task)
|
||||||
setQueueIndex(task)
|
setQueueIndex(task)
|
||||||
this.tasks.push(task)
|
this.tasks.unshift(task) // Tasks are added to the front of the queue
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTasks() {
|
clearTasks(): this {
|
||||||
this.tasks = []
|
this.tasks = []
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
withTask(
|
withTask(
|
||||||
@@ -155,7 +156,7 @@ export default class TaskHistory {
|
|||||||
/** Repeats the last task in the task history a specified number of times. */
|
/** Repeats the last task in the task history a specified number of times. */
|
||||||
repeat(n: number): this {
|
repeat(n: number): this {
|
||||||
for (let i = 0; i < n; i++)
|
for (let i = 0; i < n; i++)
|
||||||
this.addTask(structuredClone(this.tasks.at(-1)) as HistoryTaskItem)
|
this.addTask(structuredClone(this.tasks.at(0)) as HistoryTaskItem)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 100 KiB |
@@ -134,6 +134,37 @@ test.describe('Group Node', () => {
|
|||||||
expect(await manage2.getSelectedNodeType()).toBe('g2')
|
expect(await manage2.getSelectedNodeType()).toBe('g2')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Preserves hidden input configuration when containing duplicate node types', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.loadWorkflow('group_node_identical_nodes_hidden_inputs')
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
|
const groupNodeId = 19
|
||||||
|
const groupNodeName = 'two_VAE_decode'
|
||||||
|
|
||||||
|
const totalInputCount = await comfyPage.page.evaluate((nodeName) => {
|
||||||
|
const {
|
||||||
|
extra: { groupNodes }
|
||||||
|
} = window['app'].graph
|
||||||
|
const { nodes } = groupNodes[nodeName]
|
||||||
|
return nodes.reduce((acc: number, node) => {
|
||||||
|
return acc + node.inputs.length
|
||||||
|
}, 0)
|
||||||
|
}, groupNodeName)
|
||||||
|
|
||||||
|
const visibleInputCount = await comfyPage.page.evaluate((id) => {
|
||||||
|
const node = window['app'].graph.getNodeById(id)
|
||||||
|
return node.inputs.length
|
||||||
|
}, groupNodeId)
|
||||||
|
|
||||||
|
// Verify there are 4 total inputs (2 VAE decode nodes with 2 inputs each)
|
||||||
|
expect(totalInputCount).toBe(4)
|
||||||
|
|
||||||
|
// Verify there are 2 visible inputs (2 have been hidden in config)
|
||||||
|
expect(visibleInputCount).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
test('Reconnects inputs after configuration changed via manage dialog save', async ({
|
test('Reconnects inputs after configuration changed via manage dialog save', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 68 KiB |