[feat] Add custom icon system with workflow icon (#4590)

This commit is contained in:
Christian Byrne
2025-07-30 01:27:15 -07:00
committed by GitHub
parent 5c71854a96
commit 516eb26d3e
7 changed files with 460 additions and 82 deletions

View File

@@ -686,6 +686,12 @@ Component test verifies Vue components in `src/components/`.
Playwright test verifies the whole app. See <https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/browser_tests/README.md> for details. Playwright test verifies the whole app. See <https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/browser_tests/README.md> 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 ### litegraph.js
This repo is using litegraph package hosted on <https://github.com/Comfy-Org/litegraph.js>. Any changes to litegraph should be submitted in that repo instead. This repo is using litegraph package hosted on <https://github.com/Comfy-Org/litegraph.js>. Any changes to litegraph should be submitted in that repo instead.

322
package-lock.json generated
View File

@@ -88,8 +88,8 @@
"tsx": "^4.15.6", "tsx": "^4.15.6",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"typescript-eslint": "^8.0.0", "typescript-eslint": "^8.0.0",
"unplugin-icons": "^0.19.3", "unplugin-icons": "^0.22.0",
"unplugin-vue-components": "^0.27.4", "unplugin-vue-components": "^0.28.0",
"vite": "^5.4.19", "vite": "^5.4.19",
"vite-plugin-dts": "^4.3.0", "vite-plugin-dts": "^4.3.0",
"vite-plugin-html": "^3.2.2", "vite-plugin-html": "^3.2.2",
@@ -441,13 +441,13 @@
} }
}, },
"node_modules/@antfu/install-pkg": { "node_modules/@antfu/install-pkg": {
"version": "0.4.1", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.1.tgz", "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.5.0.tgz",
"integrity": "sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==", "integrity": "sha512-dKnk2xlAyC7rvTkpkHmu+Qy/2Zc3Vm/l8PtNyIOGDBtXPY3kThfU4ORNEp3V7SXw5XSOb+tOJaUYpfquPzL/Tg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"package-manager-detector": "^0.2.0", "package-manager-detector": "^0.2.5",
"tinyexec": "^0.3.0" "tinyexec": "^0.3.1"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
@@ -2329,20 +2329,112 @@
"dev": true "dev": true
}, },
"node_modules/@iconify/utils": { "node_modules/@iconify/utils": {
"version": "2.1.32", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.32.tgz", "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz",
"integrity": "sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==", "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@antfu/install-pkg": "^0.4.0", "@antfu/install-pkg": "^1.0.0",
"@antfu/utils": "^0.7.10", "@antfu/utils": "^8.1.0",
"@iconify/types": "^2.0.0", "@iconify/types": "^2.0.0",
"debug": "^4.3.6", "debug": "^4.4.0",
"globals": "^15.14.0",
"kolorist": "^1.8.0", "kolorist": "^1.8.0",
"local-pkg": "^0.5.0", "local-pkg": "^1.0.0",
"mlly": "^1.7.1" "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": { "node_modules/@inkjs/ui": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@inkjs/ui/-/ui-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@inkjs/ui/-/ui-1.0.0.tgz",
@@ -6572,9 +6664,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/confbox": { "node_modules/confbox": {
"version": "0.1.7", "version": "0.1.8",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
"integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
"dev": true "dev": true
}, },
"node_modules/config-chain": { "node_modules/config-chain": {
@@ -8293,6 +8385,12 @@
"node": ">= 0.6" "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": { "node_modules/extend": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -9028,11 +9126,10 @@
} }
}, },
"node_modules/globals": { "node_modules/globals": {
"version": "15.9.0", "version": "15.15.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
"integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
@@ -10884,13 +10981,13 @@
} }
}, },
"node_modules/local-pkg": { "node_modules/local-pkg": {
"version": "0.5.0", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz",
"integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"mlly": "^1.4.2", "mlly": "^1.7.3",
"pkg-types": "^1.0.3" "pkg-types": "^1.2.1"
}, },
"engines": { "engines": {
"node": ">=14" "node": ">=14"
@@ -12197,17 +12294,23 @@
"dev": true "dev": true
}, },
"node_modules/mlly": { "node_modules/mlly": {
"version": "1.7.1", "version": "1.7.4",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz",
"integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"acorn": "^8.11.3", "acorn": "^8.14.0",
"pathe": "^1.1.2", "pathe": "^2.0.1",
"pkg-types": "^1.1.1", "pkg-types": "^1.3.0",
"ufo": "^1.5.3" "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": { "node_modules/mri": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@@ -12760,10 +12863,13 @@
"dev": true "dev": true
}, },
"node_modules/package-manager-detector": { "node_modules/package-manager-detector": {
"version": "0.2.0", "version": "0.2.11",
"resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.0.tgz", "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz",
"integrity": "sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==", "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==",
"dev": true "dev": true,
"dependencies": {
"quansync": "^0.2.7"
}
}, },
"node_modules/pako": { "node_modules/pako": {
"version": "1.0.11", "version": "1.0.11",
@@ -13057,16 +13163,22 @@
} }
}, },
"node_modules/pkg-types": { "node_modules/pkg-types": {
"version": "1.2.0", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
"integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"confbox": "^0.1.7", "confbox": "^0.1.8",
"mlly": "^1.7.1", "mlly": "^1.7.4",
"pathe": "^1.1.2" "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": { "node_modules/playwright": {
"version": "1.52.0", "version": "1.52.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz",
@@ -13655,6 +13767,22 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/querystringify": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
@@ -15205,8 +15333,7 @@
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
"dev": true, "dev": true
"license": "MIT"
}, },
"node_modules/tinypool": { "node_modules/tinypool": {
"version": "1.0.1", "version": "1.0.1",
@@ -16138,39 +16265,32 @@
} }
}, },
"node_modules/unplugin": { "node_modules/unplugin": {
"version": "1.13.1", "version": "2.3.5",
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.13.1.tgz", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.5.tgz",
"integrity": "sha512-6Kq1iSSwg7KyjcThRUks9LuqDAKvtnioxbL9iEtB9ctTyBA5OmrB8gZd/d225VJu1w3UpUsKV7eGrvf59J7+VA==", "integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"acorn": "^8.12.1", "acorn": "^8.14.1",
"picomatch": "^4.0.2",
"webpack-virtual-modules": "^0.6.2" "webpack-virtual-modules": "^0.6.2"
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=18.12.0"
},
"peerDependencies": {
"webpack-sources": "^3"
},
"peerDependenciesMeta": {
"webpack-sources": {
"optional": true
}
} }
}, },
"node_modules/unplugin-icons": { "node_modules/unplugin-icons": {
"version": "0.19.3", "version": "0.22.0",
"resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-0.19.3.tgz", "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-0.22.0.tgz",
"integrity": "sha512-EUegRmsAI6+rrYr0vXjFlIP+lg4fSC4zb62zAZKx8FGXlWAGgEGBCa3JDe27aRAXhistObLPbBPhwa/0jYLFkQ==", "integrity": "sha512-CP+iZq5U7doOifer5bcM0jQ9t3Is7EGybIYt3myVxceI8Zuk8EZEpe1NPtJvh7iqMs1VdbK0L41t9+um9VuuLw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@antfu/install-pkg": "^0.4.1", "@antfu/install-pkg": "^0.5.0",
"@antfu/utils": "^0.7.10", "@antfu/utils": "^0.7.10",
"@iconify/utils": "^2.1.29", "@iconify/utils": "^2.2.0",
"debug": "^4.3.6", "debug": "^4.4.0",
"kolorist": "^1.8.0", "kolorist": "^1.8.0",
"local-pkg": "^0.5.0", "local-pkg": "^0.5.1",
"unplugin": "^1.12.0" "unplugin": "^2.1.0"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
@@ -16179,6 +16299,7 @@
"@svgr/core": ">=7.0.0", "@svgr/core": ">=7.0.0",
"@svgx/core": "^1.0.1", "@svgx/core": "^1.0.1",
"@vue/compiler-sfc": "^3.0.2 || ^2.7.0", "@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-compiler": "^2.6.12",
"vue-template-es2015-compiler": "^1.9.0" "vue-template-es2015-compiler": "^1.9.0"
}, },
@@ -16192,6 +16313,9 @@
"@vue/compiler-sfc": { "@vue/compiler-sfc": {
"optional": true "optional": true
}, },
"svelte": {
"optional": true
},
"vue-template-compiler": { "vue-template-compiler": {
"optional": true "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": { "node_modules/unplugin-vue-components": {
"version": "0.27.4", "version": "0.28.0",
"resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.27.4.tgz", "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.28.0.tgz",
"integrity": "sha512-1XVl5iXG7P1UrOMnaj2ogYa5YTq8aoh5jwDPQhemwO/OrXW+lPQKDXd1hMz15qxQPxgb/XXlbgo3HQ2rLEbmXQ==", "integrity": "sha512-jiTGtJ3JsRFBjgvyilfrX7yUoGKScFgbdNw+6p6kEXU+Spf/rhxzgvdfuMcvhCcLmflB/dY3pGQshYBVGOUx7Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@antfu/utils": "^0.7.10", "@antfu/utils": "^0.7.10",
"@rollup/pluginutils": "^5.1.0", "@rollup/pluginutils": "^5.1.4",
"chokidar": "^3.6.0", "chokidar": "^3.6.0",
"debug": "^4.3.6", "debug": "^4.4.0",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"local-pkg": "^0.5.0", "local-pkg": "^0.5.1",
"magic-string": "^0.30.11", "magic-string": "^0.30.15",
"minimatch": "^9.0.5", "minimatch": "^9.0.5",
"mlly": "^1.7.1", "mlly": "^1.7.3",
"unplugin": "^1.12.1" "unplugin": "^2.1.0"
}, },
"engines": { "engines": {
"node": ">=14" "node": ">=14"
@@ -16246,6 +16387,23 @@
"balanced-match": "^1.0.0" "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": { "node_modules/unplugin-vue-components/node_modules/minimatch": {
"version": "9.0.5", "version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -16261,6 +16419,18 @@
"url": "https://github.com/sponsors/isaacs" "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": { "node_modules/update-browserslist-db": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",

View File

@@ -63,8 +63,8 @@
"tsx": "^4.15.6", "tsx": "^4.15.6",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"typescript-eslint": "^8.0.0", "typescript-eslint": "^8.0.0",
"unplugin-icons": "^0.19.3", "unplugin-icons": "^0.22.0",
"unplugin-vue-components": "^0.27.4", "unplugin-vue-components": "^0.28.0",
"vite": "^5.4.19", "vite": "^5.4.19",
"vite-plugin-dts": "^4.3.0", "vite-plugin-dts": "^4.3.0",
"vite-plugin-html": "^3.2.2", "vite-plugin-html": "^3.2.2",

184
src/assets/icons/README.md Normal file
View File

@@ -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
<template>
<!-- Use as a Vue component -->
<i-comfy:your-icon />
<!-- In a PrimeVue button -->
<Button>
<template #icon>
<i-comfy:your-icon />
</template>
</Button>
</template>
```
## 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
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="..." />
</svg>
```
**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
<!-- ✅ Good: Uses currentColor -->
<svg viewBox="0 0 24 24">
<path stroke="currentColor" fill="none" d="..." />
</svg>
<!-- ❌ Bad: Hardcoded colors -->
<svg viewBox="0 0 24 24">
<path stroke="white" fill="black" d="..." />
</svg>
```
## Usage Examples
### Basic Icon
```vue
<i-comfy:workflow />
```
### With Classes
```vue
<i-comfy:workflow class="text-2xl text-blue-500" />
```
### In Buttons
```vue
<Button severity="secondary" text>
<template #icon>
<i-comfy:workflow />
</template>
</Button>
```
### Conditional Icons
```vue
<template #icon>
<i-comfy:workflow v-if="isWorkflow" />
<i-comfy:node v-else />
</template>
```
## 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
<!-- Before: PrimeIcon -->
<Button icon="pi pi-box" />
<!-- After: Custom icon -->
<Button>
<template #icon>
<i-comfy:workflow />
</template>
</Button>
```
## 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.

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 5V3C14 2.44772 13.5523 2 13 2H11C10.4477 2 10 2.44772 10 3V5C10 5.55228 10.4477 6 11 6H13C13.5523 6 14 5.55228 14 5Z" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
<path d="M6 5V3C6 2.44772 5.55228 2 5 2H3C2.44772 2 2 2.44772 2 3V5C2 5.55228 2.44772 6 3 6H5C5.55228 6 6 5.55228 6 5Z" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
<path d="M14 13V11C14 10.4477 13.5523 10 13 10H11C10.4477 10 10 10.4477 10 11V13C10 13.5523 10.4477 14 11 14H13C13.5523 14 14 13.5523 14 13Z" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
<path d="M10 4H6" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
<path d="M10 12H8C5.79086 12 4 10.2091 4 8V6" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 890 B

View File

@@ -7,9 +7,12 @@
}" }"
severity="secondary" severity="secondary"
text text
icon="pi pi-box"
@click="() => commandStore.execute('Comfy.Graph.ConvertToSubgraph')" @click="() => commandStore.execute('Comfy.Graph.ConvertToSubgraph')"
/> >
<template #icon>
<i-comfy:workflow />
</template>
</Button>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -1,5 +1,6 @@
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import dotenv from 'dotenv' import dotenv from 'dotenv'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import IconsResolver from 'unplugin-icons/resolver' import IconsResolver from 'unplugin-icons/resolver'
import Icons from 'unplugin-icons/vite' import Icons from 'unplugin-icons/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
@@ -120,12 +121,19 @@ export default defineConfig({
]), ]),
Icons({ Icons({
compiler: 'vue3' compiler: 'vue3',
customCollections: {
comfy: FileSystemIconLoader('src/assets/icons/custom')
}
}), }),
Components({ Components({
dts: true, dts: true,
resolvers: [IconsResolver()], resolvers: [
IconsResolver({
customCollections: ['comfy']
})
],
dirs: ['src/components', 'src/layout', 'src/views'], dirs: ['src/components', 'src/layout', 'src/views'],
deep: true, deep: true,
extensions: ['vue'] extensions: ['vue']