Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
ComfyUI
Commits
7d79afd4
Commit
7d79afd4
authored
Apr 20, 2023
by
omar92
Browse files
Merge branch 'master_old'
parents
a4049989
96b57a9a
Changes
28
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1790 additions
and
17 deletions
+1790
-17
web/extensions/core/keybinds.js
web/extensions/core/keybinds.js
+76
-0
web/extensions/core/noteNode.js
web/extensions/core/noteNode.js
+41
-0
web/extensions/core/snapToGrid.js
web/extensions/core/snapToGrid.js
+1
-1
web/scripts/app.js
web/scripts/app.js
+30
-10
web/scripts/ui.js
web/scripts/ui.js
+50
-6
web/style.css
web/style.css
+8
-0
web/types/comfy.d.ts
web/types/comfy.d.ts
+78
-0
web/types/litegraph.d.ts
web/types/litegraph.d.ts
+1506
-0
No files found.
web/extensions/core/keybinds.js
0 → 100644
View file @
7d79afd4
import
{
app
}
from
"
/scripts/app.js
"
;
const
id
=
"
Comfy.Keybinds
"
;
app
.
registerExtension
({
name
:
id
,
init
()
{
const
keybindListener
=
function
(
event
)
{
const
modifierPressed
=
event
.
ctrlKey
||
event
.
metaKey
;
// Queue prompt using ctrl or command + enter
if
(
modifierPressed
&&
(
event
.
key
===
"
Enter
"
||
event
.
keyCode
===
13
||
event
.
keyCode
===
10
))
{
app
.
queuePrompt
(
event
.
shiftKey
?
-
1
:
0
);
return
;
}
const
target
=
event
.
composedPath
()[
0
];
if
(
target
.
tagName
===
"
INPUT
"
||
target
.
tagName
===
"
TEXTAREA
"
)
{
return
;
}
const
modifierKeyIdMap
=
{
"
s
"
:
"
#comfy-save-button
"
,
83
:
"
#comfy-save-button
"
,
"
o
"
:
"
#comfy-file-input
"
,
79
:
"
#comfy-file-input
"
,
"
Backspace
"
:
"
#comfy-clear-button
"
,
8
:
"
#comfy-clear-button
"
,
"
Delete
"
:
"
#comfy-clear-button
"
,
46
:
"
#comfy-clear-button
"
,
"
d
"
:
"
#comfy-load-default-button
"
,
68
:
"
#comfy-load-default-button
"
,
};
const
modifierKeybindId
=
modifierKeyIdMap
[
event
.
key
]
||
modifierKeyIdMap
[
event
.
keyCode
];
if
(
modifierPressed
&&
modifierKeybindId
)
{
event
.
preventDefault
();
const
elem
=
document
.
querySelector
(
modifierKeybindId
);
elem
.
click
();
return
;
}
// Finished Handling all modifier keybinds, now handle the rest
if
(
event
.
ctrlKey
||
event
.
altKey
||
event
.
metaKey
)
{
return
;
}
// Close out of modals using escape
if
(
event
.
key
===
"
Escape
"
||
event
.
keyCode
===
27
)
{
const
modals
=
document
.
querySelectorAll
(
"
.comfy-modal
"
);
const
modal
=
Array
.
from
(
modals
).
find
(
modal
=>
window
.
getComputedStyle
(
modal
).
getPropertyValue
(
"
display
"
)
!==
"
none
"
);
if
(
modal
)
{
modal
.
style
.
display
=
"
none
"
;
}
}
const
keyIdMap
=
{
"
q
"
:
"
#comfy-view-queue-button
"
,
81
:
"
#comfy-view-queue-button
"
,
"
h
"
:
"
#comfy-view-history-button
"
,
72
:
"
#comfy-view-history-button
"
,
"
r
"
:
"
#comfy-refresh-button
"
,
82
:
"
#comfy-refresh-button
"
,
};
const
buttonId
=
keyIdMap
[
event
.
key
]
||
keyIdMap
[
event
.
keyCode
];
if
(
buttonId
)
{
const
button
=
document
.
querySelector
(
buttonId
);
button
.
click
();
}
}
window
.
addEventListener
(
"
keydown
"
,
keybindListener
,
true
);
}
});
web/extensions/core/noteNode.js
0 → 100644
View file @
7d79afd4
import
{
app
}
from
"
../../scripts/app.js
"
;
import
{
ComfyWidgets
}
from
"
../../scripts/widgets.js
"
;
// Node that add notes to your project
app
.
registerExtension
({
name
:
"
Comfy.NoteNode
"
,
registerCustomNodes
()
{
class
NoteNode
{
color
=
LGraphCanvas
.
node_colors
.
yellow
.
color
;
bgcolor
=
LGraphCanvas
.
node_colors
.
yellow
.
bgcolor
;
groupcolor
=
LGraphCanvas
.
node_colors
.
yellow
.
groupcolor
;
constructor
()
{
if
(
!
this
.
properties
)
{
this
.
properties
=
{};
this
.
properties
.
text
=
""
;
}
ComfyWidgets
.
STRING
(
this
,
""
,
[
""
,
{
default
:
this
.
properties
.
text
,
multiline
:
true
}],
app
)
this
.
serialize_widgets
=
true
;
this
.
isVirtualNode
=
true
;
}
}
// Load default visibility
LiteGraph
.
registerNodeType
(
"
Note
"
,
Object
.
assign
(
NoteNode
,
{
title_mode
:
LiteGraph
.
NORMAL_TITLE
,
title
:
"
Note
"
,
collapsable
:
true
,
})
);
NoteNode
.
category
=
"
utils
"
;
},
});
web/extensions/core/snapToGrid.js
View file @
7d79afd4
...
...
@@ -9,7 +9,7 @@ app.registerExtension({
app
.
ui
.
settings
.
addSetting
({
id
:
"
Comfy.SnapToGrid.GridSize
"
,
name
:
"
Grid Size
"
,
type
:
"
numb
er
"
,
type
:
"
slid
er
"
,
attrs
:
{
min
:
1
,
max
:
500
,
...
...
web/scripts/app.js
View file @
7d79afd4
...
...
@@ -4,27 +4,48 @@ import { api } from "./api.js";
import
{
defaultGraph
}
from
"
./defaultGraph.js
"
;
import
{
getPngMetadata
,
importA1111
}
from
"
./pnginfo.js
"
;
class
ComfyApp
{
/**
* @typedef {import("types/comfy").ComfyExtension} ComfyExtension
*/
export
class
ComfyApp
{
/**
* List of {number, batchCount} entries to queue
* List of entries to queue
* @type {{number: number, batchCount: number}[]}
*/
#
queueItems
=
[];
/**
* If the queue is currently being processed
* @type {boolean}
*/
#
processingQueue
=
false
;
constructor
()
{
this
.
ui
=
new
ComfyUI
(
this
);
/**
* List of extensions that are registered with the app
* @type {ComfyExtension[]}
*/
this
.
extensions
=
[];
/**
* Stores the execution output data for each node
* @type {Record<string, any>}
*/
this
.
nodeOutputs
=
{};
/**
* If the shift key on the keyboard is pressed
* @type {boolean}
*/
this
.
shiftDown
=
false
;
}
/**
* Invoke an extension callback
* @param {
string
} method The extension callback to execute
* @param {
...
any} args Any arguments to pass to the callback
* @param {
keyof ComfyExtension
} method The extension callback to execute
* @param {any
[]
} args Any arguments to pass to the callback
* @returns
*/
#
invokeExtensions
(
method
,
...
args
)
{
...
...
@@ -691,11 +712,6 @@ class ComfyApp {
#
addKeyboardHandler
()
{
window
.
addEventListener
(
"
keydown
"
,
(
e
)
=>
{
this
.
shiftDown
=
e
.
shiftKey
;
// Queue prompt using ctrl or command + enter
if
((
e
.
ctrlKey
||
e
.
metaKey
)
&&
(
e
.
key
===
"
Enter
"
||
e
.
keyCode
===
13
||
e
.
keyCode
===
10
))
{
this
.
queuePrompt
(
e
.
shiftKey
?
-
1
:
0
);
}
});
window
.
addEventListener
(
"
keyup
"
,
(
e
)
=>
{
this
.
shiftDown
=
e
.
shiftKey
;
...
...
@@ -1120,6 +1136,10 @@ class ComfyApp {
}
}
/**
* Registers a Comfy web extension with the app
* @param {ComfyExtension} extension
*/
registerExtension
(
extension
)
{
if
(
!
extension
.
name
)
{
throw
new
Error
(
"
Extensions must have a 'name' property.
"
);
...
...
web/scripts/ui.js
View file @
7d79afd4
...
...
@@ -270,6 +270,30 @@ class ComfySettingsDialog extends ComfyDialog {
]),
]);
break
;
case
"
slider
"
:
element
=
$el
(
"
div
"
,
[
$el
(
"
label
"
,
{
textContent
:
name
},
[
$el
(
"
input
"
,
{
type
:
"
range
"
,
value
,
oninput
:
(
e
)
=>
{
setter
(
e
.
target
.
value
);
e
.
target
.
nextElementSibling
.
value
=
e
.
target
.
value
;
},
...
attrs
}),
$el
(
"
input
"
,
{
type
:
"
number
"
,
value
,
oninput
:
(
e
)
=>
{
setter
(
e
.
target
.
value
);
e
.
target
.
previousElementSibling
.
value
=
e
.
target
.
value
;
},
...
attrs
}),
]),
]);
break
;
default
:
console
.
warn
(
"
Unsupported setting type, defaulting to text
"
);
element
=
$el
(
"
div
"
,
[
...
...
@@ -431,7 +455,15 @@ export class ComfyUI {
defaultValue
:
true
,
});
const
promptFilename
=
this
.
settings
.
addSetting
({
id
:
"
Comfy.PromptFilename
"
,
name
:
"
Prompt for filename when saving workflow
"
,
type
:
"
boolean
"
,
defaultValue
:
true
,
});
const
fileInput
=
$el
(
"
input
"
,
{
id
:
"
comfy-file-input
"
,
type
:
"
file
"
,
accept
:
"
.json,image/png
"
,
style
:
{
display
:
"
none
"
},
...
...
@@ -448,6 +480,7 @@ export class ComfyUI {
$el
(
"
button.comfy-settings-btn
"
,
{
textContent
:
"
⚙️
"
,
onclick
:
()
=>
this
.
settings
.
show
()
}),
]),
$el
(
"
button.comfy-queue-btn
"
,
{
id
:
"
queue-button
"
,
textContent
:
"
Queue Prompt
"
,
onclick
:
()
=>
app
.
queuePrompt
(
0
,
this
.
batchCount
),
}),
...
...
@@ -496,9 +529,10 @@ export class ComfyUI {
]),
]),
$el
(
"
div.comfy-menu-btns
"
,
[
$el
(
"
button
"
,
{
textContent
:
"
Queue Front
"
,
onclick
:
()
=>
app
.
queuePrompt
(
-
1
,
this
.
batchCount
)
}),
$el
(
"
button
"
,
{
id
:
"
queue-front-button
"
,
textContent
:
"
Queue Front
"
,
onclick
:
()
=>
app
.
queuePrompt
(
-
1
,
this
.
batchCount
)
}),
$el
(
"
button
"
,
{
$
:
(
b
)
=>
(
this
.
queue
.
button
=
b
),
id
:
"
comfy-view-queue-button
"
,
textContent
:
"
View Queue
"
,
onclick
:
()
=>
{
this
.
history
.
hide
();
...
...
@@ -507,6 +541,7 @@ export class ComfyUI {
}),
$el
(
"
button
"
,
{
$
:
(
b
)
=>
(
this
.
history
.
button
=
b
),
id
:
"
comfy-view-history-button
"
,
textContent
:
"
View History
"
,
onclick
:
()
=>
{
this
.
queue
.
hide
();
...
...
@@ -517,14 +552,23 @@ export class ComfyUI {
this
.
queue
.
element
,
this
.
history
.
element
,
$el
(
"
button
"
,
{
id
:
"
comfy-save-button
"
,
textContent
:
"
Save
"
,
onclick
:
()
=>
{
let
filename
=
"
workflow.json
"
;
if
(
promptFilename
.
value
)
{
filename
=
prompt
(
"
Save workflow as:
"
,
filename
);
if
(
!
filename
)
return
;
if
(
!
filename
.
toLowerCase
().
endsWith
(
"
.json
"
))
{
filename
+=
"
.json
"
;
}
}
const
json
=
JSON
.
stringify
(
app
.
graph
.
serialize
(),
null
,
2
);
// convert the data to a JSON string
const
blob
=
new
Blob
([
json
],
{
type
:
"
application/json
"
});
const
url
=
URL
.
createObjectURL
(
blob
);
const
a
=
$el
(
"
a
"
,
{
href
:
url
,
download
:
"
workflow.json
"
,
download
:
filename
,
style
:
{
display
:
"
none
"
},
parent
:
document
.
body
,
});
...
...
@@ -535,15 +579,15 @@ export class ComfyUI {
},
0
);
},
}),
$el
(
"
button
"
,
{
textContent
:
"
Load
"
,
onclick
:
()
=>
fileInput
.
click
()
}),
$el
(
"
button
"
,
{
textContent
:
"
Refresh
"
,
onclick
:
()
=>
app
.
refreshComboInNodes
()
}),
$el
(
"
button
"
,
{
textContent
:
"
Clear
"
,
onclick
:
()
=>
{
$el
(
"
button
"
,
{
id
:
"
comfy-load-button
"
,
textContent
:
"
Load
"
,
onclick
:
()
=>
fileInput
.
click
()
}),
$el
(
"
button
"
,
{
id
:
"
comfy-refresh-button
"
,
textContent
:
"
Refresh
"
,
onclick
:
()
=>
app
.
refreshComboInNodes
()
}),
$el
(
"
button
"
,
{
id
:
"
comfy-clear-button
"
,
textContent
:
"
Clear
"
,
onclick
:
()
=>
{
if
(
!
confirmClear
.
value
||
confirm
(
"
Clear workflow?
"
))
{
app
.
clean
();
app
.
graph
.
clear
();
}
}}),
$el
(
"
button
"
,
{
textContent
:
"
Load Default
"
,
onclick
:
()
=>
{
$el
(
"
button
"
,
{
id
:
"
comfy-load-default-button
"
,
textContent
:
"
Load Default
"
,
onclick
:
()
=>
{
if
(
!
confirmClear
.
value
||
confirm
(
"
Load default workflow?
"
))
{
app
.
loadGraphData
()
}
...
...
web/style.css
View file @
7d79afd4
...
...
@@ -217,6 +217,14 @@ button.comfy-queue-btn {
z-index
:
99
;
}
.comfy-modal.comfy-settings
input
[
type
=
"range"
]
{
vertical-align
:
middle
;
}
.comfy-modal.comfy-settings
input
[
type
=
"range"
]
+
input
[
type
=
"number"
]
{
width
:
3.5em
;
}
.comfy-modal
input
,
.comfy-modal
select
{
color
:
var
(
--input-text
);
...
...
web/types/comfy.d.ts
0 → 100644
View file @
7d79afd4
import
{
LGraphNode
,
IWidget
}
from
"
./litegraph
"
;
import
{
ComfyApp
}
from
"
/scripts/app
"
;
export
interface
ComfyExtension
{
/**
* The name of the extension
*/
name
:
string
;
/**
* Allows any initialisation, e.g. loading resources. Called after the canvas is created but before nodes are added
* @param app The ComfyUI app instance
*/
init
(
app
:
ComfyApp
):
Promise
<
void
>
;
/**
* Allows any additonal setup, called after the application is fully set up and running
* @param app The ComfyUI app instance
*/
setup
(
app
:
ComfyApp
):
Promise
<
void
>
;
/**
* Called before nodes are registered with the graph
* @param defs The collection of node definitions, add custom ones or edit existing ones
* @param app The ComfyUI app instance
*/
addCustomNodeDefs
(
defs
:
Record
<
string
,
ComfyObjectInfo
>
,
app
:
ComfyApp
):
Promise
<
void
>
;
/**
* Allows the extension to add custom widgets
* @param app The ComfyUI app instance
* @returns An array of {[widget name]: widget data}
*/
getCustomWidgets
(
app
:
ComfyApp
):
Promise
<
Array
<
Record
<
string
,
(
node
,
inputName
,
inputData
,
app
)
=>
{
widget
?:
IWidget
;
minWidth
?:
number
;
minHeight
?:
number
}
>
>
>
;
/**
* Allows the extension to add additional handling to the node before it is registered with LGraph
* @param nodeType The node class (not an instance)
* @param nodeData The original node object info config object
* @param app The ComfyUI app instance
*/
beforeRegisterNodeDef
(
nodeType
:
typeof
LGraphNode
,
nodeData
:
ComfyObjectInfo
,
app
:
ComfyApp
):
Promise
<
void
>
;
/**
* Allows the extension to register additional nodes with LGraph after standard nodes are added
* @param app The ComfyUI app instance
*/
registerCustomNodes
(
app
:
ComfyApp
):
Promise
<
void
>
;
/**
* Allows the extension to modify a node that has been reloaded onto the graph.
* If you break something in the backend and want to patch workflows in the frontend
* This is the place to do this
* @param node The node that has been loaded
* @param app The ComfyUI app instance
*/
loadedGraphNode
(
node
:
LGraphNode
,
app
:
ComfyApp
);
/**
* Allows the extension to run code after the constructor of the node
* @param node The node that has been created
* @param app The ComfyUI app instance
*/
nodeCreated
(
node
:
LGraphNode
,
app
:
ComfyApp
);
}
export
type
ComfyObjectInfo
=
{
name
:
string
;
display_name
?:
string
;
description
?:
string
;
category
:
string
;
input
?:
{
required
?:
Record
<
string
,
ComfyObjectInfoConfig
>
;
optional
?:
Record
<
string
,
ComfyObjectInfoConfig
>
;
};
output
?:
string
[];
output_name
:
string
[];
};
export
type
ComfyObjectInfoConfig
=
[
string
|
any
[]]
|
[
string
|
any
[],
any
];
web/types/litegraph.d.ts
0 → 100644
View file @
7d79afd4
This diff is collapsed.
Click to expand it.
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment