import { app } from "../../scripts/app.js" ;
import { api } from "../../scripts/api.js"
import { ComfyDialog , $el } from "../../scripts/ui.js" ;
import {
ShareDialog ,
SUPPORTED _OUTPUT _NODE _TYPES ,
getPotentialOutputsAndOutputNodes ,
ShareDialogChooser ,
showOpenArtShareDialog ,
showShareDialog ,
showYouMLShareDialog
} from "./comfyui-share-common.js" ;
import { OpenArtShareDialog } from "./comfyui-share-openart.js" ;
import { CustomNodesInstaller } from "./custom-nodes-downloader.js" ;
import { AlternativesInstaller } from "./a1111-alter-downloader.js" ;
import { SnapshotManager } from "./snapshot.js" ;
import { ModelInstaller } from "./model-downloader.js" ;
import { manager _instance , setManagerInstance , install _via _git _url , install _pip , rebootAPI , free _models } from "./common.js" ;
import { load _components , save _as _component } from "./components-manager.js" ;
var docStyle = document . createElement ( 'style' ) ;
docStyle . innerHTML = `
# cm - manager - dialog {
width : 1000 px ;
height : 495 px ;
box - sizing : content - box ;
z - index : 10000 ;
}
. cm - menu - container {
column - gap : 20 px ;
display : flex ;
flex - wrap : wrap ;
justify - content : center ;
box - sizing : content - box ;
}
. cm - menu - column {
display : flex ;
flex - direction : column ;
flex : 1 1 auto ;
width : 300 px ;
box - sizing : content - box ;
}
. cm - title {
background - color : black ;
text - align : center ;
height : 40 px ;
width : calc ( 100 % - 10 px ) ;
font - weight : bold ;
justify - content : center ;
align - content : center ;
vertical - align : middle ;
}
# cm - channel - badge {
color : white ;
background - color : # AA0000 ;
width : 220 px ;
height : 23 px ;
font - size : 13 px ;
border - radius : 5 px ;
left : 5 px ;
top : 5 px ;
align - content : center ;
justify - content : center ;
text - align : center ;
font - weight : bold ;
float : left ;
vertical - align : middle ;
position : relative ;
}
# custom - nodes - grid a {
color : # 5555 FF ;
font - weight : bold ;
text - decoration : none ;
}
# custom - nodes - grid a : hover {
color : # 7777 FF ;
text - decoration : underline ;
}
# external - models - grid a {
color : # 5555 FF ;
font - weight : bold ;
text - decoration : none ;
}
# external - models - grid a : hover {
color : # 7777 FF ;
text - decoration : underline ;
}
# alternatives - grid a {
color : # 5555 FF ;
font - weight : bold ;
text - decoration : none ;
}
# alternatives - grid a : hover {
color : # 7777 FF ;
text - decoration : underline ;
}
. cm - notice - board {
width : 310 px ;
padding : 0 px ! important ;
height : 260 px ;
overflow : auto ;
color : var ( -- input - text ) ;
border : 1 px solid var ( -- descrip - text ) ;
padding : 10 px ;
overflow - x : hidden ;
}
. cm - conflicted - nodes - text {
background - color : # CCCC55 ! important ;
color : # AA3333 ! important ;
font - size : 10 px ;
border - radius : 5 px ;
padding : 10 px ;
}
. cm - warn - note {
background - color : # 101010 ! important ;
color : # FF3800 ! important ;
font - size : 13 px ;
border - radius : 5 px ;
padding : 10 px ;
overflow - x : hidden ;
overflow : auto ;
}
. cm - info - note {
background - color : # 101010 ! important ;
color : # FF3800 ! important ;
font - size : 13 px ;
border - radius : 5 px ;
padding : 10 px ;
overflow - x : hidden ;
overflow : auto ;
}
` ;
document . head . appendChild ( docStyle ) ;
var update _comfyui _button = null ;
var fetch _updates _button = null ;
var update _all _button = null ;
var badge _mode = "none" ;
let share _option = 'all' ;
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
const style = `
# workflowgallery - button {
width : 310 px ;
height : 27 px ;
padding : 0 px ! important ;
position : relative ;
overflow : hidden ;
font - size : 17 px ! important ;
}
# cm - nodeinfo - button {
width : 310 px ;
height : 27 px ;
padding : 0 px ! important ;
position : relative ;
overflow : hidden ;
font - size : 17 px ! important ;
}
# cm - manual - button {
width : 310 px ;
height : 27 px ;
position : relative ;
overflow : hidden ;
}
. cm - button {
width : 310 px ;
height : 30 px ;
position : relative ;
overflow : hidden ;
font - size : 17 px ! important ;
}
. cm - experimental - button {
width : 290 px ;
height : 30 px ;
position : relative ;
overflow : hidden ;
font - size : 17 px ! important ;
}
. cm - experimental {
width : 310 px ;
border : 1 px solid # 555 ;
border - radius : 5 px ;
padding : 10 px ;
align - items : center ;
text - align : center ;
justify - content : center ;
box - sizing : border - box ;
}
. cm - experimental - legend {
margin - top : - 20 px ;
margin - left : 50 % ;
width : auto ;
height : 20 px ;
font - size : 13 px ;
font - weight : bold ;
background - color : # 990000 ;
color : # CCFFFF ;
border - radius : 5 px ;
text - align : center ;
transform : translateX ( - 50 % ) ;
}
. cm - menu - combo {
cursor : pointer ;
width : 310 px ;
box - sizing : border - box ;
}
. cm - small - button {
width : 120 px ;
height : 30 px ;
position : relative ;
overflow : hidden ;
box - sizing : border - box ;
font - size : 17 px ! important ;
}
# cm - install - customnodes - button {
width : 200 px ;
height : 30 px ;
position : relative ;
overflow : hidden ;
box - sizing : border - box ;
font - size : 17 px ! important ;
}
. cm - search - filter {
width : 200 px ;
height : 30 px ! important ;
position : relative ;
overflow : hidden ;
box - sizing : border - box ;
}
# cm - close - button {
width : calc ( 100 % - 65 px ) ;
bottom : 10 px ;
position : absolute ;
overflow : hidden ;
}
. pysssss - workflow - arrow - 2 {
position : absolute ;
top : 0 ;
bottom : 0 ;
right : 0 ;
font - size : 12 px ;
display : flex ;
align - items : center ;
width : 24 px ;
justify - content : center ;
background : rgba ( 255 , 255 , 255 , 0.1 ) ;
content : "▼" ;
}
. pysssss - workflow - arrow - 2 : after {
content : "▼" ;
}
. pysssss - workflow - arrow - 2 : hover {
filter : brightness ( 1.6 ) ;
background - color : var ( -- comfy - menu - bg ) ;
}
. pysssss - workflow - popup - 2 ~ . litecontextmenu {
transform : scale ( 1.3 ) ;
}
# workflowgallery - button - menu {
z - index : 10000000000 ! important ;
}
# cm - manual - button - menu {
z - index : 10000000000 ! important ;
}
` ;
async function init _badge _mode ( ) {
api . fetchApi ( '/manager/badge_mode' )
. then ( response => response . text ( ) )
. then ( data => { badge _mode = data ; } )
}
async function init _share _option ( ) {
api . fetchApi ( '/manager/share_option' )
. then ( response => response . text ( ) )
. then ( data => {
share _option = data || 'all' ;
} ) ;
}
async function init _notice ( notice ) {
api . fetchApi ( '/manager/notice' )
. then ( response => response . text ( ) )
. then ( data => {
notice . innerHTML = data ;
} )
}
await init _badge _mode ( ) ;
await init _share _option ( ) ;
async function fetchNicknames ( ) {
const response1 = await api . fetchApi ( ` /customnode/getmappings?mode=local ` ) ;
const mappings = await response1 . json ( ) ;
let result = { } ;
let nickname _patterns = [ ] ;
for ( let i in mappings ) {
let item = mappings [ i ] ;
var nickname ;
if ( item [ 1 ] . nickname ) {
nickname = item [ 1 ] . nickname ;
}
else if ( item [ 1 ] . title ) {
nickname = item [ 1 ] . title ;
}
else {
nickname = item [ 1 ] . title _aux ;
}
for ( let j in item [ 0 ] ) {
result [ item [ 0 ] [ j ] ] = nickname ;
}
if ( item [ 1 ] . nodename _pattern ) {
nickname _patterns . push ( [ item [ 1 ] . nodename _pattern , nickname ] ) ;
}
}
return [ result , nickname _patterns ] ;
}
const [ nicknames , nickname _patterns ] = await fetchNicknames ( ) ;
function getNickname ( node , nodename ) {
if ( node . nickname ) {
return node . nickname ;
}
else {
if ( nicknames [ nodename ] ) {
node . nickname = nicknames [ nodename ] ;
}
else {
for ( let i in nickname _patterns ) {
let item = nickname _patterns [ i ] ;
if ( nodename . match ( item [ 0 ] ) ) {
node . nickname = item [ 1 ] ;
}
}
}
return node . nickname ;
}
}
function drawBadge ( node , orig , restArgs ) {
let ctx = restArgs [ 0 ] ;
const r = orig ? . apply ? . ( node , restArgs ) ;
if ( ! node . flags . collapsed && badge _mode != 'none' && node . constructor . title _mode != LiteGraph . NO _TITLE ) {
let text = "" ;
if ( badge _mode . startsWith ( 'id_nick' ) )
text = ` # ${ node . id } ` ;
let nick = node . getNickname ( ) ;
if ( nick ) {
if ( nick == 'ComfyUI' ) {
if ( badge _mode . endsWith ( 'hide' ) ) {
nick = "" ;
}
else {
nick = "🦊"
}
}
if ( nick . length > 25 ) {
text += nick . substring ( 0 , 23 ) + ".." ;
}
else {
text += nick ;
}
}
if ( text != "" ) {
let fgColor = "white" ;
let bgColor = "#0F1F0F" ;
let visible = true ;
ctx . save ( ) ;
ctx . font = "12px sans-serif" ;
const sz = ctx . measureText ( text ) ;
ctx . fillStyle = bgColor ;
ctx . beginPath ( ) ;
ctx . roundRect ( node . size [ 0 ] - sz . width - 12 , - LiteGraph . NODE _TITLE _HEIGHT - 20 , sz . width + 12 , 20 , 5 ) ;
ctx . fill ( ) ;
ctx . fillStyle = fgColor ;
ctx . fillText ( text , node . size [ 0 ] - sz . width - 6 , - LiteGraph . NODE _TITLE _HEIGHT - 6 ) ;
ctx . restore ( ) ;
if ( node . has _errors ) {
ctx . save ( ) ;
ctx . font = "bold 14px sans-serif" ;
const sz2 = ctx . measureText ( node . type ) ;
ctx . fillStyle = 'white' ;
ctx . fillText ( node . type , node . size [ 0 ] / 2 - sz2 . width / 2 , node . size [ 1 ] / 2 ) ;
ctx . restore ( ) ;
}
}
}
return r ;
}
async function updateComfyUI ( ) {
let prev _text = update _comfyui _button . innerText ;
update _comfyui _button . innerText = "Updating ComfyUI..." ;
update _comfyui _button . disabled = true ;
update _comfyui _button . style . backgroundColor = "gray" ;
try {
const response = await api . fetchApi ( '/comfyui_manager/update_comfyui' ) ;
if ( response . status == 400 ) {
app . ui . dialog . show ( 'Failed to update ComfyUI.' ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
return false ;
}
if ( response . status == 201 ) {
app . ui . dialog . show ( 'ComfyUI has been successfully updated.' ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
}
else {
app . ui . dialog . show ( 'ComfyUI is already up to date with the latest version.' ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
}
return true ;
}
catch ( exception ) {
app . ui . dialog . show ( ` Failed to update ComfyUI / ${ exception } ` ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
return false ;
}
finally {
update _comfyui _button . disabled = false ;
update _comfyui _button . innerText = prev _text ;
update _comfyui _button . style . backgroundColor = "" ;
}
}
async function fetchUpdates ( update _check _checkbox ) {
let prev _text = fetch _updates _button . innerText ;
fetch _updates _button . innerText = "Fetching updates..." ;
fetch _updates _button . disabled = true ;
fetch _updates _button . style . backgroundColor = "gray" ;
try {
var mode = manager _instance . datasrc _combo . value ;
const response = await api . fetchApi ( ` /customnode/fetch_updates?mode= ${ mode } ` ) ;
if ( response . status != 200 && response . status != 201 ) {
app . ui . dialog . show ( 'Failed to fetch updates.' ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
return false ;
}
if ( response . status == 201 ) {
app . ui . dialog . show ( "There is an updated extension available.<BR><BR><P><B>NOTE:<BR>Fetch Updates is not an update.<BR>Please update from <button id='cm-install-customnodes-button'>Install Custom Nodes</button> </B></P>" ) ;
const button = document . getElementById ( 'cm-install-customnodes-button' ) ;
button . addEventListener ( "click" ,
async function ( ) {
app . ui . dialog . close ( ) ;
if ( ! CustomNodesInstaller . instance )
CustomNodesInstaller . instance = new CustomNodesInstaller ( app , self ) ;
await CustomNodesInstaller . instance . show ( CustomNodesInstaller . ShowMode . UPDATE ) ;
}
) ;
app . ui . dialog . element . style . zIndex = 10010 ;
update _check _checkbox . checked = false ;
}
else {
app . ui . dialog . show ( 'All extensions are already up-to-date with the latest versions.' ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
}
return true ;
}
catch ( exception ) {
app . ui . dialog . show ( ` Failed to update custom nodes / ${ exception } ` ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
return false ;
}
finally {
fetch _updates _button . disabled = false ;
fetch _updates _button . innerText = prev _text ;
fetch _updates _button . style . backgroundColor = "" ;
}
}
async function updateAll ( update _check _checkbox , manager _dialog ) {
let prev _text = update _all _button . innerText ;
update _all _button . innerText = "Updating all...(ComfyUI)" ;
update _all _button . disabled = true ;
update _all _button . style . backgroundColor = "gray" ;
try {
var mode = manager _instance . datasrc _combo . value ;
update _all _button . innerText = "Updating all..." ;
const response1 = await api . fetchApi ( '/comfyui_manager/update_comfyui' ) ;
const response2 = await api . fetchApi ( ` /customnode/update_all?mode= ${ mode } ` ) ;
if ( response1 . status != 200 && response2 . status != 201 ) {
app . ui . dialog . show ( 'Failed to update ComfyUI or several extensions.<BR><BR>See terminal log.<BR>' ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
return false ;
}
if ( response1 . status == 201 || response2 . status == 201 ) {
const update _info = await response2 . json ( ) ;
let failed _list = "" ;
if ( update _info . failed . length > 0 ) {
failed _list = "<BR>FAILED: " + update _info . failed . join ( ", " ) ;
}
let updated _list = "" ;
if ( update _info . updated . length > 0 ) {
updated _list = "<BR>UPDATED: " + update _info . updated . join ( ", " ) ;
}
app . ui . dialog . show (
"ComfyUI and all extensions have been updated to the latest version.<BR>To apply the updated custom node, please <button class='cm-small-button' id='cm-reboot-button'>RESTART</button> ComfyUI. And refresh browser.<BR>"
+ failed _list
+ updated _list
) ;
const rebootButton = document . getElementById ( 'cm-reboot-button' ) ;
rebootButton . addEventListener ( "click" ,
function ( ) {
if ( rebootAPI ( ) ) {
manager _dialog . close ( ) ;
}
} ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
}
else {
app . ui . dialog . show ( 'ComfyUI and all extensions are already up-to-date with the latest versions.' ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
}
return true ;
}
catch ( exception ) {
app . ui . dialog . show ( ` Failed to update ComfyUI or several extensions / ${ exception } ` ) ;
app . ui . dialog . element . style . zIndex = 10010 ;
return false ;
}
finally {
update _all _button . disabled = false ;
update _all _button . innerText = prev _text ;
update _all _button . style . backgroundColor = "" ;
}
}
function newDOMTokenList ( initialTokens ) {
const tmp = document . createElement ( ` div ` ) ;
const classList = tmp . classList ;
if ( initialTokens ) {
initialTokens . forEach ( token => {
classList . add ( token ) ;
} ) ;
}
return classList ;
}
/ * *
* Check whether the node is a potential output node ( img , gif or video output )
* /
const isOutputNode = ( node ) => {
return SUPPORTED _OUTPUT _NODE _TYPES . includes ( node . type ) ;
}
// -----------
class ManagerMenuDialog extends ComfyDialog {
createControlsMid ( ) {
let self = this ;
update _comfyui _button =
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Update ComfyUI" ,
onclick :
( ) => updateComfyUI ( )
} ) ;
fetch _updates _button =
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Fetch Updates" ,
onclick :
( ) => fetchUpdates ( this . update _check _checkbox )
} ) ;
update _all _button =
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Update All" ,
onclick :
( ) => updateAll ( this . update _check _checkbox , self )
} ) ;
const res =
[
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Install Custom Nodes" ,
onclick :
( ) => {
if ( ! CustomNodesInstaller . instance )
CustomNodesInstaller . instance = new CustomNodesInstaller ( app , self ) ;
CustomNodesInstaller . instance . show ( CustomNodesInstaller . ShowMode . NORMAL ) ;
}
} ) ,
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Install Missing Custom Nodes" ,
onclick :
( ) => {
if ( ! CustomNodesInstaller . instance )
CustomNodesInstaller . instance = new CustomNodesInstaller ( app , self ) ;
CustomNodesInstaller . instance . show ( CustomNodesInstaller . ShowMode . MISSING _NODES ) ;
}
} ) ,
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Install Models" ,
onclick :
( ) => {
if ( ! ModelInstaller . instance )
ModelInstaller . instance = new ModelInstaller ( app , self ) ;
ModelInstaller . instance . show ( ) ;
}
} ) ,
$el ( "br" , { } , [ ] ) ,
update _all _button ,
update _comfyui _button ,
fetch _updates _button ,
$el ( "br" , { } , [ ] ) ,
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Alternatives of A1111" ,
onclick :
( ) => {
if ( ! AlternativesInstaller . instance )
AlternativesInstaller . instance = new AlternativesInstaller ( app , self ) ;
AlternativesInstaller . instance . show ( ) ;
}
} )
] ;
return res ;
}
createControlsLeft ( ) {
let self = this ;
this . update _check _checkbox = $el ( "input" , { type : 'checkbox' , id : "skip_update_check" } , [ ] )
const uc _checkbox _text = $el ( "label" , { for : "skip_update_check" } , [ " Skip update check" ] )
uc _checkbox _text . style . color = "var(--fg-color)" ;
uc _checkbox _text . style . cursor = "pointer" ;
this . update _check _checkbox . checked = true ;
// db mode
this . datasrc _combo = document . createElement ( "select" ) ;
this . datasrc _combo . className = "cm-menu-combo" ;
this . datasrc _combo . appendChild ( $el ( 'option' , { value : 'cache' , text : 'DB: Channel (1day cache)' } , [ ] ) ) ;
this . datasrc _combo . appendChild ( $el ( 'option' , { value : 'local' , text : 'DB: Local' } , [ ] ) ) ;
this . datasrc _combo . appendChild ( $el ( 'option' , { value : 'url' , text : 'DB: Channel (remote)' } , [ ] ) ) ;
// preview method
let preview _combo = document . createElement ( "select" ) ;
preview _combo . className = "cm-menu-combo" ;
preview _combo . appendChild ( $el ( 'option' , { value : 'auto' , text : 'Preview method: Auto' } , [ ] ) ) ;
preview _combo . appendChild ( $el ( 'option' , { value : 'taesd' , text : 'Preview method: TAESD (slow)' } , [ ] ) ) ;
preview _combo . appendChild ( $el ( 'option' , { value : 'latent2rgb' , text : 'Preview method: Latent2RGB (fast)' } , [ ] ) ) ;
preview _combo . appendChild ( $el ( 'option' , { value : 'none' , text : 'Preview method: None (very fast)' } , [ ] ) ) ;
api . fetchApi ( '/manager/preview_method' )
. then ( response => response . text ( ) )
. then ( data => { preview _combo . value = data ; } ) ;
preview _combo . addEventListener ( 'change' , function ( event ) {
api . fetchApi ( ` /manager/preview_method?value= ${ event . target . value } ` ) ;
} ) ;
// nickname
let badge _combo = document . createElement ( "select" ) ;
badge _combo . className = "cm-menu-combo" ;
badge _combo . appendChild ( $el ( 'option' , { value : 'none' , text : 'Badge: None' } , [ ] ) ) ;
badge _combo . appendChild ( $el ( 'option' , { value : 'nick' , text : 'Badge: Nickname' } , [ ] ) ) ;
badge _combo . appendChild ( $el ( 'option' , { value : 'nick_hide' , text : 'Badge: Nickname (hide built-in)' } , [ ] ) ) ;
badge _combo . appendChild ( $el ( 'option' , { value : 'id_nick' , text : 'Badge: #ID Nickname' } , [ ] ) ) ;
badge _combo . appendChild ( $el ( 'option' , { value : 'id_nick_hide' , text : 'Badge: #ID Nickname (hide built-in)' } , [ ] ) ) ;
api . fetchApi ( '/manager/badge_mode' )
. then ( response => response . text ( ) )
. then ( data => { badge _combo . value = data ; badge _mode = data ; } ) ;
badge _combo . addEventListener ( 'change' , function ( event ) {
api . fetchApi ( ` /manager/badge_mode?value= ${ event . target . value } ` ) ;
badge _mode = event . target . value ;
app . graph . setDirtyCanvas ( true ) ;
} ) ;
// channel
let channel _combo = document . createElement ( "select" ) ;
channel _combo . className = "cm-menu-combo" ;
api . fetchApi ( '/manager/channel_url_list' )
. then ( response => response . json ( ) )
. then ( async data => {
try {
let urls = data . list ;
for ( let i in urls ) {
if ( urls [ i ] != '' ) {
let name _url = urls [ i ] . split ( '::' ) ;
channel _combo . appendChild ( $el ( 'option' , { value : name _url [ 0 ] , text : ` Channel: ${ name _url [ 0 ] } ` } , [ ] ) ) ;
}
}
channel _combo . addEventListener ( 'change' , function ( event ) {
api . fetchApi ( ` /manager/channel_url_list?value= ${ event . target . value } ` ) ;
} ) ;
channel _combo . value = data . selected ;
}
catch ( exception ) {
}
} ) ;
// default ui state
let default _ui _combo = document . createElement ( "select" ) ;
default _ui _combo . className = "cm-menu-combo" ;
default _ui _combo . appendChild ( $el ( 'option' , { value : 'none' , text : 'Default UI: None' } , [ ] ) ) ;
default _ui _combo . appendChild ( $el ( 'option' , { value : 'history' , text : 'Default UI: History' } , [ ] ) ) ;
default _ui _combo . appendChild ( $el ( 'option' , { value : 'queue' , text : 'Default UI: Queue' } , [ ] ) ) ;
api . fetchApi ( '/manager/default_ui' )
. then ( response => response . text ( ) )
. then ( data => { default _ui _combo . value = data ; } ) ;
default _ui _combo . addEventListener ( 'change' , function ( event ) {
api . fetchApi ( ` /manager/default_ui?value= ${ event . target . value } ` ) ;
} ) ;
// share
let share _combo = document . createElement ( "select" ) ;
share _combo . className = "cm-menu-combo" ;
const share _options = [
[ 'none' , 'None' ] ,
[ 'openart' , 'OpenArt AI' ] ,
[ 'youml' , 'YouML' ] ,
[ 'matrix' , 'Matrix Server' ] ,
[ 'comfyworkflows' , 'ComfyWorkflows' ] ,
[ 'all' , 'All' ] ,
] ;
for ( const option of share _options ) {
share _combo . appendChild ( $el ( 'option' , { value : option [ 0 ] , text : ` Share: ${ option [ 1 ] } ` } , [ ] ) ) ;
}
api . fetchApi ( '/manager/share_option' )
. then ( response => response . text ( ) )
. then ( data => {
share _combo . value = data || 'all' ;
share _option = data || 'all' ;
} ) ;
share _combo . addEventListener ( 'change' , function ( event ) {
const value = event . target . value ;
share _option = value ;
api . fetchApi ( ` /manager/share_option?value= ${ value } ` ) ;
const shareButton = document . getElementById ( "shareButton" ) ;
if ( value === 'none' ) {
shareButton . style . display = "none" ;
} else {
shareButton . style . display = "inline-block" ;
}
} ) ;
return [
$el ( "div" , { } , [ this . update _check _checkbox , uc _checkbox _text ] ) ,
$el ( "br" , { } , [ ] ) ,
this . datasrc _combo ,
channel _combo ,
preview _combo ,
badge _combo ,
default _ui _combo ,
share _combo ,
$el ( "br" , { } , [ ] ) ,
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Install via Git URL" ,
onclick : ( ) => {
var url = prompt ( "Please enter the URL of the Git repository to install" , "" ) ;
if ( url !== null ) {
install _via _git _url ( url , self ) ;
}
}
} ) ,
$el ( "br" , { } , [ ] ) ,
$el ( "filedset.cm-experimental" , { } , [
$el ( "legend.cm-experimental-legend" , { } , [ "EXPERIMENTAL" ] ) ,
$el ( "button.cm-experimental-button" , {
type : "button" ,
textContent : "Snapshot Manager" ,
onclick :
( ) => {
if ( ! SnapshotManager . instance )
SnapshotManager . instance = new SnapshotManager ( app , self ) ;
SnapshotManager . instance . show ( ) ;
}
} ) ,
$el ( "button.cm-experimental-button" , {
type : "button" ,
textContent : "Install PIP packages" ,
onclick :
( ) => {
var url = prompt ( "Please enumerate the pip packages to be installed.\n\nExample: insightface opencv-python-headless>=4.1.1\n" , "" ) ;
if ( url !== null ) {
install _pip ( url , self ) ;
}
}
} ) ,
$el ( "button.cm-experimental-button" , {
type : "button" ,
textContent : "Unload models" ,
onclick : ( ) => { free _models ( ) ; }
} )
] ) ,
] ;
}
createControlsRight ( ) {
const elts = [
$el ( "button.cm-button" , {
id : 'cm-manual-button' ,
type : "button" ,
textContent : "Community Manual" ,
onclick : ( ) => { window . open ( "https://blenderneko.github.io/ComfyUI-docs/" , "comfyui-community-manual" ) ; }
} , [
$el ( "div.pysssss-workflow-arrow-2" , {
id : ` cm-manual-button-arrow ` ,
onclick : ( e ) => {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
LiteGraph . closeAllContextMenus ( ) ;
const menu = new LiteGraph . ContextMenu (
[
{
title : "Comfy Custom Node How To" ,
callback : ( ) => { window . open ( "https://github.com/chrisgoringe/Comfy-Custom-Node-How-To/wiki/aaa_index" , "comfyui-community-manual1" ) ; } ,
} ,
{
title : "ComfyUI Guide To Making Custom Nodes" ,
callback : ( ) => { window . open ( "https://github.com/Suzie1/ComfyUI_Guide_To_Making_Custom_Nodes/wiki" , "comfyui-community-manual2" ) ; } ,
} ,
{
title : "ComfyUI Examples" ,
callback : ( ) => { window . open ( "https://comfyanonymous.github.io/ComfyUI_examples" , "comfyui-community-manual3" ) ; } ,
} ,
{
title : "Close" ,
callback : ( ) => {
LiteGraph . closeAllContextMenus ( ) ;
} ,
}
] ,
{
event : e ,
scale : 1.3 ,
} ,
window
) ;
// set the id so that we can override the context menu's z-index to be above the comfyui manager menu
menu . root . id = "cm-manual-button-menu" ;
menu . root . classList . add ( "pysssss-workflow-popup-2" ) ;
} ,
} )
] ) ,
$el ( "button" , {
id : 'workflowgallery-button' ,
type : "button" ,
style : {
... ( localStorage . getItem ( "wg_last_visited" ) ? { height : '50px' } : { } )
} ,
onclick : ( e ) => {
const last _visited _site = localStorage . getItem ( "wg_last_visited" )
if ( ! ! last _visited _site ) {
window . open ( last _visited _site , "comfyui-workflow-gallery" ) ;
} else {
this . handleWorkflowGalleryButtonClick ( e )
}
} ,
} , [
$el ( "p" , {
textContent : 'Workflow Gallery' ,
style : {
'text-align' : 'center' ,
'color' : 'white' ,
'font-size' : '18px' ,
'margin' : 0 ,
'padding' : 0 ,
}
} , [
$el ( "p" , {
id : 'workflowgallery-button-last-visited-label' ,
textContent : ` ( ${ localStorage . getItem ( "wg_last_visited" ) ? localStorage . getItem ( "wg_last_visited" ) . split ( '/' ) [ 2 ] : '' } ) ` ,
style : {
'text-align' : 'center' ,
'color' : 'white' ,
'font-size' : '12px' ,
'margin' : 0 ,
'padding' : 0 ,
}
} )
] ) ,
$el ( "div.pysssss-workflow-arrow-2" , {
id : ` comfyworkflows-button-arrow ` ,
onclick : this . handleWorkflowGalleryButtonClick
} )
] ) ,
$el ( "button.cm-button" , {
id : 'cm-nodeinfo-button' ,
type : "button" ,
textContent : "Nodes Info" ,
onclick : ( ) => { window . open ( "https://ltdrdata.github.io/" , "comfyui-node-info" ) ; }
} ) ,
$el ( "br" , { } , [ ] ) ,
] ;
var textarea = document . createElement ( "div" ) ;
textarea . className = "cm-notice-board" ;
elts . push ( textarea ) ;
init _notice ( textarea ) ;
return elts ;
}
constructor ( ) {
super ( ) ;
const close _button = $el ( "button" , { id : "cm-close-button" , type : "button" , textContent : "Close" , onclick : ( ) => this . close ( ) } ) ;
const content =
$el ( "div.comfy-modal-content" ,
[
$el ( "tr.cm-title" , { } , [
$el ( "font" , { size : 6 , color : "white" } , [ ` ComfyUI Manager Menu ` ] ) ]
) ,
$el ( "br" , { } , [ ] ) ,
$el ( "div.cm-menu-container" ,
[
$el ( "div.cm-menu-column" , [ ... this . createControlsLeft ( ) ] ) ,
$el ( "div.cm-menu-column" , [ ... this . createControlsMid ( ) ] ) ,
$el ( "div.cm-menu-column" , [ ... this . createControlsRight ( ) ] )
] ) ,
$el ( "br" , { } , [ ] ) ,
close _button ,
]
) ;
content . style . width = '100%' ;
content . style . height = '100%' ;
this . element = $el ( "div.comfy-modal" , { id : 'cm-manager-dialog' , parent : document . body } , [ content ] ) ;
}
show ( ) {
this . element . style . display = "block" ;
}
handleWorkflowGalleryButtonClick ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
LiteGraph . closeAllContextMenus ( ) ;
// Modify the style of the button so that the UI can indicate the last
// visited site right away.
const modifyButtonStyle = ( url ) => {
const workflowGalleryButton = document . getElementById ( 'workflowgallery-button' ) ;
workflowGalleryButton . style . height = '50px' ;
const lastVisitedLabel = document . getElementById ( 'workflowgallery-button-last-visited-label' ) ;
lastVisitedLabel . textContent = ` ( ${ url . split ( '/' ) [ 2 ] } ) ` ;
}
const menu = new LiteGraph . ContextMenu (
[
{
title : "Share your art" ,
callback : ( ) => {
if ( share _option === 'openart' ) {
showOpenArtShareDialog ( ) ;
return ;
} else if ( share _option === 'matrix' || share _option === 'comfyworkflows' ) {
showShareDialog ( share _option ) ;
return ;
} else if ( share _option === 'youml' ) {
showYouMLShareDialog ( ) ;
return ;
}
if ( ! ShareDialogChooser . instance ) {
ShareDialogChooser . instance = new ShareDialogChooser ( ) ;
}
ShareDialogChooser . instance . show ( ) ;
} ,
} ,
{
title : "Open 'openart.ai'" ,
callback : ( ) => {
const url = "https://openart.ai/workflows/dev" ;
localStorage . setItem ( "wg_last_visited" , url ) ;
window . open ( url , "comfyui-workflow-gallery" ) ;
modifyButtonStyle ( url ) ;
} ,
} ,
{
title : "Open 'youml.com'" ,
callback : ( ) => {
const url = "https://youml.com/?from=comfyui-share" ;
localStorage . setItem ( "wg_last_visited" , url ) ;
window . open ( url , "comfyui-workflow-gallery" ) ;
modifyButtonStyle ( url ) ;
} ,
} ,
{
title : "Open 'comfyworkflows.com'" ,
callback : ( ) => {
const url = "https://comfyworkflows.com/" ;
localStorage . setItem ( "wg_last_visited" , url ) ;
window . open ( url , "comfyui-workflow-gallery" ) ;
modifyButtonStyle ( url ) ;
} ,
} ,
{
title : "Close" ,
callback : ( ) => {
LiteGraph . closeAllContextMenus ( ) ;
} ,
}
] ,
{
event : e ,
scale : 1.3 ,
} ,
window
) ;
// set the id so that we can override the context menu's z-index to be above the comfyui manager menu
menu . root . id = "workflowgallery-button-menu" ;
menu . root . classList . add ( "pysssss-workflow-popup-2" ) ;
}
}
app . registerExtension ( {
name : "Comfy.ManagerMenu" ,
init ( ) {
$el ( "style" , {
textContent : style ,
parent : document . head ,
} ) ;
} ,
async setup ( ) {
let orig _clear = app . graph . clear ;
app . graph . clear = function ( ) {
orig _clear . call ( app . graph ) ;
load _components ( ) ;
} ;
load _components ( ) ;
const menu = document . querySelector ( ".comfy-menu" ) ;
const separator = document . createElement ( "hr" ) ;
separator . style . margin = "20px 0" ;
separator . style . width = "100%" ;
menu . append ( separator ) ;
const managerButton = document . createElement ( "button" ) ;
managerButton . textContent = "Manager" ;
managerButton . onclick = ( ) => {
if ( ! manager _instance )
setManagerInstance ( new ManagerMenuDialog ( ) ) ;
manager _instance . show ( ) ;
}
menu . append ( managerButton ) ;
const shareButton = document . createElement ( "button" ) ;
shareButton . id = "shareButton" ;
shareButton . textContent = "Share" ;
shareButton . onclick = ( ) => {
if ( share _option === 'openart' ) {
showOpenArtShareDialog ( ) ;
return ;
} else if ( share _option === 'matrix' || share _option === 'comfyworkflows' ) {
showShareDialog ( share _option ) ;
return ;
} else if ( share _option === 'youml' ) {
showYouMLShareDialog ( ) ;
return ;
}
if ( ! ShareDialogChooser . instance ) {
ShareDialogChooser . instance = new ShareDialogChooser ( ) ;
}
ShareDialogChooser . instance . show ( ) ;
}
// make the background color a gradient of blue to green
shareButton . style . background = "linear-gradient(90deg, #00C9FF 0%, #92FE9D 100%)" ;
shareButton . style . color = "black" ;
// Load share option from local storage to determine whether to show
// the share button.
const shouldShowShareButton = share _option !== 'none' ;
shareButton . style . display = shouldShowShareButton ? "inline-block" : "none" ;
menu . append ( shareButton ) ;
} ,
async beforeRegisterNodeDef ( nodeType , nodeData , app ) {
this . _addExtraNodeContextMenu ( nodeType , app ) ;
} ,
async nodeCreated ( node , app ) {
if ( ! node . badge _enabled ) {
node . getNickname = function ( ) { return getNickname ( node , node . comfyClass . trim ( ) ) } ;
const orig = node . _ _proto _ _ . onDrawForeground ;
node . onDrawForeground = function ( ctx ) {
drawBadge ( node , orig , arguments )
} ;
node . badge _enabled = true ;
}
} ,
async loadedGraphNode ( node , app ) {
if ( ! node . badge _enabled ) {
const orig = node . onDrawForeground ;
node . getNickname = function ( ) { return getNickname ( node , node . type . trim ( ) ) } ;
node . onDrawForeground = function ( ctx ) { drawBadge ( node , orig , arguments ) } ;
}
} ,
_addExtraNodeContextMenu ( node , app ) {
const origGetExtraMenuOptions = node . prototype . getExtraMenuOptions ;
node . prototype . getExtraMenuOptions = function ( _ , options ) {
origGetExtraMenuOptions ? . apply ? . ( this , arguments ) ;
if ( node . comfyClass . startsWith ( 'workflow/' ) ) {
options . push ( {
content : "Save As Component" ,
callback : ( obj ) => { save _as _component ( node , app ) ; }
} , null ) ;
}
if ( isOutputNode ( node ) ) {
const { potential _outputs } = getPotentialOutputsAndOutputNodes ( [ this ] ) ;
const hasOutput = potential _outputs . length > 0 ;
// Check if the previous menu option is `null`. If it's not,
// then we need to add a `null` as a separator.
if ( options [ options . length - 1 ] !== null ) {
options . push ( null ) ;
}
options . push ( {
content : "🏞️ Share Output" ,
disabled : ! hasOutput ,
callback : ( obj ) => {
if ( ! ShareDialog . instance ) {
ShareDialog . instance = new ShareDialog ( ) ;
}
const shareButton = document . getElementById ( "shareButton" ) ;
if ( shareButton ) {
const currentNode = this ;
if ( ! OpenArtShareDialog . instance ) {
OpenArtShareDialog . instance = new OpenArtShareDialog ( ) ;
}
OpenArtShareDialog . instance . selectedNodeId = currentNode . id ;
if ( ! ShareDialog . instance ) {
ShareDialog . instance = new ShareDialog ( share _option ) ;
}
ShareDialog . instance . selectedNodeId = currentNode . id ;
shareButton . click ( ) ;
}
}
} , null ) ;
}
}
} ,
} ) ;
async function set _default _ui ( )
{
let res = await api . fetchApi ( '/manager/default_ui' ) ;
if ( res . status == 200 ) {
let mode = await res . text ( ) ;
switch ( mode ) {
case 'history' :
app . ui . queue . hide ( ) ;
app . ui . history . show ( ) ;
break ;
case 'queue' :
app . ui . queue . show ( ) ;
app . ui . history . hide ( ) ;
break ;
default :
// do nothing
break ;
}
}
}
set _default _ui ( ) ;