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 } 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 , rebootAPI } from "./common.js" ;
var docStyle = document . createElement ( 'style' ) ;
docStyle . innerHTML = `
# cm - manager - dialog {
width : 1000 px ;
height : 360 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 : 150 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 ;
}
. cm - notice - board {
width : 310 px ;
padding : 0 px ! important ;
height : 150 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 : # 000000 ! important ;
color : # FF0000 ! important ;
font - size : 13 px ;
border - radius : 5 px ;
padding : 10 px ;
overflow - x : hidden ;
overflow : auto ;
}
. cm - info - note {
background - color : # 000000 ! important ;
color : # FF0000 ! 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 = `
# comfyworkflows - button {
width : 310 px ;
height : 27 px ;
padding : 0 px ! important ;
position : relative ;
overflow : hidden ;
}
# cm - nodeinfo - button {
width : 310 px ;
height : 27 px ;
padding : 0 px ! important ;
position : relative ;
overflow : hidden ;
}
# cm - manual - button {
width : 310 px ;
height : 27 px ;
position : relative ;
overflow : hidden ;
}
. cm - button {
width : 310 px ;
height : 27 px ;
position : relative ;
overflow : hidden ;
}
# 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 ) ;
}
# comfyworkflows - 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 = { } ;
for ( let i in mappings ) {
let item = mappings [ i ] ;
var nickname ;
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 ;
}
}
return result ;
}
let nicknames = await fetchNicknames ( ) ;
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 = "url" ;
if ( manager _instance . local _mode _checkbox . checked )
mode = "local" ;
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.' ) ;
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 = "url" ;
if ( manager _instance . local _mode _checkbox . checked )
mode = "local" ;
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 ) {
app . ui . dialog . show ( "ComfyUI and all extensions have been updated to the latest version.<BR>To apply the updated custom node, please <button id='cm-reboot-button'><font size='3px'>RESTART</font></button> ComfyUI. And refresh browser." ) ;
const rebootButton = document . getElementById ( 'cm-reboot-button' ) ;
rebootButton . onclick = 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 [
"VHS_VideoCombine" ,
"PreviewImage" ,
"SaveImage" ,
"ADE_AnimateDiffCombine" ,
"SaveAnimatedWEBP" ,
] . includes ( node . type ) ;
}
// -----------
class ManagerMenuDialog extends ComfyDialog {
local _mode _checkbox = null ;
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 ( false ) ;
}
} ) ,
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Install Missing Custom Nodes" ,
onclick :
( ) => {
if ( ! CustomNodesInstaller . instance )
CustomNodesInstaller . instance = new CustomNodesInstaller ( app , self ) ;
CustomNodesInstaller . instance . show ( true ) ;
}
} ) ,
$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 . local _mode _checkbox = $el ( "input" , { type : 'checkbox' , id : "use_local_db" } , [ ] )
const checkbox _text = $el ( "label" , { for : "use_local_db" } , [ " Use local DB" ] )
checkbox _text . style . color = "var(--fg-color)" ;
checkbox _text . style . cursor = "pointer" ;
checkbox _text . style . marginRight = "10px" ;
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 ;
// preview method
let preview _combo = document . createElement ( "select" ) ;
preview _combo . style . cursor = "pointer" ;
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 . style . cursor = "pointer" ;
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 . style . cursor = "pointer" ;
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 ) {
}
} ) ;
// share
let share _combo = document . createElement ( "select" ) ;
share _combo . style . cursor = "pointer" ;
const share _options = [
[ 'none' , 'None' ] ,
[ 'openart' , 'OpenArt AI' ] ,
[ '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 . local _mode _checkbox , checkbox _text , this . update _check _checkbox , uc _checkbox _text ] ) ,
$el ( "br" , { } , [ ] ) ,
preview _combo ,
badge _combo ,
channel _combo ,
share _combo ,
$el ( "hr" , { } , [ ] ) ,
$el ( "center" , { } , [ "!! EXPERIMENTAL !!" ] ) ,
$el ( "br" , { } , [ ] ) ,
$el ( "button.cm-button" , {
type : "button" ,
textContent : "Snapshot Manager" ,
onclick :
( ) => {
if ( ! SnapshotManager . instance )
SnapshotManager . instance = new SnapshotManager ( app , self ) ;
SnapshotManager . instance . show ( ) ;
}
} ) ,
$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 ) ;
}
}
} ) ,
] ;
}
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 : ( ) => {
this . close ( ) ;
} ,
}
] ,
{
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 : 'comfyworkflows-button' ,
type : "button" ,
textContent : "Workflow Gallery" ,
onclick : ( ) => { window . open ( "https://comfyworkflows.com/" , "comfyui-workflow-gallery" ) ; }
} , [
$el ( "div.pysssss-workflow-arrow-2" , {
id : ` comfyworkflows-button-arrow ` ,
onclick : ( e ) => {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
LiteGraph . closeAllContextMenus ( ) ;
const menu = new LiteGraph . ContextMenu (
[
{
title : "Share your art" ,
callback : ( ) => {
this . close ( ) ;
if ( ! ShareDialog . instance ) {
ShareDialog . instance = new ShareDialog ( ) ;
}
app . graphToPrompt ( ) . then ( prompt => {
// console.log({ prompt })
return app . graph . _nodes ;
} ) . then ( nodes => {
// console.log({ nodes });
const { potential _outputs , potential _output _nodes } = getPotentialOutputsAndOutputNodes ( nodes ) ;
if ( potential _outputs . length === 0 ) {
if ( potential _output _nodes . length === 0 ) {
// todo: add support for other output node types (animatediff combine, etc.)
const supported _nodes _string = SUPPORTED _OUTPUT _NODE _TYPES . join ( ", " ) ;
alert ( ` No supported output node found ( ${ supported _nodes _string } ). To share this workflow, please add an output node to your graph and re-run your prompt. ` ) ;
} else {
alert ( "To share this, first run a prompt. Once it's done, click 'Share'." ) ;
}
return ;
}
ShareDialog . instance . show ( { potential _outputs , potential _output _nodes } ) ;
} ) ;
} ,
} ,
{
title : "Close" ,
callback : ( ) => {
this . close ( ) ;
} ,
}
] ,
{
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 = "comfyworkflows-button-menu" ;
menu . root . classList . add ( "pysssss-workflow-popup-2" ) ;
} ,
} )
] ) ,
$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" ;
}
}
app . registerExtension ( {
name : "Comfy.ManagerMenu" ,
init ( ) {
$el ( "style" , {
textContent : style ,
parent : document . head ,
} ) ;
} ,
async setup ( ) {
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 ;
}
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 ) {
const onDrawForeground = nodeType . prototype . onDrawForeground ;
nodeType . prototype . onDrawForeground = function ( ctx ) {
const r = onDrawForeground ? . apply ? . ( this , arguments ) ;
if ( ! this . flags . collapsed && badge _mode != 'none' && nodeType . title _mode != LiteGraph . NO _TITLE ) {
let text = "" ;
if ( badge _mode . startsWith ( 'id_nick' ) )
text = ` # ${ this . id } ` ;
if ( nicknames [ nodeData . name . trim ( ) ] ) {
let nick = nicknames [ nodeData . name . trim ( ) ] ;
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 ( this . size [ 0 ] - sz . width - 12 , - LiteGraph . NODE _TITLE _HEIGHT - 20 , sz . width + 12 , 20 , 5 ) ;
ctx . fill ( ) ;
ctx . fillStyle = fgColor ;
ctx . fillText ( text , this . size [ 0 ] - sz . width - 6 , - LiteGraph . NODE _TITLE _HEIGHT - 6 ) ;
ctx . restore ( ) ;
}
}
return r ;
} ;
this . _addExtraNodeContextMenu ( nodeType , app ) ;
} ,
async loadedGraphNode ( node , app ) {
if ( node . has _errors ) {
const onDrawForeground = node . onDrawForeground ;
node . onDrawForeground = function ( ctx ) {
const r = onDrawForeground ? . apply ? . ( this , arguments ) ;
if ( ! this . flags . collapsed && badge _mode != 'none' ) {
let text = "" ;
if ( badge _mode . startsWith ( 'id_nick' ) )
text = ` # ${ this . id } ` ;
if ( nicknames [ node . type . trim ( ) ] ) {
let nick = nicknames [ node . type . trim ( ) ] ;
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 ( this . size [ 0 ] - sz . width - 12 , - LiteGraph . NODE _TITLE _HEIGHT - 20 , sz . width + 12 , 20 , 5 ) ;
ctx . fill ( ) ;
ctx . fillStyle = fgColor ;
ctx . fillText ( text , this . size [ 0 ] - sz . width - 6 , - LiteGraph . NODE _TITLE _HEIGHT - 6 ) ;
ctx . restore ( ) ;
ctx . save ( ) ;
ctx . font = "bold 14px sans-serif" ;
const sz2 = ctx . measureText ( node . type ) ;
ctx . fillStyle = 'white' ;
ctx . fillText ( node . type , this . size [ 0 ] / 2 - sz2 . width / 2 , this . size [ 1 ] / 2 ) ;
ctx . restore ( ) ;
}
}
return r ;
} ;
}
} ,
_addExtraNodeContextMenu ( node , app ) {
const origGetExtraMenuOptions = node . prototype . getExtraMenuOptions ;
node . prototype . getExtraMenuOptions = function ( _ , options ) {
origGetExtraMenuOptions ? . apply ? . ( this , arguments ) ;
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 ) ;
}
}
} ,
} ) ;