diff --git a/.claude/commands/create-frontend-release.md b/.claude/commands/create-frontend-release.md index 01a9b831c..91afe7de3 100644 --- a/.claude/commands/create-frontend-release.md +++ b/.claude/commands/create-frontend-release.md @@ -169,7 +169,79 @@ echo "Last stable release: $LAST_STABLE" 3. Generate breaking change summary 4. **COMPATIBILITY REVIEW**: Breaking changes documented and justified? -### Step 7: Generate Comprehensive Release Notes +### Step 7: Analyze Dependency Updates + +1. **Check for dependency version changes:** + ```bash + # Compare package.json between versions to detect dependency updates + PREV_PACKAGE_JSON=$(git show ${BASE_TAG}:package.json 2>/dev/null || echo '{}') + CURRENT_PACKAGE_JSON=$(cat package.json) + + # Extract litegraph versions + PREV_LITEGRAPH=$(echo "$PREV_PACKAGE_JSON" | grep -o '"@comfyorg/litegraph": "[^"]*"' | grep -o '[0-9][^"]*' || echo "not found") + CURRENT_LITEGRAPH=$(echo "$CURRENT_PACKAGE_JSON" | grep -o '"@comfyorg/litegraph": "[^"]*"' | grep -o '[0-9][^"]*' || echo "not found") + + echo "Litegraph version change: ${PREV_LITEGRAPH} → ${CURRENT_LITEGRAPH}" + ``` + +2. **Generate litegraph changelog if version changed:** + ```bash + if [ "$PREV_LITEGRAPH" != "$CURRENT_LITEGRAPH" ] && [ "$PREV_LITEGRAPH" != "not found" ]; then + echo "đŸ“Ļ Fetching litegraph changes between v${PREV_LITEGRAPH} and v${CURRENT_LITEGRAPH}..." + + # Clone or update litegraph repo for changelog analysis + if [ ! -d ".temp-litegraph" ]; then + git clone https://github.com/comfyanonymous/litegraph.js.git .temp-litegraph + else + cd .temp-litegraph && git fetch --all && cd .. + fi + + # Get litegraph changelog between versions + LITEGRAPH_CHANGES=$(cd .temp-litegraph && git log v${PREV_LITEGRAPH}..v${CURRENT_LITEGRAPH} --oneline --no-merges 2>/dev/null || \ + git log --oneline --no-merges --since="$(git log -1 --format=%ci ${BASE_TAG})" --until="$(git log -1 --format=%ci HEAD)" 2>/dev/null || \ + echo "Unable to fetch litegraph changes") + + # Categorize litegraph changes + LITEGRAPH_FEATURES=$(echo "$LITEGRAPH_CHANGES" | grep -iE "(feat|feature|add)" || echo "") + LITEGRAPH_FIXES=$(echo "$LITEGRAPH_CHANGES" | grep -iE "(fix|bug)" || echo "") + LITEGRAPH_BREAKING=$(echo "$LITEGRAPH_CHANGES" | grep -iE "(break|breaking)" || echo "") + LITEGRAPH_OTHER=$(echo "$LITEGRAPH_CHANGES" | grep -viE "(feat|feature|add|fix|bug|break|breaking)" || echo "") + + # Clean up temp directory + rm -rf .temp-litegraph + + echo "✅ Litegraph changelog extracted" + else + echo "â„šī¸ No litegraph version change detected" + LITEGRAPH_CHANGES="" + fi + ``` + +3. **Check other significant dependency updates:** + ```bash + # Extract all dependency changes for major version bumps + OTHER_DEP_CHANGES="" + + # Compare major dependency versions (you can extend this list) + MAJOR_DEPS=("vue" "vite" "@vitejs/plugin-vue" "typescript" "pinia") + + for dep in "${MAJOR_DEPS[@]}"; do + PREV_VER=$(echo "$PREV_PACKAGE_JSON" | grep -o "\"$dep\": \"[^\"]*\"" | grep -o '[0-9][^"]*' | head -1 || echo "") + CURR_VER=$(echo "$CURRENT_PACKAGE_JSON" | grep -o "\"$dep\": \"[^\"]*\"" | grep -o '[0-9][^"]*' | head -1 || echo "") + + if [ "$PREV_VER" != "$CURR_VER" ] && [ -n "$PREV_VER" ] && [ -n "$CURR_VER" ]; then + # Check if it's a major version change + PREV_MAJOR=$(echo "$PREV_VER" | cut -d. -f1 | sed 's/[^0-9]//g') + CURR_MAJOR=$(echo "$CURR_VER" | cut -d. -f1 | sed 's/[^0-9]//g') + + if [ "$PREV_MAJOR" != "$CURR_MAJOR" ]; then + OTHER_DEP_CHANGES="${OTHER_DEP_CHANGES}\n- **${dep}**: ${PREV_VER} → ${CURR_VER} (Major version change)" + fi + fi + done + ``` + +### Step 8: Generate Comprehensive Release Notes 1. Extract commit messages since base release: ```bash @@ -193,6 +265,12 @@ echo "Last stable release: $LAST_STABLE" - 📚 **Documentation** (docs:) - 🔧 **Maintenance** (chore:, refactor:) - âŦ†ī¸ **Dependencies** (deps:, dependency updates) + - **Litegraph Changes** (if version updated): + - 🚀 Features: ${LITEGRAPH_FEATURES} + - 🐛 Bug Fixes: ${LITEGRAPH_FIXES} + - đŸ’Ĩ Breaking Changes: ${LITEGRAPH_BREAKING} + - 🔧 Other Changes: ${LITEGRAPH_OTHER} + - **Other Major Dependencies**: ${OTHER_DEP_CHANGES} - Include PR numbers and links - Add issue references (Fixes #123) 4. **Save release notes:** @@ -200,9 +278,9 @@ echo "Last stable release: $LAST_STABLE" # Save release notes for PR and GitHub release echo "$RELEASE_NOTES" > release-notes-${NEW_VERSION}.md ``` -5. **CONTENT REVIEW**: Release notes clear and comprehensive? +5. **CONTENT REVIEW**: Release notes clear and comprehensive with dependency details? -### Step 8: Create Version Bump PR +### Step 9: Create Version Bump PR **For standard version bumps (patch/minor/major):** ```bash @@ -274,7 +352,7 @@ echo "Workflow triggered. Waiting for PR creation..." ``` 5. **PR REVIEW**: Version bump PR created and enhanced correctly? -### Step 9: Critical Release PR Verification +### Step 10: Critical Release PR Verification 1. **CRITICAL**: Verify PR has "Release" label: ```bash @@ -296,7 +374,7 @@ echo "Workflow triggered. Waiting for PR creation..." ``` 7. **FINAL CODE REVIEW**: Release label present and no [skip ci]? -### Step 10: Pre-Merge Validation +### Step 11: Pre-Merge Validation 1. **Review Requirements**: Release PRs require approval 2. Monitor CI checks - watch for update-locales @@ -304,7 +382,7 @@ echo "Workflow triggered. Waiting for PR creation..." 4. Check no new commits to main since PR creation 5. **DEPLOYMENT READINESS**: Ready to merge? -### Step 11: Execute Release +### Step 12: Execute Release 1. **FINAL CONFIRMATION**: Merge PR to trigger release? 2. Merge the Release PR: @@ -329,7 +407,7 @@ echo "Workflow triggered. Waiting for PR creation..." gh run watch ${WORKFLOW_RUN_ID} ``` -### Step 12: Enhance GitHub Release +### Step 13: Enhance GitHub Release 1. Wait for automatic release creation: ```bash @@ -357,7 +435,7 @@ echo "Workflow triggered. Waiting for PR creation..." gh release view v${NEW_VERSION} ``` -### Step 13: Verify Multi-Channel Distribution +### Step 14: Verify Multi-Channel Distribution 1. **GitHub Release:** ```bash @@ -395,7 +473,7 @@ echo "Workflow triggered. Waiting for PR creation..." 4. **DISTRIBUTION VERIFICATION**: All channels published successfully? -### Step 14: Post-Release Monitoring Setup +### Step 15: Post-Release Monitoring Setup 1. **Monitor immediate release health:** ```bash @@ -572,6 +650,15 @@ The command implements multiple quality gates: gh pr view ${PR_NUMBER} --json baseRefName ``` +### Issue: Incomplete Dependency Changelog +**Problem**: Litegraph or other dependency updates only show version bump, not actual changes +**Solution**: The command now automatically: +- Detects litegraph version changes between releases +- Clones the litegraph repository temporarily +- Extracts and categorizes changes between versions +- Includes detailed litegraph changelog in release notes +- Cleans up temporary files after analysis + ### Issue: Release Failed Due to [skip ci] **Problem**: Release workflow didn't trigger after merge **Prevention**: Always avoid this scenario @@ -592,4 +679,6 @@ Benefits: Cleaner than creating extra version numbers 2. **Workflow Speed**: Version bump workflow typically completes in ~20-30 seconds 3. **Update-locales Behavior**: Inconsistent - sometimes adds [skip ci], sometimes doesn't 4. **Recovery Options**: Reverting version is cleaner than creating extra versions +5. **Dependency Tracking**: Command now automatically includes litegraph and major dependency changes in changelogs +6. **Litegraph Integration**: Temporary cloning of litegraph repo provides detailed change analysis between versions diff --git a/.gitattributes b/.gitattributes index 37d931349..af4b6adbc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,7 @@ *.ts text eol=lf *.vue text eol=lf *.js text eol=lf + +# Generated files +src/types/comfyRegistryTypes.ts linguist-generated=true +src/types/generatedManagerTypes.ts linguist-generated=true diff --git a/.github/workflows/eslint.yaml b/.github/workflows/eslint.yaml index 72ed6b8fb..c3735ff5f 100644 --- a/.github/workflows/eslint.yaml +++ b/.github/workflows/eslint.yaml @@ -2,7 +2,7 @@ name: ESLint on: pull_request: - branches: [ main, master, dev*, core/*, desktop/* ] + branches-ignore: [ wip/*, draft/*, temp/* ] jobs: eslint: diff --git a/.github/workflows/format.yaml b/.github/workflows/format.yaml index 23eef7f55..5d71c3452 100644 --- a/.github/workflows/format.yaml +++ b/.github/workflows/format.yaml @@ -2,7 +2,7 @@ name: Prettier Check on: pull_request: - branches: [ main, master, dev*, core/*, desktop/* ] + branches-ignore: [ wip/*, draft/*, temp/* ] jobs: prettier: diff --git a/.github/workflows/test-ui.yaml b/.github/workflows/test-ui.yaml index 580bdee19..35b24d845 100644 --- a/.github/workflows/test-ui.yaml +++ b/.github/workflows/test-ui.yaml @@ -4,7 +4,7 @@ on: push: branches: [main, master, core/*, desktop/*] pull_request: - branches: [main, master, dev*, core/*, desktop/*] + branches-ignore: [wip/*, draft/*, temp/*] jobs: setup: diff --git a/.github/workflows/vitest.yaml b/.github/workflows/vitest.yaml index a2f47c342..788b6aba4 100644 --- a/.github/workflows/vitest.yaml +++ b/.github/workflows/vitest.yaml @@ -4,7 +4,7 @@ on: push: branches: [ main, master, dev*, core/*, desktop/* ] pull_request: - branches: [ main, master, dev*, core/*, desktop/* ] + branches-ignore: [ wip/*, draft/*, temp/* ] jobs: test: diff --git a/README.md b/README.md index d490ae628..89aa97687 100644 --- a/README.md +++ b/README.md @@ -686,6 +686,12 @@ Component test verifies Vue components in `src/components/`. Playwright test verifies the whole app. See for details. +### Custom Icons + +The project supports custom SVG icons through the unplugin-icons system. Custom icons are stored in `src/assets/icons/custom/` and can be used as Vue components with the `i-comfy:` prefix. + +For detailed instructions on adding and using custom icons, see [src/assets/icons/README.md](src/assets/icons/README.md). + ### litegraph.js This repo is using litegraph package hosted on . Any changes to litegraph should be submitted in that repo instead. diff --git a/browser_tests/tests/groupNode.spec.ts b/browser_tests/tests/groupNode.spec.ts index 0e4d7c38a..ae1ee505f 100644 --- a/browser_tests/tests/groupNode.spec.ts +++ b/browser_tests/tests/groupNode.spec.ts @@ -264,10 +264,15 @@ test.describe('Group Node', () => { test('Copies and pastes group node after clearing workflow', async ({ comfyPage }) => { + // Set setting + await comfyPage.setSetting('Comfy.ConfirmClear', false) + + // Clear workflow await comfyPage.menu.topbar.triggerTopbarCommand([ 'Edit', 'Clear Workflow' ]) + await comfyPage.ctrlV() await verifyNodeLoaded(comfyPage, 1) }) diff --git a/package-lock.json b/package-lock.json index c62c30977..9ec9420c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@alloc/quick-lru": "^5.2.0", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@comfyorg/comfyui-electron-types": "^0.4.43", - "@comfyorg/litegraph": "^0.16.19", + "@comfyorg/litegraph": "^0.16.20", "@primevue/forms": "^4.2.5", "@primevue/themes": "^4.2.5", "@sentry/vue": "^8.48.0", @@ -88,8 +88,8 @@ "tsx": "^4.15.6", "typescript": "^5.4.5", "typescript-eslint": "^8.0.0", - "unplugin-icons": "^0.19.3", - "unplugin-vue-components": "^0.27.4", + "unplugin-icons": "^0.22.0", + "unplugin-vue-components": "^0.28.0", "vite": "^5.4.19", "vite-plugin-dts": "^4.3.0", "vite-plugin-html": "^3.2.2", @@ -441,13 +441,13 @@ } }, "node_modules/@antfu/install-pkg": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.1.tgz", - "integrity": "sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.5.0.tgz", + "integrity": "sha512-dKnk2xlAyC7rvTkpkHmu+Qy/2Zc3Vm/l8PtNyIOGDBtXPY3kThfU4ORNEp3V7SXw5XSOb+tOJaUYpfquPzL/Tg==", "dev": true, "dependencies": { - "package-manager-detector": "^0.2.0", - "tinyexec": "^0.3.0" + "package-manager-detector": "^0.2.5", + "tinyexec": "^0.3.1" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -977,9 +977,9 @@ "license": "GPL-3.0-only" }, "node_modules/@comfyorg/litegraph": { - "version": "0.16.19", - "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.16.19.tgz", - "integrity": "sha512-3XxQbEYv4y6vplW7RnJOdy5y2j2uVjGVNYvP6t6cEuop4WJulDxZBFJp4FL5cJFkjKpgBLDMNdAhHzKtlpAB1g==", + "version": "0.16.20", + "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.16.20.tgz", + "integrity": "sha512-8iUBhKYkr9qV6vWxC3C9Wea9K7iHwyDHxxN6OrhE9sySYfUA14XuNpVMaC8eVUaIm5KBOSmr/Q1J2XVHsHEISg==", "license": "MIT" }, "node_modules/@cspotcode/source-map-support": { @@ -2329,20 +2329,112 @@ "dev": true }, "node_modules/@iconify/utils": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.32.tgz", - "integrity": "sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", "dev": true, "dependencies": { - "@antfu/install-pkg": "^0.4.0", - "@antfu/utils": "^0.7.10", + "@antfu/install-pkg": "^1.0.0", + "@antfu/utils": "^8.1.0", "@iconify/types": "^2.0.0", - "debug": "^4.3.6", + "debug": "^4.4.0", + "globals": "^15.14.0", "kolorist": "^1.8.0", - "local-pkg": "^0.5.0", - "mlly": "^1.7.1" + "local-pkg": "^1.0.0", + "mlly": "^1.7.4" } }, + "node_modules/@iconify/utils/node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "dev": true, + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@iconify/utils/node_modules/@antfu/utils": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@iconify/utils/node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true + }, + "node_modules/@iconify/utils/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@iconify/utils/node_modules/local-pkg": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz", + "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==", + "dev": true, + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.0.1", + "quansync": "^0.2.8" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@iconify/utils/node_modules/package-manager-detector": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", + "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", + "dev": true + }, + "node_modules/@iconify/utils/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, + "node_modules/@iconify/utils/node_modules/pkg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", + "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", + "dev": true, + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/@iconify/utils/node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "dev": true + }, "node_modules/@inkjs/ui": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@inkjs/ui/-/ui-1.0.0.tgz", @@ -6572,9 +6664,9 @@ "license": "MIT" }, "node_modules/confbox": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", - "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "dev": true }, "node_modules/config-chain": { @@ -8293,6 +8385,12 @@ "node": ">= 0.6" } }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "dev": true + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -9028,11 +9126,10 @@ } }, "node_modules/globals": { - "version": "15.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", - "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -10884,13 +10981,13 @@ } }, "node_modules/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", "dev": true, "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" }, "engines": { "node": ">=14" @@ -12197,17 +12294,23 @@ "dev": true }, "node_modules/mlly": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", - "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", "dev": true, "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.1.1", - "ufo": "^1.5.3" + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" } }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -12760,10 +12863,13 @@ "dev": true }, "node_modules/package-manager-detector": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.0.tgz", - "integrity": "sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==", - "dev": true + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "dev": true, + "dependencies": { + "quansync": "^0.2.7" + } }, "node_modules/pako": { "version": "1.0.11", @@ -13057,16 +13163,22 @@ } }, "node_modules/pkg-types": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", - "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", "dev": true, "dependencies": { - "confbox": "^0.1.7", - "mlly": "^1.7.1", - "pathe": "^1.1.2" + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" } }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, "node_modules/playwright": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", @@ -13655,6 +13767,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quansync": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz", + "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ] + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -15205,8 +15333,7 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/tinypool": { "version": "1.0.1", @@ -16138,39 +16265,32 @@ } }, "node_modules/unplugin": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.13.1.tgz", - "integrity": "sha512-6Kq1iSSwg7KyjcThRUks9LuqDAKvtnioxbL9iEtB9ctTyBA5OmrB8gZd/d225VJu1w3UpUsKV7eGrvf59J7+VA==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.5.tgz", + "integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==", "dev": true, "dependencies": { - "acorn": "^8.12.1", + "acorn": "^8.14.1", + "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" }, "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "webpack-sources": "^3" - }, - "peerDependenciesMeta": { - "webpack-sources": { - "optional": true - } + "node": ">=18.12.0" } }, "node_modules/unplugin-icons": { - "version": "0.19.3", - "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-0.19.3.tgz", - "integrity": "sha512-EUegRmsAI6+rrYr0vXjFlIP+lg4fSC4zb62zAZKx8FGXlWAGgEGBCa3JDe27aRAXhistObLPbBPhwa/0jYLFkQ==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-0.22.0.tgz", + "integrity": "sha512-CP+iZq5U7doOifer5bcM0jQ9t3Is7EGybIYt3myVxceI8Zuk8EZEpe1NPtJvh7iqMs1VdbK0L41t9+um9VuuLw==", "dev": true, "dependencies": { - "@antfu/install-pkg": "^0.4.1", + "@antfu/install-pkg": "^0.5.0", "@antfu/utils": "^0.7.10", - "@iconify/utils": "^2.1.29", - "debug": "^4.3.6", + "@iconify/utils": "^2.2.0", + "debug": "^4.4.0", "kolorist": "^1.8.0", - "local-pkg": "^0.5.0", - "unplugin": "^1.12.0" + "local-pkg": "^0.5.1", + "unplugin": "^2.1.0" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -16179,6 +16299,7 @@ "@svgr/core": ">=7.0.0", "@svgx/core": "^1.0.1", "@vue/compiler-sfc": "^3.0.2 || ^2.7.0", + "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0", "vue-template-compiler": "^2.6.12", "vue-template-es2015-compiler": "^1.9.0" }, @@ -16192,6 +16313,9 @@ "@vue/compiler-sfc": { "optional": true }, + "svelte": { + "optional": true + }, "vue-template-compiler": { "optional": true }, @@ -16200,22 +16324,39 @@ } } }, + "node_modules/unplugin-icons/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/unplugin-vue-components": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.27.4.tgz", - "integrity": "sha512-1XVl5iXG7P1UrOMnaj2ogYa5YTq8aoh5jwDPQhemwO/OrXW+lPQKDXd1hMz15qxQPxgb/XXlbgo3HQ2rLEbmXQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.28.0.tgz", + "integrity": "sha512-jiTGtJ3JsRFBjgvyilfrX7yUoGKScFgbdNw+6p6kEXU+Spf/rhxzgvdfuMcvhCcLmflB/dY3pGQshYBVGOUx7Q==", "dev": true, "dependencies": { "@antfu/utils": "^0.7.10", - "@rollup/pluginutils": "^5.1.0", + "@rollup/pluginutils": "^5.1.4", "chokidar": "^3.6.0", - "debug": "^4.3.6", + "debug": "^4.4.0", "fast-glob": "^3.3.2", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.11", + "local-pkg": "^0.5.1", + "magic-string": "^0.30.15", "minimatch": "^9.0.5", - "mlly": "^1.7.1", - "unplugin": "^1.12.1" + "mlly": "^1.7.3", + "unplugin": "^2.1.0" }, "engines": { "node": ">=14" @@ -16246,6 +16387,23 @@ "balanced-match": "^1.0.0" } }, + "node_modules/unplugin-vue-components/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/unplugin-vue-components/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -16261,6 +16419,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/unplugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", diff --git a/package.json b/package.json index cd1a72d94..e2652cf23 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,8 @@ "tsx": "^4.15.6", "typescript": "^5.4.5", "typescript-eslint": "^8.0.0", - "unplugin-icons": "^0.19.3", - "unplugin-vue-components": "^0.27.4", + "unplugin-icons": "^0.22.0", + "unplugin-vue-components": "^0.28.0", "vite": "^5.4.19", "vite-plugin-dts": "^4.3.0", "vite-plugin-html": "^3.2.2", @@ -78,7 +78,7 @@ "@alloc/quick-lru": "^5.2.0", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@comfyorg/comfyui-electron-types": "^0.4.43", - "@comfyorg/litegraph": "^0.16.19", + "@comfyorg/litegraph": "^0.16.20", "@primevue/forms": "^4.2.5", "@primevue/themes": "^4.2.5", "@sentry/vue": "^8.48.0", diff --git a/src/assets/icons/README.md b/src/assets/icons/README.md new file mode 100644 index 000000000..ce227de35 --- /dev/null +++ b/src/assets/icons/README.md @@ -0,0 +1,184 @@ +# ComfyUI Custom Icons Guide + +This guide explains how to add and use custom SVG icons in the ComfyUI frontend. + +## Overview + +ComfyUI uses a hybrid icon system that supports: +- **PrimeIcons** - Legacy icon library (CSS classes like `pi pi-plus`) +- **Iconify** - Modern icon system with 200,000+ icons +- **Custom Icons** - Your own SVG icons + +Custom icons are powered by [unplugin-icons](https://github.com/unplugin/unplugin-icons) and integrate seamlessly with Vue's component system. + +## Quick Start + +### 1. Add Your SVG Icon + +Place your SVG file in the `custom/` directory: +``` +src/assets/icons/custom/ +└── your-icon.svg +``` + +### 2. Use in Components + +```vue + +``` + +## SVG Requirements + +### File Naming +- Use kebab-case: `workflow-icon.svg`, `node-tree.svg` +- Avoid special characters and spaces +- The filename becomes the icon name + +### SVG Format +```xml + + + +``` + +**Important:** +- Use `viewBox` for proper scaling (24x24 is standard) +- Don't include `width` or `height` attributes +- Use `currentColor` for theme-aware icons +- Keep SVGs optimized and simple + +### Color Theming + +For icons that adapt to the current theme, use `currentColor`: + +```xml + + + + + + + + + +``` + +## Usage Examples + +### Basic Icon +```vue + +``` + +### With Classes +```vue + +``` + +### In Buttons +```vue + +``` + +### Conditional Icons +```vue + +``` + +## Technical Details + +### How It Works + +1. **unplugin-icons** automatically discovers SVG files in `custom/` +2. During build, SVGs are converted to Vue components +3. Components are tree-shaken - only used icons are bundled +4. The `i-` prefix and `comfy:` namespace identify custom icons + +### Configuration + +The icon system is configured in `vite.config.mts`: + +```typescript +Icons({ + compiler: 'vue3', + customCollections: { + 'comfy': FileSystemIconLoader('src/assets/icons/custom'), + } +}) +``` + +### TypeScript Support + +Icons are automatically typed. If TypeScript doesn't recognize a new icon: +1. Restart your dev server +2. Check that the SVG file is valid +3. Ensure the filename follows kebab-case convention + +## Troubleshooting + +### Icon Not Showing +1. **Check filename**: Must be kebab-case without special characters +2. **Restart dev server**: Required after adding new icons +3. **Verify SVG**: Ensure it's valid SVG syntax +4. **Check console**: Look for Vue component resolution errors + +### Icon Wrong Color +- Replace hardcoded colors with `currentColor` +- Use `stroke="currentColor"` for outlines +- Use `fill="currentColor"` for filled shapes + +### Icon Wrong Size +- Remove `width` and `height` from SVG +- Ensure `viewBox` is present +- Use CSS classes for sizing: `class="w-6 h-6"` + +## Best Practices + +1. **Optimize SVGs**: Use tools like [SVGO](https://jakearchibald.github.io/svgomg/) to minimize file size +2. **Consistent viewBox**: Stick to 24x24 or 16x16 for consistency +3. **Semantic names**: Use descriptive names like `workflow-duplicate` not `icon1` +4. **Theme support**: Always use `currentColor` for adaptable icons +5. **Test both themes**: Verify icons look good in light and dark modes + +## Migration from PrimeIcons + +When replacing a PrimeIcon with a custom icon: + +```vue + + +``` + +## Adding Icon Collections + +To add an entire icon set from npm: + +1. Install the icon package +2. Configure in `vite.config.mts` +3. Use with the appropriate prefix + +See the [unplugin-icons documentation](https://github.com/unplugin/unplugin-icons) for details. \ No newline at end of file diff --git a/src/assets/icons/custom/workflow.svg b/src/assets/icons/custom/workflow.svg new file mode 100644 index 000000000..72f90c1a4 --- /dev/null +++ b/src/assets/icons/custom/workflow.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/components/graph/selectionToolbox/ColorPickerButton.spec.ts b/src/components/graph/selectionToolbox/ColorPickerButton.spec.ts new file mode 100644 index 000000000..1989352b7 --- /dev/null +++ b/src/components/graph/selectionToolbox/ColorPickerButton.spec.ts @@ -0,0 +1,122 @@ +import { mount } from '@vue/test-utils' +import { createPinia, setActivePinia } from 'pinia' +import PrimeVue from 'primevue/config' +import Tooltip from 'primevue/tooltip' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { createI18n } from 'vue-i18n' + +// Import after mocks +import ColorPickerButton from '@/components/graph/selectionToolbox/ColorPickerButton.vue' +import { useCanvasStore } from '@/stores/graphStore' +import { useWorkflowStore } from '@/stores/workflowStore' + +// Mock the litegraph module +vi.mock('@comfyorg/litegraph', async () => { + const actual = await vi.importActual('@comfyorg/litegraph') + return { + ...actual, + LGraphCanvas: { + node_colors: { + red: { bgcolor: '#ff0000' }, + green: { bgcolor: '#00ff00' }, + blue: { bgcolor: '#0000ff' } + } + }, + LiteGraph: { + NODE_DEFAULT_BGCOLOR: '#353535' + }, + isColorable: vi.fn(() => true) + } +}) + +// Mock the colorUtil module +vi.mock('@/utils/colorUtil', () => ({ + adjustColor: vi.fn((color: string) => color + '_light') +})) + +// Mock the litegraphUtil module +vi.mock('@/utils/litegraphUtil', () => ({ + getItemsColorOption: vi.fn(() => null), + isLGraphNode: vi.fn((item) => item?.type === 'LGraphNode'), + isLGraphGroup: vi.fn((item) => item?.type === 'LGraphGroup'), + isReroute: vi.fn(() => false) +})) + +describe('ColorPickerButton', () => { + let canvasStore: ReturnType + let workflowStore: ReturnType + + const i18n = createI18n({ + legacy: false, + locale: 'en', + messages: { + en: { + color: { + noColor: 'No Color', + red: 'Red', + green: 'Green', + blue: 'Blue' + } + } + } + }) + + beforeEach(() => { + setActivePinia(createPinia()) + canvasStore = useCanvasStore() + workflowStore = useWorkflowStore() + + // Set up default store state + canvasStore.selectedItems = [] + + // Mock workflow store + workflowStore.activeWorkflow = { + changeTracker: { + checkState: vi.fn() + } + } as any + }) + + const createWrapper = () => { + return mount(ColorPickerButton, { + global: { + plugins: [PrimeVue, i18n], + directives: { + tooltip: Tooltip + } + } + }) + } + + it('should render when nodes are selected', () => { + // Add a mock node to selectedItems + canvasStore.selectedItems = [{ type: 'LGraphNode' } as any] + const wrapper = createWrapper() + expect(wrapper.find('button').exists()).toBe(true) + }) + + it('should not render when nothing is selected', () => { + // Keep selectedItems empty + canvasStore.selectedItems = [] + const wrapper = createWrapper() + // The button exists but is hidden with v-show + expect(wrapper.find('button').exists()).toBe(true) + expect(wrapper.find('button').attributes('style')).toContain( + 'display: none' + ) + }) + + it('should toggle color picker visibility on button click', async () => { + canvasStore.selectedItems = [{ type: 'LGraphNode' } as any] + const wrapper = createWrapper() + const button = wrapper.find('button') + + expect(wrapper.find('.color-picker-container').exists()).toBe(false) + + await button.trigger('click') + expect(wrapper.find('.color-picker-container').exists()).toBe(true) + + await button.trigger('click') + expect(wrapper.find('.color-picker-container').exists()).toBe(false) + }) +}) diff --git a/src/components/graph/selectionToolbox/ColorPickerButton.vue b/src/components/graph/selectionToolbox/ColorPickerButton.vue index 3f7daacf5..b35bc4aa4 100644 --- a/src/components/graph/selectionToolbox/ColorPickerButton.vue +++ b/src/components/graph/selectionToolbox/ColorPickerButton.vue @@ -2,6 +2,10 @@