Compare commits

...

939 Commits

Author SHA1 Message Date
pythongosssss
02c13c403f Safety mechanism to prevent loading everything 2024-11-24 11:44:26 +00:00
pythongosssss
8254e3c9cf Fix number of items shown when loading more
Fix number of items shown on status update
2024-11-24 11:37:02 +00:00
Chenlei Hu
1160231b62 1.4.9 (#1661) 2024-11-23 17:49:38 -05:00
Chenlei Hu
a51e27bedf chore: update litegraph to 0.8.35 (#1662) 2024-11-23 17:49:27 -05:00
filtered
abed0656af Add Fit Group to Contents keybind (#1658)
* Add Fit Group to Nodes keyboard command

Fits all selected groups.

* nit - Rename

* Move to commandStore & Playwright test

* nit

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
Co-authored-by: github-actions <github-actions@github.com>
2024-11-23 17:15:52 -05:00
Terry Jia
5febda16c7 fix bug and allow restore previous node size (#1659) 2024-11-23 10:56:59 -05:00
Chenlei Hu
069dc67c30 Reland "Fix undo / redo filling with empty steps" (#1653)
* Revert "Revert "Fix undo / redo filling with empty steps (#1649)" (#1652)"

This reverts commit 7623810166.

* Update test expectations

* Add dirty flag if workflow is not persisted

* Add dirty flag to other UI areas for new workflows

* Remove redundant code

* Fix regression: undo / redo steps lost on refresh

The history is still be cleared, but any changes made by issuing undo / redo comands prior to refresh are not lost.

* Update test expectations

Partially reverts f8cc2c0d67 - adds dirty flags back to unsaved workflows.

---------

Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
2024-11-23 09:49:12 -05:00
Chenlei Hu
7623810166 Revert "Fix undo / redo filling with empty steps (#1649)" (#1652)
This reverts commit ad2c1a0d3e.
2024-11-22 22:02:56 -05:00
Chenlei Hu
21fa88461f [Electron][skip ci] Update install disk space requirement to 15GB (#1651) 2024-11-22 21:59:46 -05:00
Chenlei Hu
27b0493306 Move files to constants/ (#1650) 2024-11-22 21:55:44 -05:00
filtered
ad2c1a0d3e Fix undo / redo filling with empty steps (#1649) 2024-11-22 21:49:13 -05:00
Robin Huang
f51866d988 [desktop] Update crash report description (#1646)
* Update crash report descripton

* Update settings description.
2024-11-22 21:42:55 -05:00
Chenlei Hu
46627bb44b Remove host and port from server config panel (#1648) 2024-11-22 21:40:15 -05:00
Chenlei Hu
68cadbda9f 1.4.8 (#1647) 2024-11-22 20:36:56 -05:00
pythongosssss
0f2260065a [Electron] Allow users to submit error reports (#1633)
* Allows users to submit error reports

* Text change

* Add tooltip, change severity on submit
Remove unused import
2024-11-22 17:04:51 -05:00
Chenlei Hu
4007cc13c2 [Electron] ComfyUI server config (Launch args config) (#1644)
* Remove electron adapter server args

* Add server args typing

* Add server config constant file

* Tooltip to name; name to id

* Capitalize category

* Server config store

* Prevent default value

* Add serverconfig test

* Guard server config panel with electron flag

* Filter nullish values from server args

* Use slider for preview size
2024-11-22 16:50:24 -05:00
Chenlei Hu
3920210c5c Remove Ctrl+D keybinding (#1643) 2024-11-22 11:17:36 -05:00
Chenlei Hu
4e22bffae2 chore: update litegraph to 0.8.34 (#1642) 2024-11-22 11:03:02 -05:00
Chenlei Hu
462a131557 1.4.7 (#1638) 2024-11-21 17:12:14 -08:00
Chenlei Hu
ec01a04786 Hint shift to queue front on queue button tooltip (#1634) 2024-11-21 15:18:20 -05:00
Chenlei Hu
4c48241e19 Update litegraph 0.8.33 (#1632)
* chore: update litegraph to 0.8.33

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-11-21 14:57:21 -05:00
Chenlei Hu
886c40a69a Fix keybinding conflict (#1630) 2024-11-21 11:49:57 -05:00
Chenlei Hu
479d1b28c7 Update litegraph (Global snap to grid setting) (#1629) 2024-11-21 10:30:54 -05:00
Tristan Sommer
c41b57128a maskEditor UI interface revamp + brush smoothing precision adjustment (#1626) 2024-11-21 09:39:53 -05:00
Chenlei Hu
5d178a407d [chore] Update comfyui-electron-types (#1625)
* Remove electron external dep

* [chore] Update comfyui-electron-types
2024-11-21 00:07:50 -05:00
Chenlei Hu
73b7606f6e 1.4.6 (#1622) 2024-11-20 20:36:18 -05:00
oto-ciulis-tt
94f5031f0d feat: Update Electron Download types (#1621)
* feat: Update Electron Download types

* Fix vite rollup

---------

Co-authored-by: Oto Ciulis <oto.ciulis@gmail.com>
Co-authored-by: huchenlei <huchenlei@proton.me>
2024-11-20 20:34:27 -05:00
oto-ciulis-tt
c857e7d98c feat: #270 Improve error view (#1617)
* feat: #270 Improve error view

Reverting change

Lint & Format

PR comments

Fixing typo

* nit

---------

Co-authored-by: Oto Ciulis <oto.ciulis@gmail.com>
Co-authored-by: huchenlei <huchenlei@proton.me>
2024-11-20 16:35:14 -05:00
pythongosssss
d5b8a555d9 [Electron] xterm startup logs (#1620)
* Add live terminal output

* Fix scrolling

* Refactor loading

* Fallback to polling if endpoint fails

* Comment

* Move clientId to executionStore
Refactor types

* Remove polling

* wip terminal command input

* Refactor to use node-pty

* Hide tabs if not electron

* Lint fix

* ts fix

* Refactor tab components

* Use xterm for startup logs

* Nicer logs display

* Fix not setting xterm + mark terminal as raw
2024-11-20 16:09:54 -05:00
Chenlei Hu
f34d50da3d [Refactor] Extract 'FormItem' and 'SettingItem' (#1619)
* Extract SettingItem component

* Extract GeneralSettingItem

* Rename to FormItem

* nit

* nit
2024-11-20 15:10:17 -05:00
Chenlei Hu
4f3693e322 Reland 'Bind Ctrl+s to Comfy.SaveWorkflow' (#1618) 2024-11-20 15:01:04 -05:00
Terry Jia
431ad7d27f allow render depth directly (#1610) 2024-11-20 09:36:44 -05:00
Chenlei Hu
0c97b09a5a 1.4.5 (#1616) 2024-11-20 09:35:58 -05:00
Chenlei Hu
bdb9f0d845 chore: update litegraph to 0.8.31 (#1615) 2024-11-20 09:33:25 -05:00
Chenlei Hu
77b85acdd5 Revert "Bind Ctrl+s to Comfy.SaveWorkflow (#1599)" (#1614)
This reverts commit 0058691579.
2024-11-20 09:27:01 -05:00
Chenlei Hu
8906f5c26e Add Comfy-Desktop.ComfyServer.ExtraLaunchArgs (#1609) 2024-11-19 20:44:26 -05:00
Chenlei Hu
81194cc7fe 1.4.4 (#1608) 2024-11-19 19:59:22 -05:00
Chenlei Hu
f4b972fab5 chore: update litegraph to 0.8.30 (#1607) 2024-11-19 19:52:01 -05:00
Terry Jia
3aa1c03566 better support for animation (#1606) 2024-11-19 18:25:58 -05:00
Chenlei Hu
600b7f93e5 [Electron] Add missing i18n items (#1605) 2024-11-19 15:31:57 -05:00
Chenlei Hu
2a7df57404 Fix always snap to grid (#1604) 2024-11-19 12:10:40 -05:00
Chenlei Hu
6352cd86ee Show confirm dialog on workflow path conflict (Save As) (#1590)
* Show confirm dialog on workflow path conflict (Save As)

* Fix closeworkflow

* nit

* Add playwright tests

* nit

* nit

* Move workflows dir cleanup
2024-11-18 23:07:24 -05:00
Chenlei Hu
0058691579 Bind Ctrl+s to Comfy.SaveWorkflow (#1599) 2024-11-18 23:07:11 -05:00
Chenlei Hu
1531bb6d9f 1.4.3 (#1598) 2024-11-18 22:18:03 -05:00
filtered
40245aacf9 Run pre-commit type check only for TypeScript (#1597)
* Prevent unnecessary type-checks

* Remove commented code
2024-11-18 21:58:54 -05:00
Tristan Sommer
6e49685f58 fix: improve light mode visibility, add: select color up to mask option (#1596) 2024-11-18 21:31:28 -05:00
Chenlei Hu
946823ce6c [Electron] Add Comfy-Desktop.SendStatistics setting (#1594) 2024-11-18 20:25:18 -05:00
Chenlei Hu
c05f1465db [chore] npm audit fix (#1593) 2024-11-18 20:19:43 -05:00
Chenlei Hu
88164bdac5 [Electron] Fix initial default install location validation (#1592) 2024-11-18 20:10:22 -05:00
Chenlei Hu
fc9e347055 Disable slow jest tests (#1591)
* Disable slow jest tests

* nit
2024-11-18 20:10:03 -05:00
pythongosssss
6fbf1248f4 Filter cached/canceled results (#1586)
* Filter cached/canceled results

* Highlight if on

* Update setting
2024-11-18 16:59:59 -05:00
pythongosssss
56848724cd Fix id (#1589) 2024-11-18 16:59:36 -05:00
Chenlei Hu
26c3eeb942 Fix vue warning on unnecessary defineEmits import (#1588) 2024-11-18 13:15:16 -05:00
filtered
a8f869337e Fix load crash when graph or config unset (#1587)
Resolves #1585
2024-11-18 12:51:46 -05:00
filtered
7e245ba1cf Update Litegraph: Canvas Pointer (#1556)
* Litegraph: canvas.pointer

Clear @ts-expect-error

Fix exception thrown on slot double-click

Long-standing bug but has no real impact in prod - just logs an error.
Required for new connecting_link features.

Add settings: CanvasPointer options

Update litegraph 0.8.28

Fix regression in snap to grid render

Fix snap to grid marker always on

Update snap to grid to use Positionable API

Fix test clicks registering as double-click

Improve test precision

Current test proves it has changed to something smaller.
New test proves it is exactly what was specified.
Will need refinement when a limit is put on latent width.

Fix test expects collapse node to select node

Remove redundant code

Resolved by CanvasPointer

Fix flaky test - ContextMenu

Fix settings group

* Update litegraph

* Remove snapToGrid extension

* Update test expectations [skip ci]

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
Co-authored-by: github-actions <github-actions@github.com>
2024-11-18 10:50:51 -05:00
filtered
2a93f873b4 Use Litegraph snap to grid (#1572)
Leaves only settings code in the extension
2024-11-18 10:27:37 -05:00
filtered
f8e7058e19 Add jest set/has pointer capture mocks (#1581)
* Add set/has pointer capter mocks

* Add mock impl.
2024-11-17 21:41:37 -05:00
Chenlei Hu
8d4e740baa 1.4.2 (#1580) 2024-11-17 21:23:49 -05:00
Tristan Sommer
3273ee938b New Mask Editor (#1284)
* implmentation of new mask editor

* fixed some problems, added some new ones

* Refactor: Split implementation into classes, fix multiple bugs -> all initial features work, more testing required

* first release - fixed all known issues, tested, added color select tool and settings toggle
2024-11-17 21:23:32 -05:00
Terry Jia
94f1bc3b38 add preview 3d node and up_direction parameter (#1579) 2024-11-17 21:17:00 -05:00
Terry Jia
d5ce140eb6 add load 3d node support (#1563)
* add load 3d node support

* stl and different material display support
2024-11-17 15:13:24 -05:00
pythongosssss
b5f0c4bf73 [Electron] Terminal commands (#1531)
* Add live terminal output

* Fix scrolling

* Refactor loading

* Fallback to polling if endpoint fails

* Comment

* Move clientId to executionStore
Refactor types

* Remove polling

* wip terminal command input

* Refactor to use node-pty

* Hide tabs if not electron

* Lint fix

* ts fix

* Refactor tab components
2024-11-17 14:43:08 -05:00
Chenlei Hu
545a990365 Disable debug logic in changeTracker (#1577) 2024-11-17 14:38:14 -05:00
Chenlei Hu
71e4a42cfe Only persist workflow on workflow change/switch (#1576)
* Only persist workflow on workflow change/switch

* nit

* Add playwright test

* Add modify test

* nit

* Fix playwright tests

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-11-17 14:13:52 -05:00
Chenlei Hu
16b0ebf75a Remove deprecated setting Comfy.NodeSearchBoxImpl.LinkReleaseTrigger (#1574) 2024-11-17 12:10:44 -05:00
Chenlei Hu
eaeb17bdc7 Add new top level setting category 'LiteGraph' and 'Appearance' (#1573) 2024-11-17 12:06:07 -05:00
filtered
239b464957 Apply group padding in context menu commands (#1570) 2024-11-17 11:13:28 -05:00
filtered
00b6d989ec Fix Vue console warning flood on settings open (#1571) 2024-11-17 11:12:37 -05:00
Chenlei Hu
c5f05b1855 1.4.1 (#1568) 2024-11-16 19:08:47 -05:00
oto-ciulis-tt
6fefcaad7b Show download percentage only if it's over 10% (#1539)
* Show download percentage only if it's over 10%

* PR comments

---------

Co-authored-by: Oto Ciulis <oto.ciulis@gmail.com>
2024-11-16 11:48:29 -05:00
Yoland Yan
22fdfd7f0b Minor: change adjustMenuPosition style to single side anchor and scale proportionally elsewhere (#1567) 2024-11-16 11:47:54 -05:00
oto-ciulis-tt
6842eb05de feat: Adding download count badge to sidebar (#1552)
* feat: Adding download count badge to sidebar

* Fixing lint

* Updating electronDownloadStore to handle missing DownloadManager

* PR comments

---------

Co-authored-by: Oto Ciulis <oto.ciulis@gmail.com>
2024-11-16 11:46:55 -05:00
filtered
37e7994d55 Fix husky pre-commit for winnt clients [skip ci] (#1564) 2024-11-16 09:39:27 -05:00
filtered
399893bbb2 Allow decimal places typed in settings (#1566) 2024-11-16 09:37:52 -05:00
Chenlei Hu
227db065f3 1.4.0 (#1562) 2024-11-15 21:18:29 -05:00
Chenlei Hu
b4352bcd8d Fix node search box filter test (#1561) 2024-11-15 21:18:19 -05:00
Chenlei Hu
39bab9d9e2 Disable flaky group node test (#1560) 2024-11-15 20:55:18 -05:00
Chenlei Hu
c71644f02f Use tailwind class in NodeSearchBox (#1559) 2024-11-15 20:46:04 -05:00
filtered
6aad7ee8b6 Allow remote dev to be switched on/off (#1558)
* Allow remote dev to be switched on/off

* nit - Docs
2024-11-15 19:26:41 -05:00
Chenlei Hu
2b96d831fc Fix install location path picker (#1557) 2024-11-15 18:55:58 -05:00
filtered
dde0291add Fix change tracker count desync on error (#1555)
* Add TS types

* Ensure changeTracker works after exceptions

Wraps all code between before/after change calls in try/finally blocks
2024-11-15 16:03:21 -05:00
filtered
8af016ffc1 Fix husky pre-commit for winnt clients [skip ci] (#1551) 2024-11-15 09:45:39 -05:00
Chenlei Hu
82b4547d7d Remove canvas border rendering (#1549)
* Remove canvas border rendering

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-11-15 09:24:51 -05:00
Chenlei Hu
791a25637f Revert "Fix husky pre-commit & type check only staged" (#1550)
* Revert "Fix husky pre-commit & type check only staged (#1361)"

This reverts commit 795e932b8f.

* Update package.json
2024-11-15 09:19:24 -05:00
filtered
b922aa5c7c Add option to disable ctrl + shift + zoom (#1545)
* Add option to disable ctrl + shift + zoom

Minor change to default behaviour: zoom no longer triggers if alt key is also down.

* Update coreSettings.ts

Next release will be 1.4.0 to leave room for patches in 1.3 stable after today's main repo sync.

---------

Co-authored-by: Chenlei Hu <huchenlei@proton.me>
2024-11-15 09:10:09 -05:00
Chenlei Hu
cbaebbc9c2 chore: update litegraph to 0.8.27 (#1542) 2024-11-15 09:06:33 -05:00
Chenlei Hu
86b2e1aa6c Add electron adapter extension (#1538) 2024-11-14 19:57:09 -05:00
Chenlei Hu
61c5f05126 1.3.44 (#1541) 2024-11-14 19:56:46 -05:00
Chenlei Hu
dde9c3dad5 Fix tree explorer y-axis padding (#1540)
* Fix tree explorer y-axis padding

* nit
2024-11-14 17:15:57 -05:00
Chenlei Hu
ee5c127146 1.3.43 (#1536) 2024-11-13 19:01:44 -05:00
Chenlei Hu
acba6097e0 Replace electron API mocks with actual electron API impl (#1535)
* link electron types locally

* Update electronAPI calls

* Fix source validation

* Payload to raw

* nit

* Update electron types
2024-11-13 17:20:18 -05:00
filtered
82d00a1bcf Update Template copy & paste (#1533)
* Split original clipboard functions out

* Add version check for templates

* Fix regression in use template undo steps
2024-11-13 17:04:31 -05:00
Chenlei Hu
b9224464c0 Fix reverse proxy (#1532) 2024-11-13 15:36:35 -05:00
Chenlei Hu
fba9a03df3 Lazy load setting dialog tabs (#1530) 2024-11-13 10:56:48 -05:00
Chenlei Hu
2fd624cd3d [skip ci] Update README.md (#1529)
Replace screenshot with actual logs for better accessibility.
2024-11-13 10:37:12 -05:00
Chenlei Hu
095fe2a175 Allow access of dev server in LAN for touch device testing (#1528) 2024-11-13 10:34:36 -05:00
Lasse Lauwerys
d838777e04 Touch support bug fixes (#1527)
* Improved touch support

* Fix touch support scaling error

* Fix touch scaling precision on all zoom levels

* Improved touch experiene, fixed zooming on textarea elements and fixed context menu.

* Minor bug fix
2024-11-13 10:14:11 -05:00
filtered
7e0d1d441d Flaky tests and observable state (#1526)
* Fix missing await

* Fix flaky tests - keyboard combos

Old code is causing playwright &/ changeTracker to add an undo step.  Using combo mode resolves flakiness until that can be investigated thoroughly.

* Restore skipped tests

* Fix flaky tests

* Async clean up

* Fix test always fails on retry

* Add TS types (tests)

* Fix flaky test

* Add observable busy state to workflow store

* Add workflow store busy wait to tests

* Rename test for clarity

* Fix flaky tests - use press() from locator API

Ref: https://playwright.dev/docs/api/class-keyboard#keyboard-press

* Fix flaky test - wait next frame

* Add delay between mouse events

Litegraph pointer handling is all custom coded, so a adding a delay between events for a bit of reality is actually beneficial.
2024-11-13 09:35:22 -05:00
Chenlei Hu
ddab149f16 1.3.42 (#1524) 2024-11-12 23:13:49 -05:00
Chenlei Hu
a73fdcd3bd Fix sidebar splitter state (#1523) 2024-11-12 23:12:56 -05:00
filtered
d6e0c197bd Decouple group node from Litegraph copy & paste (#1522)
* nit - Refactor

* Add old clipboard code to groupNode

* [Refactor] groupNode copy / paste functions

* Clarify function name
2024-11-12 23:11:04 -05:00
Chenlei Hu
3117d0fdc1 Fix loading of model library in non-electron env (#1521) 2024-11-12 22:38:29 -05:00
Chenlei Hu
96fda64b70 Fix queue button overlaped by pysssss.ImageFeed (#1520) 2024-11-12 21:35:14 -05:00
oto-ciulis-tt
e3d2c3a814 feat: Add download progress to sidebar (#1490)
* feat: Add download progress to sidebar

* Removing console log

* Lint fixes

* Updating UI

* Fixing lint error

* Fixing lint error

* Fixing lint error

* PR comments

* Reverting change

---------

Co-authored-by: Oto Ciulis <oto.ciulis@gmail.com>
2024-11-12 16:28:55 -05:00
Lasse Lauwerys
1a8900de1f Improved touch support (#1519)
* Improved touch support

* Fix touch support scaling error
2024-11-12 16:19:59 -05:00
Chenlei Hu
05ba526388 Type DOMWidget and DOMWidgetOptions (#1517)
* Type DOMWidget and DOMWidgetOptions

* Annotate widget value type
2024-11-12 13:35:24 -05:00
Chenlei Hu
4bc79181ae Move DOMClippingEnabled to coreSettings.ts (#1516)
* Move DOMClippingEnabled to coreSettings.ts

* nit
2024-11-12 12:01:44 -05:00
filtered
feafbf9cbf Litegraph Reroute Beta (#1421)
* Add Reroute support - ConnectingLinkImpl

Bonus: TS strict

* Add Reroute support

* Remove unused TS expect error

* Add reroute beta opt-in option

* Add settings option: Middle-click reroute node

* Add settings: Link Markers

* Move settings

* Update litegraph

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
2024-11-12 11:46:14 -05:00
Chenlei Hu
40f9b881f3 1.3.41 (#1514) 2024-11-12 10:48:36 -05:00
Chenlei Hu
8236163fea Enable New UI by default (#1515)
nit

Add playwright test

nit

nit

nit
2024-11-12 10:48:26 -05:00
filtered
59b555b448 Fix multiline text input alignment & clipping (#1513)
* Simplify multiline widget scaling

* Fix multiline widget clipping

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-11-12 10:38:03 -05:00
Chenlei Hu
71eeee6744 Less padding on sidebar tabs for small screens (#1511)
* Reduce searchbox and tree padding for small screen

* Smaller buttons
2024-11-11 20:15:34 -05:00
Chenlei Hu
1ff6e27d9c Manage widget definitions with Pinia store (#1510)
* Fix compile

* nit

* Remove extensions.test

* nit
2024-11-11 17:23:52 -05:00
Chenlei Hu
64ef0f18b1 Fix welcome page welcome text selection (#1508) 2024-11-11 11:13:59 -05:00
Chenlei Hu
73bdbddf90 Fix rename open/bookmark workflow (#1507)
* Fix rename open/bookmark workflow

* nit

* Fix save as

* Add browser test
2024-11-11 11:06:41 -05:00
Chenlei Hu
a55833b3a6 1.3.40 (#1506) 2024-11-11 09:53:42 -05:00
filtered
43012eb1d1 Add settings option: Keep links on delete (#1504) 2024-11-10 21:33:48 -05:00
Chenlei Hu
d1e019589d [Electron] ComfyUI Desktop install wizard (#1503)
* Basic prototype

* Welcome screen animation

* nit

* Refactor structure

* Fix mocking

* Add tooltips

* i18n

* Add next button

* nit

* Stepper navigate

* Extract

* More i18n

* More i18n

* Polish MigrationPicker

* Polish settings step

* Add more i18n

* nit

* nit
2024-11-10 19:56:01 -05:00
filtered
7bc79edf3d Add back/forwards compatibility to schema validation (#1501)
* Allow future extensions of added schema objects

* Add explicit versioning to zod schemas

* Extend schema 0.4 with new fields in extras

- Allows Reroutes without using schema v1
- Reroute data is retained when using old versions - simply not displayed

* Add Reroute undo/redo support
2024-11-10 19:50:18 -05:00
Chenlei Hu
58ad01adfe Reland "Re-enable multiple-undo test" (#1499)
* Revert "Revert "Re-enable multiple-undo test (#1483)" (#1498)"

This reverts commit 5f1a9659e9.

* nit
2024-11-10 12:59:19 -05:00
Chenlei Hu
5f1a9659e9 Revert "Re-enable multiple-undo test (#1483)" (#1498)
This reverts commit 6c6c356c78.
2024-11-10 11:53:55 -05:00
Chenlei Hu
6c6c356c78 Re-enable multiple-undo test (#1483)
* Re-enable multiple-undo test

* nit
2024-11-10 10:57:15 -05:00
Chenlei Hu
893fd498df 1.3.39 (#1497) 2024-11-10 10:17:42 -05:00
Chenlei Hu
1ca388457d chore: update litegraph to 0.8.25 (#1496) 2024-11-10 10:16:35 -05:00
Chenlei Hu
69f0da06f8 Add update-litegraph script (#1495)
* Add update script

* nit
2024-11-10 10:15:46 -05:00
Chenlei Hu
d9a34872c3 [Electron] Add basic welcome screen (#1491)
* WIP

* Add LogTerminal

* Modify server startup view

* Add installView

* Add basic welcome screen and dev server setup

* nit

* nit

* nit

* nit

* nit
2024-11-10 09:41:32 -05:00
filtered
31fac3873c Litegraph: Reroute support (#1420)
* Add Litegraph schema v1 support

- LLink changed from array to object
- Add reroutes
- Graph state object
- Falls back to original schema if not validated
- Add version check to schema validation pass

* Fix test schema version detection

Resolves conflict with proposed schema v1
2024-11-09 15:46:34 -05:00
Chenlei Hu
8dc057517f 1.3.38 (#1488) 2024-11-09 10:55:35 -05:00
Chenlei Hu
4617e0fb1a Fix node badge on unknown color palette (#1487)
* Fix node badge on unknown color palette

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-11-09 10:55:25 -05:00
Chenlei Hu
f8ec87ddea Fix changeTracker modified state (#1481)
* Add jsondiffpatch

* Add logs

* Add graphDiff helper

* Fix changeTracker

* Add loglevel

* Add playwright test

* Fix jest test

* nit

* nit

* Fix test url

* nit
2024-11-08 22:24:35 -05:00
Chenlei Hu
c12f059940 Persist splitter state in localStorage (#1480) 2024-11-08 20:16:13 -05:00
Chenlei Hu
cc320e0f84 Change tooltip location to bottom for sidebar action buttons (#1479) 2024-11-08 18:56:44 -05:00
Chenlei Hu
acbc38ced4 Revert "Enable New UI by default (#1460)" (#1476)
This reverts commit f0b735f3dd.
2024-11-08 16:35:52 -05:00
oto-ciulis-tt
777a6d9ce3 feat:use electron api to download models (#1473)
* enh: Use electron API to download models

* Adding tooltips

* PR comments

---------

Co-authored-by: Oto Ciulis <oto.ciulis@gmail.com>
2024-11-08 15:59:35 -05:00
pythongosssss
7e0b87dd32 Live terminal output (#1347)
* Add live terminal output

* Fix scrolling

* Refactor loading

* Fallback to polling if endpoint fails

* Comment

* Move clientId to executionStore
Refactor types

* Remove polling
2024-11-08 15:38:21 -05:00
Zoltán Dócs
0161a670cf Fit view to bounds (#1474)
* fit view:
- fit view to canvas selection
- fit view to whole graph when nothing is selected
- add button to graph canvas menu
- assign default keybinding '.'

* Adjust on changed APIs

* Update litegraph

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
2024-11-08 15:03:21 -05:00
Chenlei Hu
0eba49c536 Update litegraph (Animate to bounds) (#1475) 2024-11-08 14:12:58 -05:00
Chenlei Hu
1d9c3f00b7 Setting dialog responsive design for smaller screen size (screen width < 1536) (#1472)
* Smaller queue button

* Smaller dialog padding

* Adjust setting content

* Fix keybinding panel
2024-11-08 11:18:26 -05:00
Chenlei Hu
904408de01 1.3.37 (#1470) 2024-11-08 10:03:27 -05:00
Chenlei Hu
700336fcc7 Fix queue button icon layout shift (#1469) 2024-11-08 10:02:36 -05:00
Chenlei Hu
dd192777b7 Consistently use -1 for temporary file size (#1464) 2024-11-07 23:34:46 -05:00
Chenlei Hu
6b6992591b Update litegraph (#1462) 2024-11-07 20:20:05 -05:00
filtered
45380f7ca0 Fix TypeError thrown (#1461)
Missing node type + reroute linked
2024-11-07 20:17:16 -05:00
Chenlei Hu
f0b735f3dd Enable New UI by default (#1460)
* Enable New UI by default

* nit

* Add playwright test

* nit

* nit

* nit
2024-11-07 17:55:53 -05:00
Chenlei Hu
9568d63820 Add comfyui-electron-types dependency (#1459) 2024-11-07 16:05:26 -05:00
Chenlei Hu
073638672d Fix ('STRING',) input node (#1457)
* Fix ('STRING',) input node

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-11-07 13:03:54 -05:00
Chenlei Hu
8ae9210298 Always sort workflows tree (#1456)
* Always sort workflows tree

* nit

* nit

* Add tests

* nit

* nit
2024-11-07 11:29:29 -05:00
Chenlei Hu
daf94d74d5 1.3.36 (#1453) 2024-11-07 08:59:56 -05:00
Chenlei Hu
14b3d4c766 Fix loading of workflow bookmarks (#1452) 2024-11-07 08:58:28 -05:00
Chenlei Hu
40880dbb59 Move refresh button from actionbar to 'Edit' menu (#1451) 2024-11-07 08:47:46 -05:00
Chenlei Hu
aa4742e394 Update litegraph (#1447) 2024-11-06 21:51:03 -05:00
Chenlei Hu
0a7000328a Add menu button to toggle focus mode (#1446)
* Add focus mode toggle button

* handle menu position

* nit
2024-11-06 20:56:32 -05:00
dependabot[bot]
da7a49bb5c Bump happy-dom from 15.4.0 to 15.11.0 (#1445)
Bumps [happy-dom](https://github.com/capricorn86/happy-dom) from 15.4.0 to 15.11.0.
- [Release notes](https://github.com/capricorn86/happy-dom/releases)
- [Commits](https://github.com/capricorn86/happy-dom/compare/v15.4.0...v15.11.0)

---
updated-dependencies:
- dependency-name: happy-dom
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-06 19:59:13 -05:00
Chenlei Hu
5e4439b905 [Electron] Add electron-specific setup page (#1444)
* Add dummy server start view

* Do external nav

* nit

* nit

* nit

* nit
2024-11-06 19:39:09 -05:00
Chenlei Hu
ea0883271e 1.3.35 (#1443) 2024-11-06 13:49:12 -05:00
Chenlei Hu
3d303c7693 Fix save workflow binding on Ctrl + S (#1442)
* Fix save workflow binding on Ctrl + S

* nit
2024-11-06 13:46:56 -05:00
Chenlei Hu
9f14edaf2b 1.3.34 (#1440) 2024-11-06 08:53:38 -05:00
Chenlei Hu
d1738b50d2 Update litegraph (Remove hardcode +1 size) (#1438)
* Update litegraph (Remove hardcode +1 size)

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-11-05 19:07:38 -05:00
Chenlei Hu
c560628f1f [Extension API] Register about panel badge (#1436)
* Custom about panel badge

* Add playwright test

* Update README

* nit

* nit

* nit

* nit
2024-11-05 19:06:38 -05:00
Chenlei Hu
c56533bb23 Workflow Management Reworked (#1406)
* Merge temp userfile

Basic migration

Remove deprecated isFavourite

Rename

nit

nit

Rework open/load

Refactor save

Refactor delete

Remove workflow dep on manager

WIP

Change map to record

Fix directory

nit

isActive

Move

nit

Add unload

Add close workflow

Remove workflowManager.closeWorkflow

nit

Remove workflowManager.storePrompt

move from commandStore

move more from commandStore

nit

Use workflowservice

nit

nit

implement setWorkflow

nit

Remove workflows.ts

Fix strict errors

nit

nit

Resolves circular dep

nit

nit

Fix workflow switching

Add openworkflowPaths

Fix store

Fix key

Serialize by default

Fix proxy

nit

Update path

Proper sync

Fix tabs

WIP

nit

Resolve merge conflict

Fix userfile store tests

Update jest test

Update tabs

patch tests

Fix changeTracker init

Move insert to service

nit

Fix insert

nit

Handle bookmark rename

Refactor tests

Add delete workflow

Add test on deleting workflow

Add closeWorkflow tests

nit

* Fix path

* Move load next/previous

* Move logic from store to service

* nit

* nit

* nit

* nit

* nit

* Add ChangeTracker.initialState

* ChangeTracker load/unload

* Remove app.changeWorkflow

* Hook to app.ts

* Changetracker restore

* nit

* nit

* nit

* Add debug logs

* Remove unnecessary checkState on graphLoad

* nit

* Fix strict

* Fix temp workflow name

* Track ismodified

* Fix reactivity

* nit

* Fix graph equal

* nit

* update test

* nit

* nit

* Fix modified state

* nit

* Fix modified state

* Sidebar force close

* tabs force close

* Fix save

* Add load remote workflow test

* Force save

* Add save test

* nit

* Correctly handle delete last opened workflow

* nit

* Fix workflow rename

* Fix save

* Fix tests

* Fix strict

* Update playwright tests

* Fix filename conflict handling

* nit

* Merge temporary and persisted ref

* Update playwright expectations

* nit

* nit

* Fix saveAs

* Add playwright test

* nit
2024-11-05 11:03:27 -05:00
Chenlei Hu
1387d7e627 1.3.33 (#1435) 2024-11-05 10:06:51 -05:00
Chenlei Hu
16f2e56d8e Handle errors from top action menu commands (#1432) 2024-11-04 22:50:19 -05:00
Chenlei Hu
75ffab2160 Fix user stuck in title editing state (#1430)
* Fix user stuck in title editing state

* Fix test
2024-11-04 21:59:40 -05:00
filtered
4c19e1ba3a Speed up E2E tests using fully parallel (#1429)
With flaky tests / async bugs all dealt with, fullyParallel can be restored.
2024-11-04 20:33:50 -05:00
Chenlei Hu
2161ae4e5b Pin selected items (Nodes + Groups) (#1427)
* Pin selected items (Nodes + Groups)

* Update litegraph

* Add playwright test

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-11-04 17:36:33 -05:00
Chenlei Hu
3148c90e28 [skip ci] Update README.md (#1425) 2024-11-04 09:16:55 -05:00
Chenlei Hu
497b2fba8d 1.3.32 (#1424) 2024-11-04 09:03:27 -05:00
Chenlei Hu
09d5e29f01 Create new branch in release script (#1423) 2024-11-04 09:02:33 -05:00
filtered
56b63ebab5 Update Litegraph API: Group move / select / titlebar (#1418)
* Litegraph: Group move / select

* Update litegraph

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
2024-11-03 18:45:20 -05:00
filtered
3ba776e6ca Add Litegraph multi-select & group nesting (#1416)
* Allow nested groups

Pass all selected items (new litegraph feature) instead of just selected nodes.

* Allow nested groups - context menus

* Update litegraph

* Update litegraph (Select all / Delete selected)

* Add playwright test

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
Co-authored-by: github-actions <github-actions@github.com>
2024-11-03 18:08:42 -05:00
Chenlei Hu
0a784d9236 Highlight splitter gutter on resizing (#1414) 2024-11-03 12:38:39 -05:00
Chenlei Hu
00df7b428f Animate goto node (#1412)
* Animate goto node

* Update litegraph (animateToNode)
2024-11-03 10:57:17 -05:00
filtered
27bacc36d4 Update tests with precise node positions / sizes (#1408)
* Update tests with precise node positions / sizes

* Fix test flakiness - missing await

* Fix test failures - async not awaited

* Update action

* Update test expectations [skip ci]

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
Co-authored-by: github-actions <github-actions@github.com>
2024-11-03 10:42:54 -05:00
filtered
394df49208 Fix primitive size on load (#1407) 2024-11-03 09:29:39 -05:00
Chenlei Hu
38847e1079 1.3.31 (#1411) 2024-11-03 09:27:58 -05:00
Chenlei Hu
dd86417177 Disable flaky test on missing model download dialog (#1409) 2024-11-03 09:04:18 -05:00
filtered
1366c8cb44 Fix primitive resize when node size ref retained (#1405)
* Fix primitive resize when node size ref retained

Primitive assumes that setting node size property will replace the ref.  Minimal change.

* Use explicit variable names
2024-11-02 16:14:49 -04:00
Chenlei Hu
3a910f25e9 Track previous workflow name on Vue side (#1404) 2024-11-02 14:40:05 -04:00
Chenlei Hu
cc420b70a5 Add finally handler for rename tree node action (#1403)
* Add finally handler for rename tree node action

* nit
2024-11-02 11:39:15 -04:00
filtered
caa3ac2068 Add playwright concurrency - multi-user mode (#1400)
* Add playwright concurrency - multi-user mode

* Add extra server params

* nit

* Update to v2.1 action

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
2024-11-02 09:51:24 -04:00
Chenlei Hu
8baaf380dc Split jest tests into fast and slow groups (#1401) 2024-11-01 22:39:42 -04:00
Chenlei Hu
d719a4e0fb Move exportWorkflow from menu to workflowService (#1399) 2024-11-01 19:44:21 -04:00
Chenlei Hu
d254559e20 [Refactor] Extract createTemporary (#1398)
* [Refactor] Extract createTemporary

* nit
2024-11-01 19:32:50 -04:00
pythongosssss
d701758663 Add support for hidden & advanced widgets (#1389)
* Add support for hidden & advanced widgets

* Fix

* Update package

* Remove ts-expect-error

* Fix test, tidy
2024-11-01 19:12:44 -04:00
Chenlei Hu
a11b78d1c3 Remove deprecated method isFavourite (#1397) 2024-11-01 19:12:04 -04:00
Chenlei Hu
dfb695be72 [Refactor] Rework userFileStore to match existing API on ComfyWorkflow (#1394)
* nit

* Move load

* nit

* nit

* Update store API

* nit

* nit

* Move api

* nit

* Update tests

* Add docs

* Add temp user file

* Implement save as

* Test saveAs
2024-10-31 21:58:00 -04:00
Chenlei Hu
2974b9257a 1.3.30 (#1393) 2024-10-31 19:17:19 -04:00
Chenlei Hu
d11d07334b Add npm release script to automatically create release PR (#1392)
* Add release script

* nit
2024-10-31 19:14:35 -04:00
Chenlei Hu
0c8fe41b84 Fix queue ResultItem schema (#1386) 2024-10-30 20:36:33 -04:00
filtered
ed0592d6e0 Update litegraph API - add @ts-expect-error (#1380)
* Update litegraph API - add @ts-expect-error

LG update removes some implicit any, exposing existing errors

* Update litegraph

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
2024-10-30 20:05:04 -04:00
Chenlei Hu
94f4147f92 Fix double trigger of setting onChange callback (#1385)
* Fix onChange double trigger

* nit

* Add playwright test
2024-10-30 19:55:46 -04:00
Chenlei Hu
67ee8726ef 1.3.29 (#1383) 2024-10-30 16:34:49 -04:00
Chenlei Hu
e48c78541c Hide empty folders when searching in model library (#1382) 2024-10-30 16:27:35 -04:00
Chenlei Hu
bf7a9bf5eb Update litegraph (link snap to slot & highlight) (#1378)
* Update litegraph (link snap)

* Add settings

* nit

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-10-30 15:23:58 -04:00
filtered
3fb2d423ba Update litegraph API - 237 (#1376) 2024-10-30 13:43:38 -04:00
Chenlei Hu
74f7311585 Fix jest test mock (#1375) 2024-10-30 10:37:54 -04:00
Chenlei Hu
97c38583e9 Rename workspaceStateStore to workspaceStore (#1374) 2024-10-30 09:49:23 -04:00
Chenlei Hu
324eff93fd Update Litegraph API - canvas.state (#1372)
* Update Litegraph API - canvas.state

* Update litegraph

---------

Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
2024-10-29 22:07:04 -04:00
Chenlei Hu
4b1104f52c Add node id to execution error report (#1371) 2024-10-29 21:48:45 -04:00
Chenlei Hu
d702fc81a2 1.3.28 (#1370) 2024-10-29 20:30:07 -04:00
Chenlei Hu
2a94ab4423 Fix empty model library folder content (#1369) 2024-10-29 20:28:50 -04:00
Chenlei Hu
37f6c89383 1.3.27 (#1366) 2024-10-29 17:56:28 -04:00
Chenlei Hu
35fab0bef3 Focus mode (#1365)
* Menu hamburger

* Focus

* nit

* nit

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-10-29 17:25:18 -04:00
filtered
795e932b8f Fix husky pre-commit & type check only staged (#1361)
* Fix husky pre-commit on some Windows clients

https://github.com/typicode/husky/issues/1072#issuecomment-1784006332

* Limit commit type check to staged files

Adds tsc-files package (dev only) and its config

* Remove deprecated git add from lint-staged
2024-10-29 14:15:07 -04:00
Chenlei Hu
8dddffe840 Use browser download in missing model dialog (#1362)
* Remove custom backend download logic

* Add download hooks

* Download button

* Use browser download

* Update test
2024-10-29 14:07:16 -04:00
Chenlei Hu
1f91a88d7b Move linkRenderMode extension to core (#1359) 2024-10-29 11:00:10 -04:00
Chenlei Hu
10f43be911 Remove show model folder checkbox in missing model dialog (#1358)
* Remove show model folder checkbox in missing model dialog

* nit

* nit
2024-10-29 10:15:31 -04:00
Chenlei Hu
87517daf1f Restyle missing model warning dialog (#1354)
* Restyle missing model dialog

* nit

* nit

* nit

* nit
2024-10-29 09:26:02 -04:00
Chenlei Hu
739ebd3d04 Auto-expand model library tree on search (#1357) 2024-10-29 09:24:56 -04:00
Chenlei Hu
4582c71583 [Refactor] Rework modelStore and ModelLibrarySidebarTab (#1350)
* nit

* Rename

* nit

* Move load model folders to app level

* Various fixes

* nit

* nit

* wip

* nit

* nit

* nit

* Split

* nit

* Add back spinner

* nit

* nit

* Add refresh button

* nit

* nit

* Preserve model folder order

* Avoid order change on folder open
2024-10-28 21:23:53 -04:00
Chenlei Hu
757f0ced81 1.3.26 (#1353) 2024-10-28 19:59:43 -04:00
Chenlei Hu
a471a3f302 [skip ci] Add typecheck pre-commit hook (#1352) 2024-10-28 19:56:04 -04:00
Chenlei Hu
5a3a8d32ab Update litegraph 0.8.10 (#1351) 2024-10-28 19:49:48 -04:00
Chenlei Hu
44b109a449 Add type annotation for missingNodeTypes (#1349)
* Add type annotation for missingNodeTypes

* nit

* nit
2024-10-28 17:26:52 -04:00
Chenlei Hu
229896a4b7 Restyle missing node warning dialog (#1348)
* nit

* Restyle missing node warning dialog

* nit

* nit
2024-10-28 16:45:47 -04:00
Chenlei Hu
997b5ee819 Update litegraph (Fix group select) (#1342)
* Update litegraph (Fix group select)

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-10-27 22:05:18 -04:00
Chenlei Hu
ba99eca700 1.3.25 (#1341) 2024-10-27 20:07:47 -04:00
Chenlei Hu
82c369322d Handle invalid node def errors (#1340)
* nit

* Add error handling

* nit

* nit
2024-10-27 20:07:05 -04:00
Chenlei Hu
546c5dabc8 Update litegraph (Slot highlight) (#1339)
* Update litegraph (Slot highlight)

* Disable tooltip setting in playwright tests

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-10-27 19:31:22 -04:00
filtered
7d450adf93 Remove unused param - litegraph update (#1335) 2024-10-27 16:21:14 -04:00
Chenlei Hu
eed92864f2 Enable ts-strict for changeTracker (#1338)
* Fix app getter

* nit

* nit

* nit

* Fix rest of errors

* nit
2024-10-27 16:20:32 -04:00
Chenlei Hu
8fd7852740 Enable ts-strict for nodeBookmarkStore (#1336) 2024-10-27 15:06:37 -04:00
Chenlei Hu
2a927bb9ea [skip ci] Update README (Dev section) (#1334)
* Expand test section

* Add techstack section
2024-10-27 10:12:54 -04:00
Chenlei Hu
c566491ac7 Enable ts-strict for queueStore (#1333) 2024-10-27 10:07:09 -04:00
Zoltán Dócs
7729611a2a Bugfix node widgets wrongly being prepared for qeueing on saving the workflow (#1331)
- calling graphToPrompt() invokes beforeQueued() which should not happen when saving the workflow
2024-10-27 09:59:27 -04:00
Chenlei Hu
8861492655 Enable ts-strict for workflowStore (#1332) 2024-10-27 09:59:17 -04:00
Chenlei Hu
fa9d944b32 Convert pinia stores from options API to composition API (#1330)
* Convert toastStore

* Convert workspaceStateStore

* Convert settingStore

* Convert queueStore

* Convert modelToNodeStore

* Convert modelStore

* Convert dialogStore

* nit

* nit

* nit
2024-10-27 08:47:24 -04:00
Chenlei Hu
880437f3c0 1.3.24 (#1326) 2024-10-26 19:50:05 -04:00
Chenlei Hu
c1e960c83c Update litegraph (Multi-group selection) (#1325) 2024-10-26 18:26:59 -04:00
Chenlei Hu
571386c061 Enable ts-strict for api.ts (#1324) 2024-10-26 18:08:24 -04:00
Chenlei Hu
44d886a18b Enable ts-strict for ui/settings (#1323) 2024-10-26 17:25:15 -04:00
Chenlei Hu
92ac403679 Enable ts-strict for settingStore (#1322) 2024-10-26 17:07:17 -04:00
Chenlei Hu
dc3dab4e1c Enable ts-strict for commandStore (#1321) 2024-10-26 17:05:42 -04:00
Chenlei Hu
386594554e Enable ts-strict for executionStore (#1320)
* Fix listener types

* nit

* nit

* fix type
2024-10-26 17:00:18 -04:00
Chenlei Hu
02a951ad58 Enable ts-strict for nodeDefStore (#1319) 2024-10-26 16:02:07 -04:00
Chenlei Hu
92f0f4a21c Convert nodeDefStore to use composition API (#1318)
* Convert nodeDefStore to use composition API

* nit
2024-10-26 15:48:40 -04:00
Chenlei Hu
645897f8b8 [Refactor] Make node badge a vue component (#1317)
* [Refactor] Make node badge a vue component

* Simplify badge logic

* nit
2024-10-26 15:28:14 -04:00
Chenlei Hu
ef4179a06c Update litegraph to 0.8.6 (#1316)
* Update litegraph

* Remove ts-expect-error
2024-10-26 14:36:45 -04:00
Chenlei Hu
a3e4af40c1 1.3.23 (#1314) 2024-10-25 20:13:42 -04:00
Björn Söderqvist
1dedce5ec6 Enable ts-strict for colorUtil, contextMenuFilter and linkRenderMode (#1313)
* Add types for colorUtil.ts

* Add types for contextMenuFilter

* Add types to linkRenderMode.ts
2024-10-25 15:42:15 -04:00
Chenlei Hu
3a4b36fb31 Disallow node library bookmark folder name with / (#1311) 2024-10-25 12:35:53 -04:00
Chenlei Hu
25457d31d4 Revert "Fix playwright test on settings API (#1303)" (#1310)
This reverts commit 54e833502a.
2024-10-25 12:09:50 -04:00
Chenlei Hu
4f9dc830b6 Setup clean setting state before every playwright test (#1309)
* Use reload

* setting setup

* nit

* Remove setting cleanups

* Wait for frame

* nit

* nit
2024-10-25 11:25:17 -04:00
Chenlei Hu
12d421b42c Update litegraph (TypeScript LiteGraphGlobal) (#1308) 2024-10-25 08:45:04 -04:00
Chenlei Hu
16ebfd6171 Fix import of NodeReference in ComfyPage (#1306) 2024-10-25 08:32:12 -04:00
Chenlei Hu
59c999324e Split ComfyPage fixture (#1305)
* Split down page components

* Move litegraph utils

* nit
2024-10-25 08:29:02 -04:00
Chenlei Hu
624bcc75ab Move ComfyPage to fixtures folder (#1304) 2024-10-25 08:01:50 -04:00
Chenlei Hu
54e833502a Fix playwright test on settings API (#1303) 2024-10-25 07:53:23 -04:00
Chenlei Hu
c3242711c7 Update bug-report.yaml (Add settings section) (#1301) 2024-10-25 07:46:45 -04:00
Chenlei Hu
17391e4aad Handle potential undefined node data in NodeBookmarkTreeExplorer (#1300) 2024-10-25 07:41:43 -04:00
Chenlei Hu
48b840a88d 1.3.22 (#1297) 2024-10-24 20:03:40 -04:00
Chenlei Hu
377fed584f [Extension API] Register custom bottom panel tabs (#1296)
* Bottom panel API

* Update README
2024-10-24 19:58:44 -04:00
Chenlei Hu
14a6687cc9 Integrated terminal (#1295)
* Add terminal tab

* Add basic terminal

* Style terminal

* Add keybinding

* Auto scroll:

* Mock for jest test
2024-10-24 17:31:00 -04:00
Chenlei Hu
d142893244 Add bottom panel support (#1294)
* Add bottom panel

* Bottom panel store

* Extract ExtensionSlot component

* Tab rendering

* Add toggle button on top menu bar

* nit

* Add toggle button tooltip

* Add command
2024-10-24 15:15:19 -04:00
Chenlei Hu
957a767ed0 New settings API (#1292)
* Add settings API

* Add playwright test

* Update README
2024-10-24 10:26:01 -04:00
YANG Zhitao
3553c8e0d4 Enable ts-strict for slotDefaults.ts (#1290) 2024-10-24 07:31:45 -04:00
YANG Zhitao
05221f7961 Enable ts-strict for clipspace.ts (#1291) 2024-10-24 07:31:15 -04:00
Chenlei Hu
d113072a64 1.3.21 (#1289) 2024-10-23 20:38:50 -04:00
Chenlei Hu
afa619b7df Revert "Enable ts-strict for invertMenuScrolling.ts (#1264)" (#1288)
This reverts commit 9388ee0705.
2024-10-23 20:37:40 -04:00
Chenlei Hu
e25bbc19cb Revert "Enable ts-strict for contextMenuFilter.ts (#1263)" (#1287)
This reverts commit b655c5544d.
2024-10-23 20:37:03 -04:00
Chenlei Hu
0251bc9e6c 1.3.20 (#1285) 2024-10-23 19:53:33 -04:00
Chenlei Hu
7a3f20c57d Fix sidebar scrollpanel style (#1283) 2024-10-23 18:27:54 -04:00
Chenlei Hu
269fc7c8c9 Remove misleading console error on paste (#1282) 2024-10-23 14:59:24 -04:00
Chenlei Hu
db08f74d6a Update litegraph (TypeScript LgraphNode) (#1281) 2024-10-23 14:38:09 -04:00
Chenlei Hu
5db757ade2 Enable ts-strict for uploadImage.ts (#1280) 2024-10-23 13:59:47 -04:00
Chenlei Hu
59c03d2de5 [Refactor] Rename ModelStore to ModelFolder (#1244)
* Refactor click

* Rename ModelStore to ModelFolder
2024-10-23 12:04:49 -04:00
YANG Zhitao
b655c5544d Enable ts-strict for contextMenuFilter.ts (#1263)
* Enable ts-strict for contextMenuFilter.ts

* function instead of arrow function

* tackle @ts-expect-error at contextMenuFilter.ts by use class instead of function + prototype
2024-10-23 12:03:50 -04:00
YANG Zhitao
9388ee0705 Enable ts-strict for invertMenuScrolling.ts (#1264) 2024-10-23 12:03:33 -04:00
juju
f228ec29eb Enable ts-strict for editAttention.ts (#1269)
* fix typecheck errors

* revert tsconfig strict true
2024-10-23 12:02:52 -04:00
Yoland Yan
7239e94092 Mini lang (#1245) 2024-10-13 09:56:58 +02:00
Chenlei Hu
e33a5f7736 1.3.19 (#1243) 2024-10-12 17:21:59 -04:00
Chenlei Hu
c1c990e6f3 Properly show empty folder in model sidebar tab (#1242)
* Properly show empty folder in model sidebar tab

* nit

* nit
2024-10-12 17:02:58 -04:00
Chenlei Hu
a890756868 Enable ts-strict for modelStore.ts (#1241) 2024-10-12 16:38:27 -04:00
Chenlei Hu
015ee2df15 Revert "Enable ts-strict for colorUtil.ts (#1239)" (#1240)
This reverts commit c3b2697568.
2024-10-12 15:58:43 -04:00
Chenlei Hu
c3b2697568 Enable ts-strict for colorUtil.ts (#1239) 2024-10-12 14:11:32 -04:00
Chenlei Hu
dfcabd2834 Enable ts-strict for treeUtil.ts (#1238) 2024-10-12 14:10:08 -04:00
Chenlei Hu
634196cbd6 Enable ts-strict for dialogStore.ts (#1236) 2024-10-12 12:17:55 -04:00
Chenlei Hu
5611e90fda Add ts-strict-ignore plugin (#1235)
* Add ts-strict-ignore plugin

* nit

* Add to typecheck script
2024-10-12 11:56:49 -04:00
Chenlei Hu
c23d95f8f9 1.3.18 (#1232) 2024-10-11 16:27:50 -04:00
Chenlei Hu
c2377b62ac Fix sidebar tab bg color (#1229)
* Fix node library sidebar style

* Fix styles

* nit
2024-10-11 15:04:29 -04:00
Chenlei Hu
f328d4cd81 Hide system scrollbar for queue sidebar tab (#1227) 2024-10-11 13:59:51 -04:00
Chenlei Hu
fbc1482b90 Use scrollpanel in sidebar template (#1226)
* Use scrollpanel in sidebar template

* Migrate

* nit
2024-10-11 13:41:41 -04:00
Chenlei Hu
90e07af4f5 Update litegraph (TypeScript LGraphCanvas) (#1225) 2024-10-11 13:39:19 -04:00
AustinMroz
014c3f3172 Fix load workflow with converted widget (#1222)
f74973 introduced a regression where a node which has had an input
added, will clobber a converted input of in a workflow created before
the input was added.

The prior behaviour was also incorrect (the new input would not exist on
the node), but would often be runnable.

Keeping the position of the converted widget and adding the new input to
the end is an unfortunate compromise. Doing it the other way around
breaks primitive nodes
2024-10-11 12:20:03 -04:00
Chenlei Hu
419009424b Handle missing extraPngInfo field in queue (#1223) 2024-10-11 12:16:35 -04:00
Chenlei Hu
f599c9bcb8 Fix format (#1224) 2024-10-11 12:16:17 -04:00
Chenlei Hu
6c696cddb9 Fix dialog header component error (#1221) 2024-10-11 11:55:13 -04:00
Chenlei Hu
d787c21f8b [skip ci] Explicitly specify prettier printWidth (#1220) 2024-10-11 11:53:35 -04:00
Chenlei Hu
f96f08be32 Add ru to locale list (#1218) 2024-10-11 10:43:10 -04:00
Ioan
8ebb51b9a3 Update i18n.ts (#1214)
* Update i18n.ts

Added Russian translation

* Update i18n.ts
2024-10-11 09:14:04 -04:00
Chenlei Hu
60e1b82df6 Update litegraph (TypeScript LLink) (#1213)
* Update litegraph (TypeScript LLink)

* Remove ts-expect-error
2024-10-10 20:54:31 -04:00
Chenlei Hu
459afa158c Restyle dialog with scrollpanel (#1212) 2024-10-10 20:27:14 -04:00
Chenlei Hu
1c3d3b33f6 1.3.17 (#1211) 2024-10-10 16:52:01 -04:00
Chenlei Hu
f64365915b Mark app.showMissingNodeDialog private (#1210) 2024-10-10 16:35:29 -04:00
Chenlei Hu
ec8e6f79b3 Fix create group node command error states (#1209)
* Fix edge cases

* Add playwright test

* nit
2024-10-10 15:56:00 -04:00
Chenlei Hu
b89f467983 Add group node commands/keybindings (#1208)
* Add group node commands/keybindings

* Fix jest tests
2024-10-10 12:50:05 -04:00
Chenlei Hu
009dbcf8c7 Wrap pragmatic dnd API with hooks (#1207) 2024-10-10 10:53:49 -04:00
Chenlei Hu
4413fd248c Remove question mark badge on folders in model library tree (#1205) 2024-10-10 10:13:14 -04:00
Chenlei Hu
8962597e69 Update litegraph (TypeScript LGraph) (#1206) 2024-10-10 10:13:02 -04:00
Chenlei Hu
f4d4111fbd 1.3.16 (#1202) 2024-10-09 22:17:57 -04:00
Chenlei Hu
babac5a4a9 1.3.15 (#1201) 2024-10-09 22:15:06 -04:00
Chenlei Hu
f71595fcc9 Fix node def handling of undefined fields (#1199)
* Fix node def handling

* nit

* Add test
2024-10-09 22:11:27 -04:00
Chenlei Hu
59a5f5f5d0 Add help menu on command menu bar (#1197)
* Add help menu on command menu bar

* nit
2024-10-09 20:35:17 -04:00
Chenlei Hu
2d5faa7f3d Anchor floating actionbar to closest side when resizing window (#1195)
* Anchor floating actionbar to closest side when resizing window

* nit

* nit
2024-10-09 17:41:36 -04:00
Chenlei Hu
32fa950aa1 1.3.14 (#1194) 2024-10-09 16:41:38 -04:00
Chenlei Hu
82112c2c6e Move low priority init to idle task (#1192) 2024-10-09 16:22:58 -04:00
Chenlei Hu
f94bdc358b Disable node def validation by default (#1190)
* Add setting

* Make node def validation optional
2024-10-09 16:02:27 -04:00
Chenlei Hu
f6466d7062 Avoid calling settingStore.set when there is no legacy node bookmark (#1191)
* Avoid calling settingStore.set when there is no legacy node bookmark

* nit
2024-10-09 16:02:14 -04:00
Chenlei Hu
1c5fd2465e Move vitejs/plugin-vue to devDep (#1189) 2024-10-09 15:16:43 -04:00
Chenlei Hu
e99329cff5 Remove class-transformer dependency (#1187)
* nit

* Fix test

* Remove class-transformer and its deps

* nit

* Fix invalid type for dummy node
2024-10-09 15:10:19 -04:00
bymyself
fabcbaec82 Restore backend state when Playwright test finishes (#1168)
* Restore backend state when Playwright test finishes. Resolve #1094

* Add warning when env var not set

* Rename and replace with scaffolding option for models dir

* Rename

* Define another env var [skip ci]

* Fix paths [skip ci]

* Update README.md

---------

Co-authored-by: Chenlei Hu <huchenlei@proton.me>
2024-10-09 15:05:19 -04:00
Chenlei Hu
c8f50509ed Fix VHS advanced preview html video type (#1186)
* Fix VHS advanced preview html video type

* nit
2024-10-09 15:02:02 -04:00
Chenlei Hu
165604bb80 Support VHS advanced preview in queue sidebar tab (#1183)
* Map VHS video type

* Advance preview format

* nit

* View VHS advanced preview

* Disable result gallery vitest

* Proper disable
2024-10-09 12:42:00 -04:00
Chenlei Hu
829bce1c8c Load Keybinding and Extension panel async (#1179) 2024-10-08 22:20:30 -04:00
Chenlei Hu
c3b82165fa 1.3.13 (#1178) 2024-10-08 20:14:56 -04:00
Chenlei Hu
d673a521d8 Add always snap to grid setting (#1177)
* Always snap to grid

* Ban pysssss.SnapToGrid

* nit
2024-10-08 20:12:23 -04:00
Chenlei Hu
ee88a79bc3 1.3.12 (#1175) 2024-10-08 17:12:53 -04:00
Chenlei Hu
5f3afa3776 Supports VHS video outputs in queue sidebar tab (#1174)
* Properly identify gifs

* Detect VHS video

* Basic video support in queue

* Video in lightbox

* Preview button

* nit

* Fix vitest
2024-10-08 17:10:44 -04:00
Chenlei Hu
3cafc10c2b Fix pan mode icon display (#1173) 2024-10-08 14:24:39 -04:00
Chenlei Hu
2cb1cea196 Make LGraphCanvas shallowReactive (#1169)
* Make LGraphCanvas shallowReactive

* Restore canvas options after creation
2024-10-08 14:07:15 -04:00
Chenlei Hu
482da21ba7 Remove state check on continuous keydown (#1171)
* Remove state check on continuous keydown

* nit
2024-10-08 11:01:08 -04:00
bymyself
bf80340310 Add test on settings visibility on mobile (#1164)
* Add test on settings dialog visibility on mobile

* Consolidate settings-dialog tests

* Simplify zoom speed setting tests
2024-10-08 09:43:16 -04:00
bymyself
5ef15c0daf Add aria labels to dialogs (#1167) 2024-10-08 09:41:28 -04:00
bymyself
62be958d47 Add tests on node multi-select (#1163) 2024-10-08 09:11:22 -04:00
Chenlei Hu
1ba236bbce Simplify node tooltip lifecycle (#1162) 2024-10-07 22:13:52 -04:00
Chenlei Hu
a4e08f60fe Extract theme toggle as command (#1161)
* Extract theme toggle as command

* nit
2024-10-07 21:54:58 -04:00
Chenlei Hu
5ba1d1a3f7 Show sidebar toggle keybinding shortcut on sidebar icon tooltip (#1160) 2024-10-07 21:44:38 -04:00
Chenlei Hu
58dd15a662 Fix core sidebar tab toggle command register (#1159) 2024-10-07 21:32:30 -04:00
Chenlei Hu
50a6ee27a0 Refactor core sidebar tab registration (#1158)
* Refactor sidebar tab register

* Register core tabs
2024-10-07 21:23:52 -04:00
Chenlei Hu
23952d9751 Show queue front icon when shift is pressed (#1157)
* Move shiftDown state to workspaceStateStore

* Queue front state
2024-10-07 19:54:00 -04:00
Chenlei Hu
2b26514190 1.3.11 (#1154) 2024-10-07 17:27:25 -04:00
Chenlei Hu
f8343d0f93 Fix flaky playwright test (#1152) 2024-10-07 17:11:20 -04:00
Chenlei Hu
cc17bee945 Manage app.ts litegraph keybindings (#1151)
* Manage app.ts litegraph keybindings

* nit
2024-10-07 16:50:58 -04:00
Alex "mcmonkey" Goodwin
ff1ca268a4 Model Library sidebar: allow searching metadata (#1148)
* Model Library sidebar: allow searching metadata

title, description, etc

* don't use vue stuff inside of vue because vue doesn't support vue

very cool

* remove old import

* and that one
2024-10-07 14:50:45 -04:00
Chenlei Hu
99c948f578 Update README (#1149) 2024-10-07 14:46:43 -04:00
Alex "mcmonkey" Goodwin
d68a1116dc ModelToNodeStore minor fix (#1147) 2024-10-07 13:13:15 -04:00
Chenlei Hu
dee1ec1a2a Update Litegraph (TypeScript conversion) (#1145)
* Fix various type errors

* Fix rest of ts errors

* update litegraph

* nit
2024-10-07 11:31:54 -04:00
bymyself
9cbfc9856b Fix widget/input conversion on text widgets (#1129)
* Support converting dynamically created text widgets to input

* Fix array contains

* Add test wait

* Try to fix test only failing in CI

* Fix test: Disable conversion option nesting in contextmenu
2024-10-07 11:22:28 -04:00
bymyself
a95a6f9b47 Fix saved workflow cleanup in menu tests (#1142)
* Fix saved workflow cleanup in menu tests

* Clear workflow dir before each test
2024-10-07 09:35:15 -04:00
Chenlei Hu
c83ce863d7 Rework command menu extension API (#1144)
* Rework command menu API

* Update test

* Update README

* Prevent register other extension's command
2024-10-06 23:31:57 -04:00
Chenlei Hu
05aa78372b Add keybinding search (#1143) 2024-10-06 22:40:20 -04:00
Chenlei Hu
38e3dcbaeb Add frontend extension management panel (#1141)
* Manage register of extension in pinia

* Add disabled extensions setting

* nit

* Disable extension

* Add virtual divider

* Basic extension panel

* Style cell

* nit

* Fix loading

* inactive rules

* nit

* Calculate changes

* nit

* Experimental setting guard
2024-10-06 22:15:33 -04:00
Chenlei Hu
cfa763962e Update playwright test fixture (#1139)
* Update playwright test fixture

* fix resolve

* nit

* Wait dialog close
2024-10-06 21:21:55 -04:00
Chenlei Hu
8c156cc651 Replace window.dialog with prompt dialog in workflows.ts (#1138) 2024-10-06 19:31:46 -04:00
Chenlei Hu
c7aabecc0e 1.3.10 (#1137) 2024-10-06 17:25:09 -04:00
Chenlei Hu
defacf3398 Remove unused code (#1136) 2024-10-06 16:22:48 -04:00
Chenlei Hu
7f2920644e Revert "Remove model library searchbox (#1133)" (#1135)
This reverts commit 1b3cc4de1a.
2024-10-06 16:02:14 -04:00
Chenlei Hu
c92ff79231 Add workflow tab tooltip to show full path (#1134) 2024-10-06 12:10:54 -04:00
bymyself
3c70c1e463 Show keybinding on topbar dropdown menus (#1127)
* Show keybinding on topbar dropdown menus, resolve #1092

* Add text-muted to tailwind config

* Add Playwright test

* Preserve Primevue classes in menu item template

* Extend MenuItem

* Revert adding undo/redo to core keybindings

* Change test selector

* refactor

* Extract as component

* refactor

* nit

* fix extension API

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
2024-10-06 12:08:16 -04:00
Chenlei Hu
1b3cc4de1a Remove model library searchbox (#1133) 2024-10-06 11:44:38 -04:00
Chenlei Hu
b97331cbab Restyle SettingGroup (#1125)
* Setting group CSS

* nit

* nit

* Dim label color

* nit

* Set width
2024-10-05 20:34:15 -04:00
Chenlei Hu
b7287dbb22 1.3.9 (#1124) 2024-10-05 18:09:51 -04:00
Chenlei Hu
2c90735bb1 Restore top/bottom menu location setting (#1123)
* Rename floating to top

* Adjust teleport target

* Fix dropdown direction for bottom menubar

* Fix z-index
2024-10-05 18:08:48 -04:00
Chenlei Hu
4d5fbeff45 Add eslint-plugin-unused-imports (#1121) 2024-10-05 16:36:02 -04:00
Chenlei Hu
ad55722662 Docking action bar on top menu bar (#1119)
* Teleport when docked

* Docking logic

* Remove unnecessary v-show

* Docked panel style

* Drop zone highlight

* Rename test

* Add playwright test
2024-10-05 14:06:29 -04:00
Chenlei Hu
9c118c8e37 Add '- ComfyUI' suffix on browser title (#1118)
* Add '- ComfyUI' suffix on browser title

* Update test expectations
2024-10-05 11:35:17 -04:00
Chenlei Hu
267660a661 Extract QueueButton as component (#1117) 2024-10-05 10:07:50 -04:00
filtered
f2017291d6 Prevent converted widget being duplicated (#1115) 2024-10-05 08:49:17 -04:00
Chenlei Hu
4cc69544b5 Replace window.alert with toast alert (#1112)
* Replace window.alert with toast alert

* Mock jest
2024-10-04 22:00:44 -04:00
Chenlei Hu
2649d72d3f Refactor sidebarTabStore (#1111) 2024-10-04 21:20:35 -04:00
Chenlei Hu
a852b8e6e1 [skip ci] Add ersionAdded to newly added commands (#1110) 2024-10-04 20:41:05 -04:00
Chenlei Hu
6deb994235 Add command to switch opened workflow tabs (#1109) 2024-10-04 20:33:16 -04:00
Chenlei Hu
b30d285025 Add toggle command for each sidebar tab registered (#1108)
* Add toggle command for each sidebar tab registered

* nit
2024-10-04 20:22:10 -04:00
Chenlei Hu
18476d28dc 1.3.8 (#1107) 2024-10-04 16:32:46 -04:00
Chenlei Hu
57a4cb9036 Add default toast error handling for command execution (#1106)
* Error handling execute command

* Cleanup

* Add playwright test

* Mock i18n in jest test

* Reduce test func timeout
2024-10-04 16:28:08 -04:00
Chenlei Hu
ebc71b0e46 Add PromptDialog to replace window.prompt (#1104)
* Save file prompt dialog

* Don't download if dialog dismissed

* refactor

* style dialog

* nit

* Autofocus
2024-10-04 15:33:27 -04:00
Chenlei Hu
39d68bcdc4 Remove new default workflow button in workflows sidebar (#1100) 2024-10-04 11:37:37 -04:00
Chenlei Hu
e20126a254 Rename AppMenu to Actionbar (#1099)
* Rename AppMenu to Actionbar

* nit

* nit
2024-10-04 06:49:06 -04:00
Chenlei Hu
416fd0aed6 Restyle action bar (#1098) 2024-10-04 06:30:55 -04:00
Zoltán Dócs
661b8081c1 Show in-progress preview of the running task in the queue (#1091) 2024-10-03 18:48:06 -04:00
Chenlei Hu
64b5f4e7d5 1.3.7 (#1090) 2024-10-03 17:04:54 -04:00
Chenlei Hu
1775d43d90 Support keybinding customization (#1081)
* Basic keybinding panel

nit

Make row selectable

Reduce padding

Better key seq render

Show actions on demand

Turn off autocomplete

nit

Persist keybindings

Autofocus

Fix set unsetted keybinding bug

Refactor

Add reset button

Add back default keybinding logic

Report key conflict error

Adjust style

fix bug

Highlight modified keybindings

* Set current editing command's id as dialog header
2024-10-03 16:58:56 -04:00
Chenlei Hu
142882a8ff Move keybinds to coreKeybindings (#1078)
* Refactor core keybinds

* Prevent default

* Add playwright test
2024-10-03 11:25:53 -04:00
Robin Huang
65cad74eba Add model download URL and change to use safetensors. (#1076) 2024-10-03 09:08:26 -04:00
Chenlei Hu
77aaa38a92 [Extension API] Custom commands and keybindings (#1075)
* Add keybinding schema

* nit

* Keybinding store

* nit

* wip

* Bind condition on ComfyCommand

* Add settings

* nit

* Revamp keybinding store

* Add tests

* Add load keybinding

* load extension keybindings

* Load extension commands

* Handle keybindings

* test

* Keybinding playwright test

* Update README

* nit

* Remove log

* Remove system stats fromt logging.ts
2024-10-02 21:38:04 -04:00
Chenlei Hu
ea3d8cf728 1.3.6 (#1073) 2024-10-02 16:02:37 -04:00
Alex "mcmonkey" Goodwin
b3a624a572 Allow dragging model library outputs onto existing nodes (#1004)
* allow multiple compatible node registrations for model type

* allow dragging model library outputs onto existing nodes

* easier registration

* add alt loaders for checkpoint and lora
2024-10-02 15:53:19 -04:00
bymyself
a737be7e16 Fix group node copy paste (#1069)
* Fix group node copy paste

* nit

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
2024-10-02 15:51:33 -04:00
Acly
aca2194892 Emit graph changed event after modifying a widget value via keyboard (#1072) 2024-10-02 15:28:42 -04:00
Chenlei Hu
3a2b2f9e15 Add toggle link visibility button on canvas menu (#1070)
* Basic link visibility toggle

* Icon change

* nit

* Update litegraph

* nit

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-10-02 15:23:37 -04:00
Chenlei Hu
a19f713c57 Revert "Hide Comfy.Workflow.WorkflowTabsPosition (#984)" (#1071)
This reverts commit a41f3b1ac6.
2024-10-02 15:14:23 -04:00
Chenlei Hu
8b2ef3c352 Use jpg for template workflow thumbnail (#1066) 2024-10-02 12:11:09 -04:00
pythongosssss
861bcabd66 Add support for multiple changes in a single ChangeTracker state (#1022)
* wip

* Add tests

* Update package

* remove logs

* nit

* nit

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
2024-10-02 11:53:54 -04:00
Alex "mcmonkey" Goodwin
cc2b64df52 add filename in model preview popup (#1005)
* add filename in model preview popup

for #1003

* user setting for model name fomat in the tree

* add a tooltip for the setting to explain what things mean

* more explicit file_name naming

* touch of additional text in the tooltip
2024-10-02 10:59:01 -04:00
Alex "mcmonkey" Goodwin
a7a0035b0e allow custom bypass color (#993)
* allow custom bypass color

* shove the not-litegraph-color-def into litegraph

* prettier just does this to be funny i swear
2024-10-02 10:49:59 -04:00
bymyself
31b1aeeb69 Add test for selecting nodes on mac (#1055)
* Add test for selecting nodes on mac

* Deselect nodes in teardown

* Fix unstable test. Remove test on unimplemented feature
2024-10-02 10:42:57 -04:00
Chenlei Hu
3f10fd53bd 1.3.5 (#1061) 2024-10-01 16:39:36 -04:00
Chenlei Hu
0194d76722 Make max batch count configurable (#1060) 2024-10-01 15:42:12 -04:00
Chenlei Hu
98a0291bbd Remove update-main action (#1058) 2024-10-01 13:14:53 -04:00
Chenlei Hu
f9fc36f0ed Cleanup zip file on release (#1057) 2024-10-01 13:13:18 -04:00
pythongosssss
a2bd2a9bae Implement creating inputs by dragging link to widget (#1021)
* Implement creating inputs by dragging link to widget

* Update litegraph

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: huchenlei <huchenlei@proton.me>
Co-authored-by: github-actions <github-actions@github.com>
2024-10-01 12:53:38 -04:00
Chenlei Hu
c42222cf0d Update litegraph (Pan when dragging link) (#1056)
* Update litegraph (Pan when dragging link)

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-10-01 10:54:28 -04:00
Chenlei Hu
271a4979b7 [Skip CI] Remove outdated roadmap (#1052) 2024-09-30 20:25:35 -04:00
Chenlei Hu
1c980397b8 1.3.4 (#1051) 2024-09-30 20:21:52 -04:00
Chenlei Hu
5d957a05b9 Replace locking/unlocking of canvas with select/pan mode (#1050)
* Add dragging canvas state

* Change icon

* Add playwright test

* tooltip change

* Enable for legacy menu

* Update litegraph

* Hide canvas menu for test
2024-09-30 20:20:49 -04:00
pythongosssss
f75f774ddb Graph canvas menu (#1023)
* add graph canvas menu

* Move to corner

* Remove action bar reset zoom button

* nit

* Add setting

---------

Co-authored-by: huchenlei <chenlei.hu@mail.utoronto.ca>
2024-09-30 16:06:43 -04:00
bymyself
224c0080ee Fix topbar submenu width on mobile/tablet (#1047)
* Fix topbar submenu width on tablet

* Add Playwright test

* Use better selector

* nit

---------

Co-authored-by: Chenlei Hu <chenlei.hu@mail.utoronto.ca>
2024-09-30 15:21:09 -04:00
bymyself
6ea5fea1a7 Fix menu drag on touch device (#1046) 2024-09-30 15:15:58 -04:00
bymyself
04e1344676 Fix closing saved workflows (#1049)
* Fix workflow save. Resolves #996 Resolves #1048

* Add test on closing tabs

* Add test on closing open workflows in sidebar
2024-09-30 15:09:24 -04:00
Chenlei Hu
0117964ca5 1.3.3 (#1036) 2024-09-28 11:22:19 +09:00
Chenlei Hu
9d110d39b2 Make action bar draggable (#1035)
* Basic draggable

* Nowrap

* Prevent double reset

* Persist position

* nit

* nit

* Window resize adjustment

* Fix playwright test
2024-09-28 11:21:38 +09:00
Chenlei Hu
8d7693e5ad 1.3.2 (#1024) 2024-09-27 15:31:48 +09:00
Yoland Yan
ec9a30d269 Minor: change app-menu bottom spacing (#1020) 2024-09-27 15:10:13 +09:00
Chenlei Hu
56fc2dd753 Update litegraph (Canvas readonly attr change event) (#1019) 2024-09-27 15:00:54 +09:00
Chenlei Hu
9050591ff9 Update litegraph (Drag cursor shape) (#1014) 2024-09-27 10:51:52 +09:00
Alex "mcmonkey" Goodwin
0cf21b190c don't remove invalid dropdown values (#998)
for #963
2024-09-27 09:03:55 +09:00
Alex "mcmonkey" Goodwin
2531ec178e minor css improvement with editable text (#990) 2024-09-27 08:26:24 +09:00
Alex "mcmonkey" Goodwin
81119acaf2 add a hotkey for settings (#991)
* add a hotkey for settings

for #942

* playwright test for settings menu hotkey

* make hotkey intercompatible with both old and new UI
2024-09-27 08:25:56 +09:00
Chenlei Hu
05f999903d 1.3.1 (#989) 2024-09-26 16:23:35 +09:00
Chenlei Hu
66c02d1e3a Revert "Mark Comfy.Workflow.ShowMissingModelsWarning as stable & enabled by d…" (#988)
This reverts commit cdaa0bda5b.
2024-09-26 16:19:09 +09:00
Chenlei Hu
a05df99a8a Update litegraph (Link copy) (#986)
* Update litegraph (Link copy)

* Update readme
2024-09-26 16:06:59 +09:00
Chenlei Hu
e200e2f89c Add model info for flux workflow template (#987) 2024-09-26 16:06:37 +09:00
Chenlei Hu
cdaa0bda5b Mark Comfy.Workflow.ShowMissingModelsWarning as stable & enabled by default (#985) 2024-09-26 15:10:17 +09:00
Chenlei Hu
a41f3b1ac6 Hide Comfy.Workflow.WorkflowTabsPosition (#984) 2024-09-26 15:10:03 +09:00
Chenlei Hu
3585cb69f5 Allow extension register custom topbar menu command (#982)
* Refactor command store

* Rename coreMenuStore to menuStore

* Extension API to register command

* Update README

* Add playwright test
2024-09-26 10:44:15 +09:00
Chenlei Hu
a53f0ba4db 1.3.0 (#981) 2024-09-26 08:51:13 +09:00
huchenlei
7300f6edc2 Format 2024-09-26 08:48:51 +09:00
christian-byrne
1126eaa346 Add test workflow 2024-09-26 08:48:51 +09:00
christian-byrne
7e5d82d0e8 Use visibility of textarea to determine if loaded correctly 2024-09-26 08:48:51 +09:00
christian-byrne
2b2b1cdb85 Add Playwright test 2024-09-26 08:48:51 +09:00
christian-byrne
0b7c1609fd Fix nodes with only optional inputs 2024-09-26 08:48:51 +09:00
Chenlei Hu
7760f91a56 Revert "Disable broken playwright test by backend change (#967)"
This reverts commit da651eee6f.
2024-09-25 19:20:08 +09:00
七海千秋
58d8ab40c4 add missing i18n messages
added all missing chinese messages
2024-09-25 19:18:59 +09:00
Chenlei Hu
4ab3aa9a39 Backward compatibility with extension injections on legacy menu bar (#970)
* Compatible to legacy top menu extensions

* Rework css

* nit
2024-09-25 16:01:50 +09:00
AustinMroz
9199639320 Reset FileInput value after load (#958)
When a file is browsed for, the fileInput remains associated with the
chosen file even after the associated workflow is loaded. If a user
attempts to load the same file again, the onchange event does not fire
and loading fails silently. This is fixed by clearing the fileInput
after the workflow has been loaded.

Out of an abundance of caution, the onchange is made async to ensure the
fileInput isn't cleared until after the workflow has loaded.

Add a test to check if the same workflow file can be loaded
consecutively.
2024-09-25 16:01:50 +09:00
Chenlei Hu
5ee0fd3519 Rework queue button (#968)
* Move queue button to right side

* Rework split button

* Group

* Remove unused code

* x2 buttons

* Use primevue divider

* adjust style

* Add tooltip

* Update test

* Add clearing pending tasks button to queue bar

* Fix state

* Dropdown list fix
2024-09-25 16:01:50 +09:00
AustinMroz
59976ea357 Reduce SearchBox margins for lower resolutions. (#959)
On lower screen widths, the SearchBox would scale itself down instead of
reducing the fairly wide margins. This wastes space and has reduces the
usability of the search box contents itself by cutting off information
(such as the experimental badge) on nodes with medium or longer titles

This is not without side effects, so further adjustments may be needed.
Currently, the searchbox is slightly offset to the right even for wide
screens and the adjustments are disabled for very small screens (<=768)
such that the preview is offscreen, but the entirety of the searchbox is
properly displayed down to 512
2024-09-25 16:01:50 +09:00
Chenlei Hu
62bddded37 Move clipspace from action bar to topbar dropdown menu (#956) 2024-09-25 16:01:50 +09:00
Alex "mcmonkey" Goodwin
35a7c81fd8 initial download-folder-selector interface (#890)
* initial download-folder-selector interface

* use primevue select

* add a folder select visibility checkbox

* slightly reduce indirection

* fix up select box updating

* revert bad upstream changes

* cleanup

* allow localhost sourced models in ui side

(for testing purposes only basically, but does no harm in deployed envs)

* add screenshot expectations to test

* Update test expectations [skip ci]

* add testing of folder select

* fix test

* don't exclude folder selector when there's only 1

since the checkbox covers that better anyway

* oo - fix checkbox

* Update test expectations [skip ci]

* testing - don't expect screenshots :(

* experimental new test code

* toHaveClass is silly

* add // comments documenting intent of allowedSources

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-25 16:01:50 +09:00
AustinMroz
30469a6d88 Support redo with Ctrl+Shift+Z (#957) 2024-09-25 16:01:50 +09:00
Alex "mcmonkey" Goodwin
d2d645f74b better badges for empty/loading model library folders (#953)
* better badges for empty/loading model library folders

for #945

* fix total count on loaded nodes

* fix test break

* additional test fix

* use a null prop fallback instead of having to explicitly calc

* patch
2024-09-25 16:01:50 +09:00
Chenlei Hu
4e5bcd6a3b Migrate deprecated setting values (#954) 2024-09-25 16:01:50 +09:00
Chenlei Hu
6a8287e192 Show opened workflows as topbar tabs (#952)
* Basic tab switching

* Closing tabs

* Style buttons

* wip

* Fix scroll style

* Add setting

* Add playwright test

* Add unsaved status

* nit
2024-09-25 16:01:50 +09:00
Alex "mcmonkey" Goodwin
7b344d5629 Fix routing (#939)
* reinstate working routing code and remove broken code

* forward object_info

* remove object_info bit
2024-09-25 16:01:50 +09:00
Alex "mcmonkey" Goodwin
35579e644e don't show redundant model previews (#949)
for #944
2024-09-25 16:01:50 +09:00
Chenlei Hu
1bc78a716e Fix action bar commands (#946) 2024-09-25 16:01:50 +09:00
Chenlei Hu
0d28c108d2 Add topbar dropdown menu (#937)
* Add basic menu

* Add workflows/edit to menu bar

* Add command store

* Fix z-index

* Fix beta menu setting switch

* nit

* Drop to center

* Fix command invocation
2024-09-25 16:01:50 +09:00
Alex "mcmonkey" Goodwin
6a158d46b8 [Draft] Model library sidebar tab (#837)
* basic/empty model library sidebar tab

in-progress

* make it actually list out models

* extremely primitive search impl

* list out available folders

(incomplete list atm)

* load list dynamically

* nice lil loading icon

* that's not doing anything

* run autoformatter

* fix up some absolute vue shenanigans

* swap to pi-box

* is_fake_object

* i think apply the tailwind thingo

* trim '.safetensors' from end of display title

* oop

* after load, retain title if no new title is given

* is_load_requested to prevent duplication

* dirty initial model metadata load & preview

based on node preview code

* update model store tests

* initial image icon for model lib

* i hate this

* better empty spacer

* add api handler for '/models'

* load model folders list instead of hardcoding

* add a 'no content' placeholder for empty folders

* autoformat

* autoload model metadata

* error handling on metadata loading

* larger model icons

* click a model to spawn a node for it

* draggable model nodes

* add a setting for whether to autoload or not

* autoformat will be the death of me

* cleanup promise code

* make the model preview actually half-decent

* revert bad unchecked change

* put registration back
2024-09-25 16:01:50 +09:00
pythongosssss
bf7652227a Workflow templates (#938)
* Add template gallery

* Add simple test

* Add examples

* Enable floating menu in test
2024-09-25 16:01:50 +09:00
Chenlei Hu
2aaee5c331 Remove support of Top/Bottom in menu positions (#933)
* Remove support of Top/Bottom in menu positions

* Update menu positions in test setting

* nit
2024-09-25 16:01:50 +09:00
huchenlei
fa2884f9b2 Resolve merge conflict 2024-09-25 16:01:50 +09:00
ArtificialLab
9c7ea5bd87 Fix routing (#929)
* fix router and move graph related parts to GraphView.vue

* (fix) add back child element in UnloadWindowConfirmDialog

* (cleanup) remove empty callback

* (fix) routing issue when base url is not webroot

* add back DEV_SERVER_COMFYUI_URL
2024-09-25 16:01:50 +09:00
Chenlei Hu
5e51ae37cf Relands "Fix routing and layout issue" (#931)
* Revert "Revert "Fix routing and layout issue (#923)" (#930)"

This reverts commit 1e2dfea173.

* Fix merge conflicts
2024-09-25 16:01:50 +09:00
Chenlei Hu
4fa3a38f98 Revert "Fix routing and layout issue (#923)" (#930)
This reverts commit 94db2e90da.
2024-09-25 16:01:50 +09:00
ArtificialLab
d735513e60 Fix routing and layout issue (#923)
* fix router and move graph related parts to GraphView.vue

* (fix) add back child element in UnloadWindowConfirmDialog

* (cleanup) remove empty callback
2024-09-25 16:01:50 +09:00
filtered
38c2ec7532 Fix workflow search cannot find uppercase letters (#908) 2024-09-25 16:01:50 +09:00
Chenlei Hu
f4d4cc3439 Move workflow dropdown to sidebar tab (#893)
* Initial move to sidebar

Remove broken CSS

Move action buttons

Migrate open workflows

Add basic browse

WIP

Add insert support

Remove legacy workflow manager

Remove unused CSS

Reorder

Remove legacy workflow UI

nit

* Support bookmark

Add workflow bookmark store

nit

Add back bookmark functionality

Correctly load bookmarks

nit

Fix many other issues

Fix this binding

style divider

* Extract tree leaf component

* Hide bookmark section when no bookmarks

* nit

* Fix save

* Add workflows searchbox

* Add search support

* Show total opened

* Add basic test

* Add more tests

* Fix redo/undo test

* Temporarily disable browser tab title test
2024-09-25 16:01:50 +09:00
Chenlei Hu
4ae066c57d Proxy ComfyWorkflow objects (#869)
* Proxy ComfyWorkflow objects

* nit
2024-09-25 16:01:50 +09:00
pythongosssss
2d1ff64951 Floating menu option (#726)
* Add floating menu

* Fix

* Updates

* Add auto-queue change test

* Fix
2024-09-25 16:01:50 +09:00
Chenlei Hu
73a7f7dae0 1.2.64 (#972) 2024-09-25 15:56:09 +09:00
Chenlei Hu
cf6367b649 Fix node bypass color (#971)
* Fix node bypass color

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-25 15:54:05 +09:00
bymyself
84fc0e9205 Fix group node naming compatibility (#969)
* Convery legacy group node names in workflow

* Add Playwright test

* Remove hardcoded strings
2024-09-25 15:25:08 +09:00
bymyself
941f71faea Move groupnode test teardown logic to afterEach (#965) 2024-09-25 11:53:52 +09:00
Chenlei Hu
da651eee6f Disable broken playwright test by backend change (#967) 2024-09-25 11:41:11 +09:00
Chenlei Hu
eed00f97f9 Disable jest test on audio_stable_audio_example.flac.json (#966)
* Disable jest test on audio_stable_audio_example.flac.json

* nit

* nit
2024-09-25 11:33:23 +09:00
Chenlei Hu
2387a5e9bd 1.2.63 (#955) 2024-09-24 16:30:45 +09:00
bymyself
6b9c1b70ba Fix group node bookmarking (#950)
* Resolves #926 group node bookmark

* Remove expect outside scope of test

* Update unit tests

* Update group node manager path separators

* Update group node path sepator in fixture
2024-09-24 16:26:02 +09:00
bymyself
b21c0f59f9 Apply node opacity setting to all node colors (#947)
* Apply opacity to node colors. Resolves #928

* Handle default and custom colors all in draw handler

* Add colorUtil unit tests

* Add Playwright test

* Remove comment

* Revert colorPalette.ts changes

* Remove unused imports

* Fix typo

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-24 16:19:53 +09:00
dependabot[bot]
5d8e8a2486 Bump rollup from 4.22.0 to 4.22.4 (#951)
Bumps [rollup](https://github.com/rollup/rollup) from 4.22.0 to 4.22.4.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.22.0...v4.22.4)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-24 14:57:46 +09:00
Alex "mcmonkey" Goodwin
423df92ff8 allow browser tests update off main branch (#940)
why was this blocked??
2024-09-23 19:43:04 +09:00
Chenlei Hu
04a950d7f5 More robust group node playwright test (#935)
* More robust group node playwright test

* nit

* nit
2024-09-23 14:30:45 +09:00
Chenlei Hu
65560604a8 Revert "test to validate subrouting (#927)" (#936)
This reverts commit 6a3dbe08de.
2024-09-23 14:30:17 +09:00
Chenlei Hu
78dea484c9 Fix type for Comfy.Sidebar.Size in apiTypes (#932) 2024-09-23 11:31:18 +09:00
Alex "mcmonkey" Goodwin
6a3dbe08de test to validate subrouting (#927)
* test to validate subrouting

* Update test expectations [skip ci]

* core tests need to prep the page

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-23 09:24:49 +09:00
Chenlei Hu
9aa976fdf0 Show total/free ram in about page (#924) 2024-09-22 17:40:40 +09:00
Chenlei Hu
39eeda8430 1.2.62 (#922) 2024-09-22 16:40:00 +09:00
Chenlei Hu
2878952b1d Makes forceInput node input slot correctly reflect option/required state (#921)
* Correctly style optional force input input slot

* Add force input playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-22 16:35:56 +09:00
pythongosssss
223a1f677b Fix links being lost after manage group node (#916)
* Fix links being lost after manage group node

* Change to use groupnodebuilder

* Make test more reliable
2024-09-22 16:17:39 +09:00
Chenlei Hu
7b4b40db5b Update litegraph (Slot style) (#919)
* Update litegraph (Slot style)

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-22 16:15:39 +09:00
Chenlei Hu
1052603a17 Revert "Any keyboard layout for Ctrl + V, Z, Y... (#763)" (#920)
This reverts commit 23796d9040.
2024-09-22 16:14:59 +09:00
pythongosssss
4ee1b23e9b Exclude litegraph from being cached (#918) 2024-09-22 15:25:52 +09:00
bymyself
326e0748c0 Add node opacity setting (#909)
* Add node opacity setting

* Add colorUtil unit test

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-22 15:18:38 +09:00
ArtificialLab
ea0f74a9f6 Cleanup (#915)
* (update) cleanup:
- move reflect to main.ts
- add config.ts with comfy frontend version
- cleanup index.html and App.vue

* (fix) lint doesn't like branch assignments

* (fix) properly add __COMFYUI_FRONTEND_VERSION__ to ts globals
2024-09-22 10:12:54 +04:00
Chenlei Hu
cdaac0d9bb 1.2.61 (#913) 2024-09-22 12:14:13 +09:00
Chenlei Hu
f749734863 Make optional node input's slot hollow circle (#912)
* Use hollow circle for optional input

* nit

* Show hollow shape for optional input

* Add playwright tests

* Update litegraph

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-22 12:12:48 +09:00
bymyself
a15c4d1612 Fix audio widget serialize option (#910) 2024-09-22 11:54:07 +09:00
bymyself
290bf52fc5 Fix frontend node tooltip error (#911) 2024-09-22 11:52:35 +09:00
dmx
529e889d0e (move) treenode style to style.css 2024-09-22 06:09:56 +04:00
dmx
5a5a69de17 (UI) NodeTree 2024-09-22 06:04:45 +04:00
dmx
194549a4b0 (clean) CSS in App.vue 2024-09-22 06:03:54 +04:00
bymyself
4052fc55f3 Fix node preview styles (#903)
* Use colorPalette to style node previews

* Use widget text secondary color for description

* Remove unused css

* nit

---------

Co-authored-by: huchenlei <chenlei.hu@mail.utoronto.ca>
2024-09-22 09:05:06 +09:00
Chenlei Hu
82d03b5c1b Add colorPalette cleanup for playwright test (#907) 2024-09-22 08:53:01 +09:00
filtered
c7f123766e Add TS types / merge ComfyLGraphNode (#902)
* Add TS type for LGraphNodeConstructor

* Add TS type & move shared prop to parent

* Add TS types - Comfy augmentations

* nit - TS type

* Merge ComfyLGNode into existing augmentations

* nit - fix missed explicit type on import
2024-09-21 18:18:27 +09:00
filtered
88acabb355 Fix TS type on InputSpec (#901) 2024-09-21 14:12:39 +09:00
Chenlei Hu
e5f1eb8609 Update browser tests README (#900) 2024-09-21 10:49:29 +09:00
Chenlei Hu
eb7ab0860d 1.2.60 (#896) 2024-09-20 19:52:09 +09:00
Chenlei Hu
9ed3545b95 Fix frontend node conflicting with node badge (#895) 2024-09-20 19:45:34 +09:00
Chenlei Hu
d223f3865b 1.2.59 (#892) 2024-09-20 09:25:50 +09:00
Chenlei Hu
4538db86cf Add node execution progress to browser title (#891)
* Add node execution progress to browser title

* nit

* nit
2024-09-20 09:22:09 +09:00
dependabot[bot]
3931cae044 Bump vite from 5.3.3 to 5.4.6 (#889)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.3.3 to 5.4.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-19 20:11:34 +09:00
Chenlei Hu
810a63f808 Support async hooks in TreeExplorerNode (#888)
* Support async hooks in TreeExplorerNode

* rebase

* nit

* Fix component test failure

* Add edit vitest

* Add more tests

* Add component test
2024-09-19 20:10:43 +09:00
Chenlei Hu
609984d400 No selection on tree node if selectionKeys prop is not set (#887) 2024-09-19 16:48:56 +09:00
Chenlei Hu
a57c958058 Bind extra context menu items on TreeExplorerNode interface (#886) 2024-09-19 14:51:07 +09:00
Chenlei Hu
b6dbe8f07b Shorten node source package name by remove ComfyUI prefix/suffix (#883)
* Shorten node source package name by remove ComfyUI prefix/suffix

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-19 12:40:05 +09:00
Chenlei Hu
29d69338ef 1.2.58 (#882) 2024-09-19 12:00:45 +09:00
Chenlei Hu
98de010811 Fix node searchbox filter removal (#881) 2024-09-19 11:58:29 +09:00
Chenlei Hu
63302a6634 Fix sorting on type filter + empty query (#880)
* Fix sorting on type filter + empty query

* nit

* nit
2024-09-19 11:22:23 +09:00
Chenlei Hu
8568e037bf Sort search result by node frequency (#879)
* Sort search result by node frequency

* Fix jest test
2024-09-19 10:09:54 +09:00
Chenlei Hu
6c4143ca94 Show node by frequency on empty query (#878) 2024-09-19 09:35:22 +09:00
Chenlei Hu
efa2fa269d 1.2.57 (#868) 2024-09-18 09:38:27 +09:00
Chenlei Hu
a2cf6a7be2 Refactor TreeExplorer (Add handleClick hook) (#867)
* Refactor TreeExplorer (Add handleClick hook)

* nit
2024-09-18 09:36:21 +09:00
bymyself
e493473c35 Add tests on using group nodes in library sidebar (#864)
* Add tests on adding group node from library sidebar

* Improve test name clarity
2024-09-18 09:09:35 +09:00
MaraScott
415a2e7fa5 add square pointer (#848)
* add square pointer

* create enum + refactorize to create init_shape and draw_shape methods
2024-09-17 17:24:07 +09:00
Chenlei Hu
ba9a3b4a9b Move workflows management to pinia (#862) 2024-09-17 17:15:20 +09:00
bymyself
174c52958f Add test on mobile canvas panning (#863)
* Add test on mobile canvas panning

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-17 17:15:05 +09:00
Chenlei Hu
4e41db2d6a [Beta Menu] Shows unsaved state on browser tab title (#860)
* [Beta Menu] Shows unsaved state on browser tab title

* Proper state management

* Add playwright test

* Fix browser tests
2024-09-17 16:14:06 +09:00
bymyself
e8daebdc0c Add group nodes to search and node library (#861)
* Register group nodes in nodeDefStore

* Add playwright tests

* Update test expectations [skip ci]

* Mock nodeDefStore in group node unit test

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-17 16:06:58 +09:00
Chenlei Hu
582acd7bd1 1.2.56 (#859) 2024-09-17 11:17:26 +09:00
Chenlei Hu
48fe14e263 [Beta Menu] Show active workflow name on browser tab title (#857) 2024-09-17 11:11:52 +09:00
bymyself
f9fd0f59ff Add nullcheck to snap-to-grid setting (#858) 2024-09-17 11:11:39 +09:00
Chenlei Hu
3fe4b4b856 Add generation progress to browser tab title (#855) 2024-09-17 10:31:29 +09:00
bymyself
c510b344af Allow zero as input slider min/max (#854) 2024-09-17 09:43:32 +09:00
Chenlei Hu
980dd285ad Revert move floating menu to Vue (#853) 2024-09-17 09:33:25 +09:00
Chenlei Hu
2b60244e4a Move setting declarations from ui to coreSettings (#847)
* Move setting declarations from ui to coreSettings

* nit

* nit

* Move effect to vue component
2024-09-16 17:47:47 +09:00
Chenlei Hu
45a866f194 Fix ComfyUI class setup procedure (#846) 2024-09-16 17:14:11 +09:00
Chenlei Hu
091b8a74fb Mock settingStore (#845) 2024-09-16 16:25:32 +09:00
Chenlei Hu
74fa4a2c2d Move inlined settings in settingsStore to a separate file (#844) 2024-09-16 14:46:06 +09:00
Chenlei Hu
327b67a022 Move floating menu to a Vue component (#843)
* Move floating menu to a Vue component

* nit

* Fix jest tests
2024-09-16 14:26:46 +09:00
Chenlei Hu
d0a4db5f4f Update litegraph (Copy connection by shift drag from path) (#841)
* Add playwright tests

* Update lg

* nit

* nit

* Skip tests

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-16 12:02:48 +09:00
Chenlei Hu
861eaa155f Refactor test fixture dnd (#840)
* Refactor test fixture dnd

* nit
2024-09-16 10:13:25 +09:00
Chenlei Hu
3550e7f7f1 Show bookmark icon on booked mark nodes in node search box (#839) 2024-09-15 17:31:44 +09:00
Chenlei Hu
7d25d976d1 Extract search option as a Vue component (#838) 2024-09-15 17:15:19 +09:00
Chenlei Hu
7025e321de 1.2.55 (#836) 2024-09-15 11:22:27 +09:00
huchenlei
429fa75fcc Update litegraph (Fix group right click) 2024-09-15 11:03:34 +09:00
Chenlei Hu
347563adf9 Move json format functions to formatUtil (#834) 2024-09-15 09:46:34 +09:00
Chenlei Hu
9bdb3c0332 1.2.54 (#830) 2024-09-14 17:12:50 +09:00
Chenlei Hu
12c699cc87 Update litegraph (Getters) (#829)
* Update litegraph (Getters)

* Update
2024-09-14 17:10:57 +09:00
Chenlei Hu
588cfeca4b Replace ComfyApp.runningNodeId with executionStore.executingNodeId (#828)
* Replace ComfyApp.runningNodeId with executionStore.executingNodeId

* nit
2024-09-14 16:01:37 +09:00
Chenlei Hu
f983f42c45 Add executionStore (#827)
* Extract execution store

* Fix executing nodes highlight

* nit

* nit

* nit
2024-09-14 15:15:15 +09:00
Chenlei Hu
fef780a72f Make useTreeExpansion hook accept expandedKeys as param (#826) 2024-09-14 11:27:38 +09:00
Chenlei Hu
ebdcd92977 Extract error handling with toast message as hook (#825) 2024-09-14 11:25:08 +09:00
filtered
c98ea5ba01 Use LiteGraph validation for node search->create (#822)
Adds LiteGraph type to augmentation until LG types are auto-generated
Removes @ts-expect-error
2024-09-14 08:36:48 +09:00
filtered
48f84a46cd Add apiTypes present in docs but missing in zod (#821)
* Add apiTypes present in docs but missing in zod

* Fix prettier check
2024-09-14 08:35:32 +09:00
filtered
9483cfe915 Add graceful correction when widgets undef. (#820)
Fixes crash on load of workflow where `node.widgets` has no `.find()`
2024-09-14 08:33:43 +09:00
Chenlei Hu
862e2c2607 1.2.53 (#818) 2024-09-13 20:59:16 +09:00
Chenlei Hu
a08ec196c7 Fix frontend-only node freezing litegraph (#817) 2024-09-13 20:58:19 +09:00
Chenlei Hu
17db1e6074 Rework userFileStore (#815)
* Rework userFileStore

* nit

* Add back unittests
2024-09-13 17:40:08 +09:00
Chenlei Hu
65a8dbb7e0 1.2.52 (#814) 2024-09-13 16:25:01 +09:00
Chenlei Hu
efd8b5c19d Add playwright test for custom color palette (#812)
* Add playwright test for custom color palette

* nit

* Fix leaked side effect

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-13 13:52:16 +09:00
Chenlei Hu
0a188aaf72 Disable zoom when editing titles (#813) 2024-09-13 11:42:24 +09:00
Chenlei Hu
eb45cca031 Pin searchbox at top when node library scrolls (#811)
* minor style fix

* nit

* Pin searchbox at top when node library scrolls
2024-09-13 10:50:06 +09:00
Chenlei Hu
d8d6fa86e4 Add button to clear pending tasks (#810) 2024-09-13 10:23:28 +09:00
Chenlei Hu
880ac4fa5a Add node lifecycle badge text (#809) 2024-09-13 10:04:36 +09:00
Chenlei Hu
7d3b8dc44c Make \n correctly displayed on error message (#805) 2024-09-13 09:04:21 +09:00
Chenlei Hu
1230d92b37 1.2.51 (#804) 2024-09-12 20:23:04 +09:00
Chenlei Hu
8889c4de4a Fix node def registeration (#803) 2024-09-12 20:17:21 +09:00
Chenlei Hu
637f5b501e Add about panel in settings dialog (#799)
* basic about page

* Remove frontend version from setting dialog header

* Style about page

* basic system stats

* Basic styling

* Reword

* Format memory amount
2024-09-12 17:31:19 +09:00
Chenlei Hu
d2b3e325a4 1.2.50 (#798) 2024-09-12 17:16:54 +09:00
Chenlei Hu
c99ca004b4 Fix badge position on collapsed nodes (#797)
* Update litegraph (Proper collapsed node handling)

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-12 17:15:20 +09:00
Chenlei Hu
fa9a415c62 Fix litegraph crash on using custom colorPalette (#795) 2024-09-12 16:56:17 +09:00
Chenlei Hu
da3271fe57 Move InputSlider to common/ (#794) 2024-09-12 15:19:36 +09:00
Chenlei Hu
358c0ce83c Update litegraph (Pin/Unpin selected nodes) (#791)
* Update litegraph (Pin/Unpin selected nodes)

* Checkout head_ref first

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-12 15:04:43 +09:00
Chenlei Hu
110c007912 Update litegraph (Proper ContextMenu export) (#790) 2024-09-12 14:09:00 +09:00
Chenlei Hu
fdb01c06f2 Split playwright tests to multiple runners by project (#789) 2024-09-12 11:35:36 +09:00
Chenlei Hu
ca6bf7d054 Split jest unit test and playwright test into different actions (#787)
* Split jest unit test and playwright test into different actions

* Use composite action

* Add tag v1
2024-09-12 10:54:50 +09:00
Chenlei Hu
14f5019556 1.2.49 (#788) 2024-09-12 10:16:59 +09:00
Chenlei Hu
80ca1808f0 Node source/id badge (#781)
* Add basic node badge

* Node source badge

* Prevent manager badge rendering

* Update litegraph (Badge support)

* Add playwright tests

* Separate nodes

* nit

* Checkout devtools repo for browser test expectation CI

* Fix failing unittests

* Rename setting

* Hide all badges in playwright tests

* Handle group node

* Update test expectations [skip ci]

* Fix unittest

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-12 09:36:06 +09:00
Chenlei Hu
f2a30ec197 Fix missing model dialog test (#782) 2024-09-11 21:00:32 +09:00
Chenlei Hu
b8bdba0bcc Fix tailwindcss in NoResultsPlaceholder.vue (#780) 2024-09-11 17:38:42 +09:00
Chenlei Hu
baf0bc8de4 Run all playwright tests under dpr=2 (#779)
* Run all playwright tests under dpr=2

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-11 11:33:40 +09:00
Chenlei Hu
8ce7b515a3 Fix ComfyExtension types (#778) 2024-09-11 10:43:01 +09:00
Chenlei Hu
06a05cb283 Fix loading large workflow embedded in webp (#777)
* Fix loading large workflow embedded in webp

* Update test expectations [skip ci]

* nit

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-11 10:12:01 +09:00
bymyself
15758101aa Fix searchbox popover on touch devices (#773)
* Add delay on touch pointer event when opening searchbox

* Add playwright mobile test
2024-09-11 08:55:04 +09:00
ArtificialLab
05b3ad2f59 Front stack primary updates and improvements (#757)
* (fix) index.html formating for prettier

* (add) proper icon management
- on-demand icons auto importing
- handle all available icon sets (https://icones.js.org)

* (fix) proper css management

* (add) front stack improvement:
- implement vue router
- prepare for App.vue simplification
- proper management of views and layouts
- fix Tailwind CSS and prepare for overall css cleaning

* (fix) move back user.css to public dir

* (fix) remove user.css import from main.ts
2024-09-11 08:53:54 +09:00
bymyself
90abf9744c Document registerCustomNodes (#772) 2024-09-10 12:43:39 +09:00
filtered
0e01bb3c07 Fix reroute to wildcard & multi-typed slots (#769)
Use the same type check the rest of the connection process uses.
2024-09-09 17:53:47 +09:00
Chenlei Hu
8b77dde55a [skip ci] Update litegraph dev guide (#770) 2024-09-09 17:51:44 +09:00
Chenlei Hu
a41de30dc5 Fix tailwind css setup (#768)
* Fix tailwind css setup

* Install as dev dep

* Uninstall primeui tailwind
2024-09-09 16:56:32 +09:00
Chenlei Hu
534ea17816 1.2.48 (#767) 2024-09-09 16:20:01 +09:00
Chenlei Hu
913582c7cd [Chore] Update primevue (#766) 2024-09-09 16:14:20 +09:00
Chenlei Hu
3779878b57 Update litegraph (Bug fixes) (#765) 2024-09-09 16:11:28 +09:00
Chenlei Hu
023299cf1a Fix loading workflow from of edited webp (#764)
* Fix loading workflow from of edited webp

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-09 16:07:15 +09:00
Chenlei Hu
23796d9040 Any keyboard layout for Ctrl + V, Z, Y... (#763)
* Any keyboard layout for Ctrl + V, Z, Y...

`if` conditioning changed for better support any keyboard layout for shortcuts

* Format

---------

Co-authored-by: Khachatur Avanesian <jailbreakvideo@gmail.com>
2024-09-09 10:17:14 +09:00
bymyself
21c3883cc7 Improve beta menu nav accessibility (#762)
* Add ARIA labels to beta menu btns without text

* Adjust test locator
2024-09-09 09:49:43 +09:00
bymyself
616e295262 Improve searchbox accessibility (#760)
* Set field key for search result options label

* Add playwright test

* Add role attr to search dialog
2024-09-09 09:49:02 +09:00
bymyself
c201e86b97 Improve sidebar accessibility (#759)
* Add ARIA label to sidebar buttons

* Add component test

* Add generalized component tests
2024-09-09 09:48:29 +09:00
bymyself
61ee43aa6f [skip ci] Fix search box props type (#753) 2024-09-07 02:34:47 -04:00
bymyself
08a1fd0056 Dismiss gallery lightbox on background click (#752)
* Dismiss gallery on background click

* Add vitest tests
2024-09-07 02:32:56 -04:00
Chenlei Hu
56f3842045 1.2.47 (#747) 2024-09-05 11:41:00 -04:00
Chenlei Hu
81bc0fd9cb Release script (#746)
* Update main repo release script

* update readme [skip ci]
2024-09-05 11:40:05 -04:00
Chenlei Hu
38c957d3a9 Fix load of string node id workflow (#744)
* Update litegraph

* Fix string node id

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-05 11:16:41 -04:00
Chenlei Hu
9d855d637e Ignore missing dialog (#743)
* Do not report missing nodes/models warning repeatedly

* Add playwright tests

* cast finalOptions, add comments to interface

* Use old menu in tests to not break top left click methods

* Assert no dialog on undo and on redo separately

* nit

* nit

---------

Co-authored-by: christian-byrne <abolkonsky.rem@gmail.com>
2024-09-05 11:00:41 -04:00
bymyself
743683c01d Add logging setting display name (#742) 2024-09-05 10:39:22 -04:00
Chenlei Hu
720e7e112d 1.2.46 (#738) 2024-09-04 20:29:14 -04:00
Chenlei Hu
ce157afeac Disable minify on release dist (#737) 2024-09-04 20:28:38 -04:00
Chenlei Hu
95701ab761 Add keyboard shortcut for pin/unpin node (#736)
* Add keyboard shortcut for pin/unpin node

* Add playwright test

* Add nextFrame calls

* Keyboard event on canvas

* disable test
2024-09-04 20:26:10 -04:00
Alex "mcmonkey" Goodwin
060e61f0db initial model store (#674)
* initial model store

* refactor the 'modelstoreserviceimpl' to pinia

* pepper in some reactive

the inner ModelStore (per-folder) can't be pinia because its made of temporary instances, but it can be reactive

* use refs in metadata class

* remove 'reactive'

* remove ref too

* add simple unit tests for modelStore

* make things worse via autoformatting

* move mock impls to a function
2024-09-04 19:59:40 -04:00
Chenlei Hu
6c7fb5041d Replace locking with pining in core (#734)
* Replace locking with pin in core

* Add extra expectation

* Update litegraph

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-04 19:59:06 -04:00
Chenlei Hu
25a3c30fef [skip ci] Update README (#733) 2024-09-04 10:31:12 -04:00
Chenlei Hu
287bd7ddd0 Add test on text widget popover dismiss (#732)
* Add test on text widget popover dismiss (#719)

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: bymyself <abolkonsky.rem@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
2024-09-04 10:31:01 -04:00
Chenlei Hu
b396d1a9fe Trigger searchbox on group body db click (#731) 2024-09-04 10:16:12 -04:00
Chenlei Hu
ada8500d21 1.2.45 (#727) 2024-09-03 20:17:34 -04:00
Chenlei Hu
0f32ab334a Update litegraph (Group highlight option) (#725) 2024-09-03 15:34:01 -04:00
Chenlei Hu
36cdebcad1 Restore context menu for new searchbox (#724)
* Searchbox revamp

* nit

* nit

* Add playwright test

* Update litegraph

* Rename setting

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-03 13:28:26 -04:00
Chenlei Hu
974a7ef63f Store shallowRef of litegraph canvas (#722)
* Store shallowRef of litegraph canvas

* nit
2024-09-03 10:11:31 -04:00
bymyself
b49b19c9b0 [skip ci] Add ToastMessageOptions docs to README (#721) 2024-09-03 08:10:23 -04:00
bymyself
a5d93f6910 Update Settings type with new fields (#718) 2024-09-02 22:50:56 -04:00
Chenlei Hu
8a99124470 1.2.44 (#716) 2024-09-02 20:31:01 -04:00
Chenlei Hu
4a230f720e Edit group name on group creation (With Ctrl + g) (#715)
* Editor store

* Merge editors

* nit

* Edit on group creation

* nit
2024-09-02 20:20:40 -04:00
Chenlei Hu
4ad1e67ebf Double click group title to edit (#714)
* Double click group title to edit

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-02 18:01:02 -04:00
Chenlei Hu
80e4384644 Eager init nodeSearchService (#713) 2024-09-02 15:40:10 -04:00
Chenlei Hu
51b7467012 Auto transforms bugged node pos (#712) 2024-09-02 14:41:45 -04:00
Chenlei Hu
9d69db6db7 Update litegraph (Fix node.pos serialization) (#711) 2024-09-02 14:34:58 -04:00
Chenlei Hu
e733b87f22 Add beforeRegisterVueAppNodeDefs hook (#709)
* Add beforeRegisterVueAppNodeDefs hook

* Remove min-char constraint on node library search
2024-09-02 11:06:16 -04:00
Chenlei Hu
adcef7d2f4 Add locale setting (#708) 2024-09-02 10:01:00 -04:00
mijuku233
8ba5da14bc Custom background image (#698) 2024-09-01 19:46:55 -04:00
Chenlei Hu
c181bf1f55 1.2.43 (#705) 2024-09-01 19:01:16 -04:00
Chenlei Hu
d9a7537169 Fix top level bookmark migration (#706) 2024-09-01 18:55:56 -04:00
Chenlei Hu
75e91137f0 Display node display_name instead of name (#704) 2024-09-01 18:33:19 -04:00
Chenlei Hu
a4a298924e Show node id name in node searchbox (#703)
* Show node unique name in node searchbox

* nit
2024-09-01 18:12:27 -04:00
Chenlei Hu
14da8433f7 Migrate node library sidebar to use unique name instead of display name (#702)
* Migrate node library sidebar to use unique name instead of display name

* Break word
2024-09-01 17:52:53 -04:00
Chenlei Hu
ff2d160230 Reduce divider margin in node library (#701) 2024-09-01 14:49:31 -04:00
Chenlei Hu
b0b5f92940 Add setting to control tree explorer item padding (#700) 2024-09-01 14:36:15 -04:00
Chenlei Hu
d04dbcd2c1 [Major Refactor] Use TreeExplorer on nodeLibrarySidebarTab (#699)
* Basic move

* Add back node bookmark

* Move node preview

* Fix drag node to canvas

* Restore click node to add to canvas

* Split bookmark tree and library tree

* Migrate rename and delete context menu

* Fix expanded keys

* Split components

* Support extra menu items

* Context menu only for folder

* Migrate add folder

* Handle drop

* Store color customization

* remove extra padding

* Do not show context menu if no item

* Hide divider if no bookmark

* Sort bookmarks alphabetically default

* nit

* proper edit

* Update test selectors

* Auto expand on item drop

* nit

* Fix tests

* Search also searches bookmarks tree

* Add serach playwright test
2024-09-01 14:03:15 -04:00
huchenlei
5383f97eba Add tree explorer tree node test 2024-08-31 21:10:32 -04:00
huchenlei
bc7da487e8 Add drop handler 2024-08-31 21:10:32 -04:00
huchenlei
86e7c12e27 Add draggable/droppable flags 2024-08-31 21:10:32 -04:00
huchenlei
50f1ca8eaf Add extra interfaces 2024-08-31 21:10:32 -04:00
huchenlei
280b43fd58 Merge folder and node impl 2024-08-31 21:10:32 -04:00
huchenlei
488f0d82b4 More refactor 2024-08-31 21:10:32 -04:00
huchenlei
bc3ec65967 Move 2024-08-31 21:10:32 -04:00
huchenlei
61342edba0 1.2.42 2024-08-31 09:56:05 -04:00
huchenlei
9247aec03a nit 2024-08-31 09:54:36 -04:00
huchenlei
0e88308571 Remove github button in error dialog 2024-08-31 09:54:36 -04:00
huchenlei
380cbdd5fc 1.2.41 2024-08-30 21:27:06 -04:00
huchenlei
68d6b1f172 Add confirm dialog on window close 2024-08-30 16:51:10 -04:00
Chenlei Hu
70d5e98c73 Update github action run conditions (#682) 2024-08-30 15:32:26 -04:00
Chenlei Hu
9009e784f9 Add component test (Vitest) (#681)
* Add component test (Vitest)

* Fix compile error
2024-08-30 15:32:26 -04:00
Chenlei Hu
877e500510 Update litegraph (ES6 LGraphCanvas) (#679) 2024-08-30 15:32:26 -04:00
Chenlei Hu
aee2afee36 1.2.40 (#685) 2024-08-29 21:30:58 -04:00
Chenlei Hu
f42609c966 Add support for extra system stats in error report (#684)
* Add support for extra system stats in error report

* Add toast on error
2024-08-29 21:29:33 -04:00
pythongosssss
aaea05a37b Sync pr fix clip path when using new menu (#184)
* Sync pr fix clip path when using new menu

* Enable test

* Update outdated test image

* Update test image
2024-08-29 18:02:10 -04:00
pythongosssss
d0067719b8 Fix primitive resize (#683)
* Fix primitive resize on load
Fixes #676

* Add test

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: huchenlei <chenlei.hu@mail.utoronto.ca>
Co-authored-by: github-actions <github-actions@github.com>
2024-08-29 18:01:57 -04:00
Chenlei Hu
e59ed85cc0 1.2.39 (#675) 2024-08-28 20:22:18 -04:00
Alex "mcmonkey" Goodwin
d5b4311e24 run formatter and add .js to gitattributes (#673)
* run formatter and add .js to gitattributes

* remove npm format change
2024-08-28 17:21:07 -04:00
Alex "mcmonkey" Goodwin
6fed739402 add a git attributes file (#672)
set all ts and vue scripts as LF on Windows as there are scripts (eg the prettier format script) that will break with windows format
2024-08-28 17:03:38 -04:00
Chenlei Hu
b25c5259bd Fix node title editor's width when node is collapsed (#671)
* Fix node title editor's width when node is collapsed

* nit
2024-08-28 16:35:01 -04:00
Chenlei Hu
fb5bb57b0d Mark show missing models warning as experimental (#670)
* Mark show missing models warning as experimental

* nit
2024-08-28 16:22:40 -04:00
Chenlei Hu
986827cb91 Update litegraph (ES6 LGraphNode) (#669) 2024-08-28 15:45:45 -04:00
Chenlei Hu
6f9d2bfa17 Relands "Add support for LiteGraph to convert to classes" (#667)
* Revert "Revert "Add support for LiteGraph to convert to classes (#334)" (#386)"

This reverts commit d607f6c7f7.

* Format
2024-08-28 15:35:05 -04:00
Chenlei Hu
4e8acf6c77 Add setting to disable double click node title edit (#668) 2024-08-28 13:58:33 -04:00
Chenlei Hu
493805d0ee Update README (#664) 2024-08-28 12:00:13 -04:00
Chenlei Hu
2c174b5956 Group selected nodes by Ctrl + g (#663)
* Ctrl + g to group selected noes

* Add playwright test

* nit

* Move button loc

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-08-28 11:49:41 -04:00
Chenlei Hu
5c2cb00cd6 Replace autocomplete with select as node filter option input (#659)
* Replace autocomplete with select as node filter option input

* i18n
2024-08-27 21:45:44 -04:00
pythongosssss
968f417061 Node library search filters (#636)
* Add search filters to node library

* Fix

* Dont close on add

* Fix wildcard

---------

Co-authored-by: Chenlei Hu <chenlei.hu@mail.utoronto.ca>
2024-08-27 21:17:34 -04:00
Chenlei Hu
fef9395a2c Add format check CI task (#658) 2024-08-27 20:32:33 -04:00
Chenlei Hu
698754b835 1.2.38 (#657) 2024-08-27 18:23:10 -04:00
filtered
29d21348ca Fix image grid render consistency (#654)
- Makes image grids of non-uniform-size images fill the space the same as uniform image batches
- Re-uses existing code
- Removes workaround
- "Supports image sizes up to 1 terapixel"
2024-08-27 18:22:14 -04:00
Chenlei Hu
09d8f2a502 Missing model download dialog test (#656)
* Test prep

* Add missing model dialog test

* Basic test of download model

* Add comment

* Adjust setting in test

* Change download dir to not interfere with other tests
2024-08-27 17:34:32 -04:00
Chenlei Hu
50b418113c Double click node title to trigger edit (#655)
* Update litegraph

* Double click edit node title

* Update

* Auto select all

* Update litegraph

* Add playwright test

* Update readme

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-08-27 11:38:39 -04:00
Chenlei Hu
6ab92f28db Fix dialog maximization (#652) 2024-08-27 09:58:09 -04:00
Alex "mcmonkey" Goodwin
b19cbd9111 Model Downloader Improvements (#650)
* use new download_path value in download api

from https://github.com/comfyanonymous/ComfyUI/pull/4621

* add in-UI refusal for ckpt files
2024-08-27 08:56:57 -04:00
pythongosssss
9cdefca481 Enhancements to the queue image feed (#646)
* Enhancements to the queue image feed
 - Change flat list icon
 - Add cover/contain mode
 - Add right click -> go to node
 - Add go to node link on detail

* Add loading spinner

* resolve comments

---------

Co-authored-by: huchenlei <chenlei.hu@mail.utoronto.ca>
2024-08-26 21:57:23 -04:00
Chenlei Hu
84662ada9e 1.2.37 (#649) 2024-08-26 21:36:40 -04:00
Chenlei Hu
26b02979a1 1.2.36 (#648) 2024-08-26 21:31:38 -04:00
Chenlei Hu
0795c3041c Bookmark folder icon customization (#647)
* Add bookmark customization support

* WIP

* Fix bugs

* Fix color update

* Handle rename and delete of customization

* nit

* Add custom color picker

* Computed final color

* i18n

* Remove cancel button as dialog already has it

* Add playwright test
2024-08-26 21:30:38 -04:00
pythongosssss
c604209f40 Fix crash connecting to node with no slots (#644)
* Fix crash connecting to node with no slots

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: huchenlei <chenlei.hu@mail.utoronto.ca>
Co-authored-by: github-actions <github-actions@github.com>
2024-08-26 18:29:52 -04:00
Chenlei Hu
96d129e8a6 Fix edit attention test (#640) 2024-08-26 11:31:46 -04:00
Chenlei Hu
a69858c87a Fix unbookmark node in node library (#639)
* Add unbookmark test

* Fix unbookmark
2024-08-26 11:04:08 -04:00
Chenlei Hu
03ac6eea19 Add playwright undo tests (#638) 2024-08-26 10:47:37 -04:00
filtered
f9ae5aaa0f Fix textarea undo history cleared by editAttention (#635) 2024-08-26 10:34:17 -04:00
Chenlei Hu
a5cdebe1a8 Adjust toast location to avoid overlap with side toolbar and menu (#637) 2024-08-26 10:32:34 -04:00
Chenlei Hu
298a4744d8 1.2.35 (#632) 2024-08-25 22:04:24 -04:00
Chenlei Hu
090fda2f22 Node library custom bookmark folder (#631)
* Add new folder button

* Add tree util test

* nit

* Support empty folder in node library

* Drag to bookmark folder

* Use bookmark icon for bookmark folder

* Highlight on dragover

* nit

* Auto-expand on item added

* Extract bookmark system as store

* Add context menu on bookmark folder

* Add editable text

* Fix reactivity

* Plumb editable text

* refactor

* Rename node

* Fix focus

* Prevent name collision

* nit

* Add new folder

* nested folder support

* Change drag behavior

* Add basic playwright tests

* nit

* Target tree-node-content instead of tree-node
2024-08-25 21:53:58 -04:00
Chenlei Hu
f36c934d37 Hide hidden settings (#630)
* Hide hidden settings

* nit
2024-08-25 15:45:05 -04:00
Chenlei Hu
87ef469d1c Update litegraph (popover dismiss) (#629) 2024-08-25 15:08:02 -04:00
Chenlei Hu
a1e40b14c7 Add textarea widget spell check setting (#627) 2024-08-25 10:22:36 -04:00
Chenlei Hu
b863c11e67 Fix node library bookmark preview (#626) 2024-08-25 10:01:42 -04:00
Chenlei Hu
7a7188aeb0 1.2.34 (#623) 2024-08-24 19:43:53 -04:00
Veikka
2079a54ffa multi-level node search ranking (#622) 2024-08-24 19:42:56 -04:00
Chenlei Hu
74baf2af12 Rename onNonLeafClick to toggleNodeOnEvent (#621) 2024-08-24 18:09:55 -04:00
Chenlei Hu
5dec86861b Replace ToggleButton with Button to be consistent in style (#620) 2024-08-24 17:54:03 -04:00
Chenlei Hu
fc05029f4e Cleanup dragSelector prop (#619) 2024-08-24 17:48:36 -04:00
Chenlei Hu
98064f301d Extract tree expand/collapse logic as hook (#618) 2024-08-24 17:43:26 -04:00
Chenlei Hu
bff1dc91fa Refactor node library drag and drop (#617) 2024-08-24 17:40:56 -04:00
bymyself
f4242f8a66 Explicitly invoke setting.options when it's a function (#616) 2024-08-24 16:22:08 -04:00
Chenlei Hu
845ab88d55 Update string input spec to include extra multiline args (#615) 2024-08-24 15:21:58 -04:00
Chenlei Hu
6c557eaa58 Fix unrecognized bookmark node crash the node library sidebar (#614)
* Add playwright test

* nit
2024-08-24 11:58:14 -04:00
Chenlei Hu
2fdaabd2c9 Ctrl + Click recursively expand/collapse node library folder (#613) 2024-08-24 11:01:10 -04:00
Chenlei Hu
a2143d9120 Bookmark nodes in node library (#612)
* Basic bookmark

* Extract node leaf as component

* bigger hitbox
2024-08-24 10:39:18 -04:00
Chenlei Hu
f2b02dd10b Add userFileStore (#611)
* WIP

* Refactor

* Add userFileStore test
2024-08-23 21:39:22 -04:00
Chenlei Hu
b831a82360 Target ES2022 in vite config to align with tsconfig target (#610) 2024-08-23 21:11:45 -04:00
Chenlei Hu
d232e38c33 1.2.33 (#609) 2024-08-23 18:53:48 -04:00
Chenlei Hu
1a3cf4c3f3 Show node name and node id on flattened task outputs (#608)
* wip

* Show node name and node id
2024-08-23 18:52:45 -04:00
Chenlei Hu
31d172d4d9 Fix hotkeys triggering while editing properties panel values (#606) (#607)
* Fix hotkeys triggering while editing properties panel values (#606)

* Add properties panel inputs to key handler ignore

* Add properties panel test

* Update test expectations [skip ci]

---------

Co-authored-by: bymyself <abolkonsky.rem@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
2024-08-23 16:46:40 -04:00
Chenlei Hu
92ce064ebf Fix github request too long issue (#605)
* Fix github request too long issue

* Remove life on error message
2024-08-23 13:52:53 -04:00
bymyself
3d041dd742 Fix settings dialog on mobile (#602) 2024-08-23 09:43:45 -04:00
Alex "mcmonkey" Goodwin
af378262f4 Model downloader dialog (#569)
* API core for model downloader

* initial basic dialog for missing models

* app.ts handling for missing models

* don't explode if getModels is a 404

* actually track downloads in progress

* overall pile of improvements to the missing models view

* minor fixes

* add setting to disable missing models warning

* temporarily remove 'models' entry from default graph

to avoid missing model dialog causing issues. Also because ckpt autodownloading shouldn't be allowed

* swap the url to a title

* add model directory to display

* match settingStore commit

* check setting before scanning models list

ie avoid redundant calcs when setting is disabled anyway
2024-08-23 09:43:20 -04:00
Chenlei Hu
57c5a78af3 Fix signature of listUserData (#601) 2024-08-22 20:26:23 -04:00
Chenlei Hu
233fd1347e Move error handling out of api.ts to workflows.ts (#600) 2024-08-22 20:21:31 -04:00
Chenlei Hu
60221254d9 1.2.32 (#599) 2024-08-22 19:48:04 -04:00
Chenlei Hu
7434691bed Disable flaky test (#598) 2024-08-22 19:42:38 -04:00
Chenlei Hu
fbdc9d430b Improve node search matching algorithm (#597) 2024-08-22 19:40:58 -04:00
Chenlei Hu
3e457f812d Execution Error Dialog Revamp (One click issue searching and filing) (#595)
* Add basic error dialog

* 2 level error report

* Add find issue button

* nit

* Add file issue button

* Single dialog

* nit

* Fix long text wrapping

* Merge component

* Test execution error dialog
2024-08-22 15:55:38 -04:00
bymyself
0466c79725 Fix right-click save image in gallery (firefox) (#588) 2024-08-21 21:28:12 -04:00
Chenlei Hu
8b989c6415 Add a wait to prevent searchbox popup (#589)
* Add a wait to prevent searchbox popup

* nit
2024-08-21 21:27:54 -04:00
Chenlei Hu
2e51122778 Add browser tests for litegraph changes (#580) (#585) (#587)
* Add browser tests for litegraph changes (#580) (#585)

* Zoom speed tests

* Merge

* Prompt dialog test

* Update test expectations [skip ci]

---------

Co-authored-by: bymyself <abolkonsky.rem@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
2024-08-21 20:39:30 -04:00
Chenlei Hu
8f8eac038a 1.2.31 (#586) 2024-08-21 19:03:40 -04:00
Tobias Menzi
4b3bad4bc5 Change broken relative font paths (#584) 2024-08-21 19:01:02 -04:00
Chenlei Hu
17c7f57d8f Update litegraph (Zoom speed adjustment) (#580) 2024-08-21 17:16:06 -04:00
Chenlei Hu
5542845710 Task output folder view (#579)
* Change to button

* Folder view
2024-08-21 16:47:30 -04:00
Chenlei Hu
f2de9b0d3c Revert "Add custom sort function to improve search matching (#574)" (#578)
This reverts commit 77ba201367.
2024-08-21 14:25:20 -04:00
Chenlei Hu
71fa71e82c Prefer saved media over preview media as task cover media (#576) 2024-08-21 14:19:27 -04:00
Chenlei Hu
77ba201367 Add custom sort function to improve search matching (#574) 2024-08-21 13:54:29 -04:00
Chenlei Hu
6edfc9bc1b Use settingStore to access setting (#572) 2024-08-21 11:42:12 -04:00
Chenlei Hu
743dc4879a Add setting to disable missing nodes dialog (#571)
* Add setting to disable missing nodes dialog

* nit

* nit
2024-08-21 10:31:07 -04:00
Alex "mcmonkey" Goodwin
2c1bd662e1 minor typo fix (#567) 2024-08-21 09:51:15 -04:00
Björn Söderqvist
7051d86ba7 Fix incorrect type paths (#568) 2024-08-21 09:41:06 -04:00
Chenlei Hu
7487c565c8 1.2.30 (#566) 2024-08-20 17:48:31 -04:00
Chenlei Hu
a86d10b02d Fix node library searchbox background color (#565) 2024-08-20 17:41:59 -04:00
Chenlei Hu
3d89c245e5 Add experimental/deprecated tags to search result / node library (#564) 2024-08-20 17:35:23 -04:00
Chenlei Hu
9dd6da3dc2 Support node deprecated/experimental flag (#563)
* Add deprecated field

* Hide deprecated nodes

* Add experimental node show/hide

* Add setting tooltips

* nit

* nit

* nit
2024-08-20 17:00:47 -04:00
Chenlei Hu
269e468425 i18n node searchbox placeholder (#561) 2024-08-20 15:20:00 -04:00
Chenlei Hu
c3ef716d53 Reduce search highlight padding (#560) 2024-08-20 15:12:30 -04:00
Chenlei Hu
bd7bbd9e95 Reduce debounce delay in node searchbox (#559) 2024-08-20 15:10:06 -04:00
ruucm
447d1c95ef enable cmd shortcuts for mac (mute & bypass) (#557) 2024-08-20 11:01:17 -04:00
Chenlei Hu
c4bc0e8430 Auto expand tree on search in node library tab (#558)
* Add custom nodelib searchbox

* Auto expand on search

* Support alphabetical sort in filtered tree
2024-08-20 11:01:05 -04:00
Chenlei Hu
f3ab9cfb8e Fix multiuser selection screen (#554) 2024-08-19 22:59:14 -04:00
Chenlei Hu
52c8c8194e Add browser_tests README (#553) 2024-08-19 22:45:06 -04:00
Chenlei Hu
9dbc114ae9 Remove win32 expectations (#551) 2024-08-19 21:59:05 -04:00
Chenlei Hu
556edea299 1.2.29 (#550) 2024-08-19 21:49:58 -04:00
Chenlei Hu
d5584a1d39 Fix node source chip alignment (#549) 2024-08-19 21:45:49 -04:00
Chenlei Hu
628f2afc34 Remove double reroute pin (#548) 2024-08-19 21:38:41 -04:00
Alex "mcmonkey" Goodwin
ea01fde607 [Experimental] hide/show logic improvement (#475)
* experimental hide/show logic improvement

for #470

* minor early out fix

not sure this is strictly needed (doesn't seem to be from a test, seems draw stops being called when hidden?) but better safe than sorry

* use null

* persist collapsed state
2024-08-19 21:22:48 -04:00
Chenlei Hu
b8a3e6b1ad Reduce padding on searchbox result item (#547)
* Reduce search result padding

* nit
2024-08-19 21:19:34 -04:00
Chenlei Hu
cfad3cd918 Add setting to hide node category in search result (#546) 2024-08-19 21:00:15 -04:00
Alex "mcmonkey" Goodwin
339e201920 Dom widget playwright tests (#540)
* gitignore win32 browser_test files

just so i can local dev with playwright without git cluttering up

* experimental add test for dom widget node toggle open/closed

* Update test expectations [skip ci]

* vs code extension lied to me, manually fix loc

* Update test expectations [skip ci]

* okay that time was my fault

* Update test expectations [skip ci]

* yknow what dont expect exactly default after actually

* Update test expectations [skip ci]

* test for multiple far panning

* Update test expectations [skip ci]

* oops, flip that

* Update test expectations [skip ci]

* more stable pan coords

* Update test expectations [skip ci]

* 'move' is not strictly relative, so compensate accordingly

* Update test expectations [skip ci]

* test to zoom very far out

* Update test expectations [skip ci]

* add hackaround for node search menu popup

* Update test expectations [skip ci]

* make zoom work better

* Update test expectations [skip ci]

* fix preexisting typo

this function is never called so it's fine

* dom widget toggle needs a delay

otherwise it's a double click

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-08-19 20:54:07 -04:00
bymyself
c227a8af9a Use DPI to calculate screen center (#544) 2024-08-19 20:51:24 -04:00
Chenlei Hu
6b47162606 Update README.md (#545) 2024-08-19 18:18:28 -04:00
Chenlei Hu
a4c5a2a3d1 Update README.md (#543) 2024-08-19 17:26:12 -04:00
Chenlei Hu
45a47be7c0 Ctrl+Shift+Drag zoom on mouse position (#538) (#541)
* Ctrl+Shift+Drag zoom on mouse position (#538)

* Update test expectations [skip ci]

---------

Co-authored-by: bymyself <abolkonsky.rem@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
2024-08-19 17:08:50 -04:00
Chenlei Hu
2252f0a134 Resize large image to fit into window for Image Gallery (#539) 2024-08-19 16:50:40 -04:00
pythongosssss
0dfbcfb2d6 Support clicking on library entries to toggle expand/insert node (#511)
* Support clicking on library entries to toggle expand/insert node

* Fix type
2024-08-19 14:09:34 -04:00
bymyself
b46036f25d Fix escape not resetting activeIndex in gallery (#535)
* Fix escape not resetting activeIndex in gallery

* Use handleVisibilityChange for consistency
2024-08-19 14:00:47 -04:00
Chenlei Hu
f5ce42d5d5 Add setting to show/hide canvas info (#533)
* Add setting to show/hide canvas info

* nit
2024-08-19 12:12:37 -04:00
Chenlei Hu
ce75a29202 Update README.md (Sheild badges) (#532) 2024-08-19 11:36:14 -04:00
Chenlei Hu
6a8a68a240 Image failed to load placeholder (#531)
* Image failed to load placeholder

* Use broken image placeholder in gallery

* nit
2024-08-19 11:16:35 -04:00
Chenlei Hu
f9adaadc7d Use toast on reconnection message (#530) 2024-08-19 10:18:09 -04:00
Chenlei Hu
727992048e Fix type check on TaskItemImpl (#529) 2024-08-19 09:47:33 -04:00
Chenlei Hu
dd1e3f087d Update README.md (#523) 2024-08-18 22:16:46 -04:00
Chenlei Hu
f1c1a3dab7 1.2.28 (#522) 2024-08-18 21:59:00 -04:00
Chenlei Hu
4a43dfe6b9 Performance optimization on queueStore (#521)
* Eager calculate flat outputs

* Optimize flat tasks performance
2024-08-18 21:48:48 -04:00
Chenlei Hu
8576d3797b Prefer image for queue task preview (#520) 2024-08-18 20:59:43 -04:00
Chenlei Hu
22e2628479 Queue preview gallery (#519)
* Custom preview event

* Plub event

* Basic gallery

* Gallery nits

* Navigate with keyboard keys
2024-08-18 20:42:42 -04:00
Chenlei Hu
4e1f14139b Fix queue tab update issue (#518) 2024-08-18 18:11:33 -04:00
Chenlei Hu
0c53ab9177 Fix context menu on node with only optional input (#514) 2024-08-18 16:25:45 -04:00
Chenlei Hu
eb5f4d9bc7 Support view of gifs in queue media preview (#513) 2024-08-18 16:20:56 -04:00
Chenlei Hu
19681fd43d Experimental settings BETA tag (#509)
* Add BETA tag for experimental setting

* Mark node searchbox impl as experimental

* nit
2024-08-18 13:04:37 -04:00
Chenlei Hu
add2f9baa0 Group comfy core settings (#508)
* Add category overwrite

* Group settings
2024-08-18 12:49:23 -04:00
Chenlei Hu
ec5f1152da Organize setting display for new settings dialog (#507) 2024-08-18 11:31:50 -04:00
Chenlei Hu
17aa44d9f6 Activate new settings dialog from default UI (#506) 2024-08-18 10:23:03 -04:00
Chenlei Hu
d8887a434d Add Comfy.Workflow.SortNodeIdOnSave setting (#502)
* Update litegraph (Sort order)

* nit

* Add sort node on save setting

* nit
2024-08-17 23:23:40 -04:00
Chenlei Hu
9d3ca763d0 Queue tab infinite scroll (#501)
* Add vueuse

* Infinite scroll queue tab

* Set item per page to 8

* Handle sidebar resize

* nit
2024-08-17 22:48:31 -04:00
Chenlei Hu
a45851d7a6 1.2.27 (#500) 2024-08-17 21:42:02 -04:00
Chenlei Hu
266336104a Fix frontend only node category (#499) 2024-08-17 21:31:56 -04:00
Chenlei Hu
8c40f83b35 Properly update vue app's node defs on refresh (#493) 2024-08-17 13:23:47 -04:00
Chenlei Hu
5ba524fd94 Add toast message on refresh button click (#492) 2024-08-17 13:11:47 -04:00
Chenlei Hu
966b1dd057 Extension API to add toast message (#491)
* Extension API to add toast message

* Update readme
2024-08-17 12:44:55 -04:00
Chenlei Hu
069766337a Add toast message on execution interrupted (#490)
* Move toast to top level

* Toast store
2024-08-17 12:29:48 -04:00
Chenlei Hu
a1a6eeed0f Remove unused var mainCanvas (#488) 2024-08-17 11:33:17 -04:00
Chenlei Hu
b33874db2b Fix queue cancelled status (#487) 2024-08-17 11:31:46 -04:00
bymyself
05c6193a1d Fix loadGraphData call when loading from json (#484) 2024-08-17 10:47:39 -04:00
bymyself
73f7889f81 Add touch-action event back to canvas (#483) 2024-08-17 09:05:23 -04:00
Alex "mcmonkey" Goodwin
c73915e31e EditAttention: allow negatives (#476)
for #474
2024-08-16 21:56:03 -04:00
Alex "mcmonkey" Goodwin
7626d08c98 vite relative paths fix (#472) 2024-08-16 21:15:03 -04:00
Alex "mcmonkey" Goodwin
f10554d914 fix minor typing error (#473)
introduced at e6d29656fa
2024-08-16 21:14:15 -04:00
Chenlei Hu
238225dd12 1.2.26 (#471) 2024-08-16 14:07:32 -04:00
Chenlei Hu
9cea54686a Pin reroute on link release searchbox (#469)
* Correctly handle wildcard type

* Add test

* nit

* Pin reroute on link release search

* Connect wildcard

* nit
2024-08-16 13:22:17 -04:00
Chenlei Hu
d1715972e3 Register frontend only node defs (#468)
* Register frontend only node defs

* nit

* nit
2024-08-16 12:16:41 -04:00
Chenlei Hu
49a910d7b0 Update litegraph (Sort order) (#467)
* Update litegraph (Sort order)

* Update

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-08-16 11:36:18 -04:00
Chenlei Hu
479ca63e3c Add setting to adjust textarea font size (#463) 2024-08-16 10:17:40 -04:00
Chenlei Hu
7468555c06 1.2.25 (#456) 2024-08-16 01:04:26 -04:00
Chenlei Hu
14a395a0ce Fix queue item context menu (#455) 2024-08-16 01:02:51 -04:00
Chenlei Hu
0a99a2bd53 1.2.24 (#454) 2024-08-16 00:02:15 -04:00
Chenlei Hu
955c703fde Still load workflow even validation fails (#453) 2024-08-15 23:55:20 -04:00
Chenlei Hu
ef8b952d79 Fix undefined spec (#452) 2024-08-15 23:37:42 -04:00
Chenlei Hu
e6d29656fa Queue media preview (#449)
* output url

* Basic image previews

* Split out task item component

* Move task actions to context menu

* simplify

* Move spinner

* Lift context menu to tab scope

* Better tag

* Fix placeholder style

* nit

* Correctly handle cancelled

* nit

* Split out result item as separate component

* nit

* Fix center crop

* nit

* Simplify task item

* Flat list

* Show prompt id

* Make image draggable

* Disable preview for dragging

* Fix key

* Correctly handle task in expanded view

* Add preview
2024-08-15 23:26:38 -04:00
Alex "mcmonkey" Goodwin
9e3dffd7fd Fix relative paths (#447)
ComfyUI is not necessarily the webroot, paths should be relative rather than absolute
2024-08-15 20:27:06 -04:00
Chenlei Hu
429e44f74d Control minify with env var (#445) 2024-08-15 14:41:36 -04:00
Alex "mcmonkey" Goodwin
73b0ecc8cb fix preview nodes clipping under the bottom (#444)
for #411 - moves code to main typescript section (to avoid vue weirdness) and finds the preview node element, adjust top by height
2024-08-15 13:33:02 -04:00
Björn Söderqvist
775f536d30 Add more Zod types to api.ts (#440) 2024-08-15 10:45:40 -04:00
Chenlei Hu
1ff7a70579 1.2.23 (#441) 2024-08-15 09:54:10 -04:00
Chenlei Hu
51233b4be3 Fix node preview location when sidebar location is right (#435) 2024-08-14 22:46:05 -04:00
bymyself
5c4d1c2cec Fix copy/paste on firefox (#432) 2024-08-14 22:05:15 -04:00
Chenlei Hu
711205b499 1.2.22 (#429) 2024-08-14 15:11:45 -04:00
Chenlei Hu
00fe3515b9 Fix delete queue item (#428) 2024-08-14 15:10:17 -04:00
Chenlei Hu
d8ee0b4584 Relax schema on node I/O link (#427) 2024-08-14 14:17:18 -04:00
Chenlei Hu
fc1b0b3e53 Fix searchbox trigger on non-always link release setting (#426)
* Fix searchbox trigger on non-always link release setting

* Add browsertest
2024-08-14 13:44:22 -04:00
Chenlei Hu
a68f7c680b Rename SideBar to Sidebar (#422)
* Rename SideBar to Sidebar

* rename files

* rename files
2024-08-14 11:27:23 -04:00
Chenlei Hu
c6b6bdcb67 Add setting to disable node preview in searchbox (#421) 2024-08-14 10:29:32 -04:00
Chenlei Hu
6993a56c2d Highlight query in search result in node searchbox (#420)
* min match set to 2

* Highlight query in search result

* nit
2024-08-14 10:28:17 -04:00
Chenlei Hu
4eb56a19ba Fix queue/history keybinding (#419) 2024-08-14 09:53:49 -04:00
Chenlei Hu
784947fdea 1.2.21 (#416) 2024-08-13 21:45:23 -04:00
Alex "mcmonkey" Goodwin
64ee131e64 Don't cache API requests (#415)
API requests should by default never be cached
2024-08-13 20:22:22 -04:00
Chenlei Hu
1b59e3ab6d Fast searchbox dialog activation (#410) 2024-08-13 16:25:11 -04:00
Chenlei Hu
37b414d1b2 Use fuse.js extended search (#405) 2024-08-13 16:01:52 -04:00
Yuta Hayashibe
7245eee85b Perform text replacement for SaveAnimatedWEBP in addition to SaveImage (#403) 2024-08-13 13:57:26 -04:00
Yuta Hayashibe
9f3696e70f Fix typos (#404)
* Fix typos: Interupt -> Interrupt

* Fix typos: tempateManagerRow -> templateManagerRow

* Fix some typos

* Fix typos: Convertable -> Convertible

* Fix some typos
2024-08-13 13:57:02 -04:00
Chenlei Hu
d5deb6d2a0 Only report each unknown message type once (#402) 2024-08-13 11:18:30 -04:00
Chenlei Hu
41889c3cfe Add GitHub action to automatically submit release PR to main repo (#400) 2024-08-13 10:14:38 -04:00
Chenlei Hu
e8602e88fa 1.2.20 (#398) 2024-08-12 22:28:17 -04:00
Chenlei Hu
b5f2d334d9 Add sidebar size setting (normal/small) (#397)
* Add sidebar size setting (normal/small)

* Set default small sidebar size for small window
2024-08-12 22:27:35 -04:00
Chenlei Hu
c459698956 Add side bar location in settings (left/right) (#396)
* Allow side bar on right side

* Panel on the right
2024-08-12 21:49:07 -04:00
Chenlei Hu
f91e335ca7 Only shim legacy directories (#395)
* Only shim legacy directories

* nit
2024-08-12 21:11:30 -04:00
Chenlei Hu
1fc173b48f 1.2.19 (#394) 2024-08-12 20:55:06 -04:00
Chenlei Hu
4fe44339fd Only validate new history items (#393) 2024-08-12 20:30:11 -04:00
Chenlei Hu
f987f4f5f3 Configure minify (#392) 2024-08-12 17:12:47 -04:00
Chenlei Hu
91e21b1387 Update ws message schema on reconnecting (#390)
* Update ws message schema on reconnecting

* nit
2024-08-12 13:21:48 -04:00
Chenlei Hu
a5be1f6072 Revert "Minify build (#373)" (#391)
This reverts commit 9385014799.
2024-08-12 13:12:51 -04:00
Chenlei Hu
d5f30be06d 1.2.18 (#387) 2024-08-12 09:21:53 -04:00
Chenlei Hu
d607f6c7f7 Revert "Add support for LiteGraph to convert to classes (#334)" (#386)
This reverts commit e2141a81e2.
2024-08-12 09:19:10 -04:00
Chenlei Hu
d9df0328c5 Revert "Update litegraph (ES6 LLink & LGraphNode) (#372)" (#385)
This reverts commit cfce1c6037.
2024-08-12 09:18:37 -04:00
Chenlei Hu
aff059b98b 1.2.17 (#380) 2024-08-11 19:46:42 -04:00
Chenlei Hu
cf53e8df6a Revert "Update primevue/themes to 4.0.0 (#378)" (#379)
This reverts commit d1d4324e57.
2024-08-11 19:39:55 -04:00
Chenlei Hu
d1d4324e57 Update primevue/themes to 4.0.0 (#378) 2024-08-11 19:38:27 -04:00
Chenlei Hu
c86abd3dc6 Remove pending task count on queue button (#377) 2024-08-11 19:33:43 -04:00
Chenlei Hu
281ed0c5d1 Show pending task count on side bar queue icon (#376)
* remove listener

* Store pending task count

* Add iconBadge to queue icon
2024-08-11 19:15:21 -04:00
Chenlei Hu
edf0396619 Type WS messages (#375) 2024-08-11 18:05:14 -04:00
Chenlei Hu
2a5b2e8d12 Reduce fusejs threshold (#374) 2024-08-11 12:39:23 -04:00
Chenlei Hu
9385014799 Minify build (#373) 2024-08-11 10:59:39 -04:00
Chenlei Hu
cfce1c6037 Update litegraph (ES6 LLink & LGraphNode) (#372) 2024-08-11 10:14:00 -04:00
Chenlei Hu
480679aa32 Relax restriction on data type in schema (#371) 2024-08-11 10:12:16 -04:00
filtered
e2141a81e2 Add support for LiteGraph to convert to classes (#334)
* Add support for LiteGraph to convert to classes

* Fix large context menu search regression

* Remove debug code

* Fix regression from rename & prototype change

* Fix super() calls to match LGraphNode
2024-08-11 09:46:54 -04:00
Chenlei Hu
0f3b58b610 Add space to setting group label (#370)
* Add space to setting group label

* handle acronym
2024-08-11 09:36:52 -04:00
Chenlei Hu
7ef8e56c25 1.2.16 (#366) 2024-08-10 21:06:51 -04:00
Chenlei Hu
9ffdf768b1 Better no task placeholder (#365) 2024-08-10 21:05:40 -04:00
Chenlei Hu
23fcdd3e44 Attach DOM widgets to canvas container instead of document body (#364) 2024-08-10 18:38:56 -04:00
Chenlei Hu
7ce7490bc3 Add search settings feature (#362)
* Add setting searchbox ui

* Basic search

* Remove first divider

* Keep group label on search result

* No result placeholder

* Prevent no result flash

* i18n

* Disable category nav when searching
2024-08-10 17:26:57 -04:00
pythongosssss
3e7b0a4907 Test using latest examples (#361) 2024-08-10 16:46:33 -04:00
Chenlei Hu
a095e7ecae Show category in node search box instead of description (#360) 2024-08-10 14:52:37 -04:00
Chenlei Hu
fe0d63e16c Use consistent NodeId and SlotIndex in schema (#359)
* Use consistent NodeId and SlotIndex in schema

* Add test
2024-08-10 14:15:00 -04:00
Chenlei Hu
3c76554bec 1.2.15 (#358) 2024-08-10 11:55:13 -04:00
Chenlei Hu
ce2a2dd2b6 Add link release searchbox trigger mode (#356) 2024-08-10 10:30:29 -04:00
Chenlei Hu
d6c304690c Allow skipping workflow validation (#355) 2024-08-10 09:49:12 -04:00
Chenlei Hu
e1bc2708d3 Make workflow group color optional (#354) 2024-08-10 08:55:48 -04:00
Chenlei Hu
95dc6ff5de Refactor nodeDefStore.nodeTree (#351) 2024-08-09 17:53:42 -04:00
Chenlei Hu
4b83ee3918 link release pops up searchbox by default (#348) (#350)
* link release pops up searchbox by default (#348)

* link release pops up searchbox by default

* Update browser test

* Fix tests

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-08-09 16:33:30 -04:00
Chenlei Hu
078df413b5 Update lg (ES6 LGraph) (#347) 2024-08-09 11:36:52 -04:00
Chenlei Hu
3fc85f1fb6 Update README.md (#346) 2024-08-09 11:11:43 -04:00
Chenlei Hu
ce14c1c071 i18n for setting dialog header (#343) 2024-08-09 09:30:08 -04:00
Chenlei Hu
0d31dc5b4c Reduce setting content height to avoid dialog level scroll (#341) 2024-08-08 20:31:00 -04:00
Chenlei Hu
42c1e4b5e1 1.2.14 (#340) 2024-08-08 20:15:38 -04:00
Chenlei Hu
5490ccf4f0 Assign default category Others (#339)
* Assign default category Others

* nit
2024-08-08 20:09:49 -04:00
Chenlei Hu
a5f0d2b201 Categorize setting items (#338)
* Basic setting panel rework

* refactor

* Style the setting item

* Reject invalid value

* nit

* nit

* Sort settings by label

* info chip as icon

* nit
2024-08-08 17:52:41 -04:00
Chenlei Hu
02d7f91e9e Migrate settings dialog to Vue (#335)
* Basic setting dialog

* Add custom setting value render

* handle combo options

* Add input slider

* 100% width for select dropdown
2024-08-07 14:01:43 -04:00
Chenlei Hu
eb1c66c90a Revert "Revert "Update litegraph (Vite build) (#320)" (#329)" (#332)
This reverts commit 3249bbf4ab.
2024-08-06 21:44:44 -04:00
Chenlei Hu
6b1776450b Explicitly bind litegraph names to global scope (#331) 2024-08-06 21:40:05 -04:00
Chenlei Hu
7804b25d5f 1.2.13 (#330) 2024-08-06 21:06:34 -04:00
Chenlei Hu
3249bbf4ab Revert "Update litegraph (Vite build) (#320)" (#329)
This reverts commit e162d0007c.
2024-08-06 21:04:48 -04:00
Chenlei Hu
baa1e54fc0 1.2.12 (#328) 2024-08-06 20:26:40 -04:00
Chenlei Hu
968a1da227 Add more litegraph change browser tests (#326)
* Add tests on litegraph batch disconnect shortcut (#323)

* Add tests on right click node pin/unpin (#324)

* Update test expectations [skip ci]

---------

Co-authored-by: bymyself <abolkonsky.rem@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
2024-08-06 20:24:40 -04:00
Chenlei Hu
a013d83fc0 Shift dialog close to right (#327) 2024-08-06 20:23:57 -04:00
Chenlei Hu
564ec887f2 Fix script type (#325) 2024-08-06 20:18:34 -04:00
Chenlei Hu
79469bd2b1 Missing node dialog revamp (#322)
* Basic rework of load workflow warning dialog

* Better style

* Add vue jest support

* Mock vue component in jest test

* nit

* Make dialog maximizable
2024-08-06 20:11:05 -04:00
Robin Huang
6fe2297cc1 Add models information to default workflow. (#321)
* Add models information to default workflow.

* Add models to zod schema.

* Fix zod schema.

* Update schema name.

* Add z prefix to modelfile schema name.
2024-08-06 13:45:07 -04:00
Chenlei Hu
e162d0007c Update litegraph (Vite build) (#320) 2024-08-06 10:46:19 -04:00
Chenlei Hu
5419865740 1.2.11 (#318) 2024-08-05 20:32:46 -04:00
Chenlei Hu
b90b1194d6 Organize searchbox files (#315) 2024-08-05 18:29:06 -04:00
Chenlei Hu
1eb45ddc55 Update README.md (#314) 2024-08-05 17:47:42 -04:00
Chenlei Hu
2f1df2c6ce Proper truncate of long content in node preview (#313) 2024-08-05 16:57:36 -04:00
Chenlei Hu
3269b54aae Sync 2666 Execution Model Inversion (#312)
* Sync 2666 Execution Model Inversion

* Fix unittest

* Fallback compatible
2024-08-05 16:42:37 -04:00
Chenlei Hu
50c4c87e62 Add error boundary on node.DrawBackground (#311) 2024-08-05 15:41:12 -04:00
Chenlei Hu
1b96c16cca Sync commit fixing flac metadata (#307) 2024-08-05 13:59:44 -04:00
Chenlei Hu
31ca016055 1.2.10 (#301) 2024-08-04 20:15:29 -04:00
Chenlei Hu
cf9d95aa97 Fix PrimeVue ref error on adding node via searchbox (#298) 2024-08-04 12:27:02 -04:00
pythongosssss
7a980f46c9 Add support for node/input/output tooltips (#287)
* Add support for node/input/output tooltips

* pr feedback

* Remove
2024-08-04 11:54:46 -04:00
Chenlei Hu
c48f68e53e Revert to sync validation on node def for better performance (#297) 2024-08-04 11:02:54 -04:00
Chenlei Hu
0210c7f438 Update README.md (#296) 2024-08-04 10:15:07 -04:00
Chenlei Hu
0384cf96c4 Reduce loglevel on node def validation (#295)
* Lower the loglevel of node def validation messages

* Fix tests
2024-08-04 10:08:55 -04:00
Chenlei Hu
4bebcb408e Add eslint (#294)
* Add eslint

* Add eslint github action
2024-08-04 09:35:49 -04:00
余腾靖
b5a919e8b2 fix: remove useless @ts-ignore and migrate to @ts-expect-error (#293)
* fix: vite primevue/treenode import error

* refactor: remove useless @ts-ignore and replace with @ts-expect-error

* build(tsconfig): enable incremental to speed up secondary time type check
2024-08-04 07:22:24 -04:00
Chenlei Hu
7e669b0f68 Add issue templates (#290) 2024-08-03 21:30:25 -04:00
Chenlei Hu
244cd3c920 1.2.9 (#285) 2024-08-03 14:25:16 -04:00
Chenlei Hu
0cf5e647af Fix copy paste of widget value (#284)
* Fix copy paste of widget value

* Fix ui tests

* Allow undefined group font size

* Update test expectations [skip ci]

* nit

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-08-03 14:22:43 -04:00
Chenlei Hu
f0f867481d Fix canvas not init issue (#283) 2024-08-03 10:48:54 -04:00
Chenlei Hu
d02b074fa3 Manage searchbox imp setting in Vue app (#282)
* Manage searchbox setting in vue

* nit
2024-08-03 10:31:10 -04:00
Chenlei Hu
e14d84526a 1.2.8 (#279) 2024-08-01 21:19:41 -04:00
Chenlei Hu
2aa9166079 Disable flux example workflow test (#278) 2024-08-01 21:17:28 -04:00
Chenlei Hu
3baa07e0a9 Update litegraph (Font size fix / Perf improvement) (#275) 2024-07-31 11:06:04 -04:00
Chenlei Hu
c494cd211e Allow INT/FLOAT represent list of numbers (#274) 2024-07-31 10:45:46 -04:00
Chenlei Hu
c00e2fd208 Allow input spec with extra values passthrough (#273)
* Allow input spec extra values passthrough

* Refine custom input spec

* nit

* nit
2024-07-31 09:51:32 -04:00
bymyself
d77343da83 Sync #4090 (#272) 2024-07-31 08:45:44 -04:00
Chenlei Hu
c611c15d40 Update README.md (#271) 2024-07-30 19:12:44 -04:00
Chenlei Hu
269686eebb 1.2.7 (#270) 2024-07-30 19:09:22 -04:00
Chenlei Hu
0e3590d017 Update litegraph (Batch link move with shift + drag) (#268)
* Refactor based on new event data format

* nit

* Add playwright tests

* Update test expectations [skip ci]

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-07-30 19:06:58 -04:00
Alistor
7d2d6df57b Add spellcheck option to Multiline widget, add Interrupt Queue keybind (#267)
* Add spellcheck option to Multiline widget and set to false by default

* Add Queue Interrupt Keybind

* Update keybinds.ts

Fixed indentation
2024-07-30 17:34:54 -04:00
Chenlei Hu
4462dabc63 Truncate JSON default value in node preview (#264) 2024-07-30 10:12:47 -04:00
Chenlei Hu
53bfc0c95a Block UI interaction when loading (#263) 2024-07-30 09:56:40 -04:00
Chenlei Hu
b78682689e Update litegraph (#262) 2024-07-30 09:25:27 -04:00
Chenlei Hu
6d1dce8255 1.2.6 (#261) 2024-07-29 18:38:51 -04:00
Chenlei Hu
73f4e5143d Attach isLeaf info (#260) 2024-07-29 17:49:57 -04:00
Chenlei Hu
7d75cc99ba Add sort button in node library sidebar tab (#259)
* Add sort button on node library

* tab template
2024-07-29 12:39:54 -04:00
Chenlei Hu
0aa7d0b99a Store spinner state in workspace state store (#256) 2024-07-29 10:54:22 -04:00
Chenlei Hu
66b690e5c8 Manage canvas element in Vue (#255)
* Manage canvas element in Vue

* nit

* Fix unittest
2024-07-29 10:29:29 -04:00
Chenlei Hu
6e27b884fc Fix node preview widget overflow (#254)
* Fix node preview widget overflow

* nit
2024-07-28 21:51:14 -04:00
Chenlei Hu
561162fb3e Update litegraph (Fix drag + alt copy node) (#253)
* Update litegraph

* Update github action

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-07-28 13:39:45 -04:00
Chenlei Hu
7c6bd7ed71 1.2.5 (#252) 2024-07-28 09:41:53 -04:00
Chenlei Hu
fc5bdf80b3 Fix no task message display (#251) 2024-07-28 09:39:17 -04:00
Chenlei Hu
033f242e43 Float workflow selection on top of sidebar (#250) 2024-07-28 09:37:00 -04:00
Chenlei Hu
304429b967 Update README.md (#249) 2024-07-28 08:20:41 -04:00
Chenlei Hu
6dbdb9baa6 Fix clientX/Y offset calculation (#248) 2024-07-28 07:50:00 -04:00
filtered
3e3e909e36 Fix "undo" incorrectly undoing text input (#247)
Fixes an issue where under certain conditions, the ComfyUI custom undo / redo functions would not run when intended to.

When trying to undo an action like deleting several nodes, instead the native browser undo runs - e.g. a textarea gets focus and the last typed text is undone.  Clicking outside the textarea and hitting ctrl + z again just keeps doing the same thing.
2024-07-28 06:57:05 -04:00
Chenlei Hu
e128f39760 1.2.4 (#246) 2024-07-27 22:36:22 -04:00
Chenlei Hu
f22713aeb0 1.2.3 (#244) 2024-07-27 22:32:06 -04:00
Chenlei Hu
74fd1c9abe Reduce history queue max length (#243) 2024-07-27 22:23:46 -04:00
Chenlei Hu
f4f0c960a3 Fix combo input default value (#242)
* Fix combo input default value

* Supress logs and fix failure
2024-07-27 22:17:42 -04:00
Chenlei Hu
c0875d066a Node library side bar tab (#237)
* Basic tree

* Add node filter

* Fix key issue

* Add icons

* Node count

* nit

* Add node preview basics

* Node preview

* Make comfy node in node lib draggable

* Set drop target

* Add node on drop

* Drop on dynamic location

* nit

* nit

* Fix hover preview issue

* nit

* More visual diff between node and folder

* Add playwright test

* Add dep

* Get rid of screenshot test
2024-07-27 21:28:48 -04:00
Chenlei Hu
980ed0083d Fix settings getter (#240) 2024-07-27 18:22:22 -04:00
Chenlei Hu
76be537351 Add pp.addNodeOnGraph to simplify adding node on canvas (#239) 2024-07-27 17:44:17 -04:00
Alexander Brown
424a5f7a86 Expose Queue Button on the menu. (#238) 2024-07-27 17:44:06 -04:00
Chenlei Hu
542e1c1f59 Move sidebar y-axis overflow style to container (#236) 2024-07-26 16:25:12 -04:00
Chenlei Hu
972ffe73e3 Use ComfyNodeDefImpl on nodeDefStore (#235) 2024-07-26 15:53:22 -04:00
Chenlei Hu
b4d7735855 Use ComfyNodeDefImpl on nodeSearchService (#233)
* Make nodeSearchService use ComfyNodeDefImpl

* Fix test
2024-07-26 13:41:24 -04:00
Chenlei Hu
9bcc08d7ab Allow duplicated output names (#232) 2024-07-26 11:57:55 -04:00
Chenlei Hu
a1750212e5 Make node def output class (#231)
* Refactor node def output

* Adjust test
2024-07-26 11:09:43 -04:00
Chenlei Hu
4dba1d3ab0 Fix splitter overlay z-index (#230) 2024-07-26 10:47:04 -04:00
Chenlei Hu
ee6eed1c1c Fix extension register tab with API (#229)
* Get rid of extension manager impl

* nit

* Test register tab
2024-07-26 10:29:20 -04:00
Chenlei Hu
8e1d3f3baa Use ComfyNodeDefImpl on NodePreview component (#228)
* store widgets

* Use node def impl
2024-07-25 23:15:03 -04:00
Chenlei Hu
dc13ed102b Reverse init order (#227) 2024-07-25 22:20:38 -04:00
Chenlei Hu
9d56bb4e0e Set default empty record for InputSpec (#225) 2024-07-25 21:29:22 -04:00
Chenlei Hu
c97ff6fd85 Add name field to BaseInputSpec (#226) 2024-07-25 21:07:16 -04:00
Chenlei Hu
0ec15ba101 Transform ComfyNodeDef to ComfyNodeDefImpl (#224) 2024-07-25 20:27:16 -04:00
Chenlei Hu
55d5ec8c25 (update) CSS formatting (#221)
Co-authored-by: dmx <vincent.f@intp.com>
2024-07-25 16:32:57 -04:00
Chenlei Hu
c6d2767af1 Transforms ComfyInputsSpec on nodes (#220)
* Convert input spec defs

* Fix test

* Add combo test

* import metadata
2024-07-25 13:50:55 -04:00
Chenlei Hu
e179f75387 Apply new code format standard (#217) 2024-07-25 10:10:18 -04:00
Chenlei Hu
19c70d95d3 Sidebar tab API for extensions (#215)
* Add extensionManager to manage tabs

* Fix null bug

* nit
2024-07-24 21:31:59 -04:00
Chenlei Hu
ebdd7b8e40 Use store to manage nodeSearchService (#214) 2024-07-24 12:00:23 -04:00
Chenlei Hu
b73fe80761 [Major Refactor] Use pinia store to manage setting & nodeDef (#202)
* Node def store and settings tore

* Fix initial values

* Remove legacy setting listen

* Fix searchbox test
2024-07-24 11:49:09 -04:00
Chenlei Hu
84d8c5fc16 Fix ws dev server URL (#213) 2024-07-24 11:01:22 -04:00
Chenlei Hu
5b4e96f6c5 Test theme toggle feature (#212)
* WIP

* Add test on theme toggle

* Add teardown logic

* Cleanup menu setting
2024-07-23 17:56:35 -04:00
Chenlei Hu
1b7db43f8a Format everything (#211) 2024-07-23 15:40:54 -04:00
Chenlei Hu
648e52e39c 1.2.2 (#209) 2024-07-23 14:21:37 -04:00
Chenlei Hu
89b195dc13 Only install chromium in github action (#210) 2024-07-23 14:21:26 -04:00
Chenlei Hu
69d95f6e46 Update litegraph (Fix auto connect slot) (#208)
* Update litegraph

* Update version again

* Add browser test for litegraph change

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-07-23 14:05:02 -04:00
Chenlei Hu
609d3fe279 Add i18n for side tool bar tooltips (#207)
* Add npm dep

* Add i18n for side bar tooltips
2024-07-23 10:43:10 -04:00
Chenlei Hu
9b36c6b254 Add side bar icon tooltip (#206) 2024-07-23 09:45:47 -04:00
Chenlei Hu
d87058babf 1.2.1 (#204) 2024-07-22 23:00:13 -04:00
Chenlei Hu
a71f7671ae Fix default setting issue for first time install (#203)
* Fix default setting issue for first time install

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-07-22 22:54:59 -04:00
Chenlei Hu
bd68617c82 Fix theme toggle (#200)
* Use builtin event on color change

* Fix theme toggle

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-07-22 16:46:02 -04:00
635 changed files with 57318 additions and 17201 deletions

View File

@@ -6,11 +6,23 @@ PLAYWRIGHT_TEST_URL=http://localhost:5173
# Note: localhost:8188 does not work.
DEV_SERVER_COMFYUI_URL=http://127.0.0.1:8188
# Allow dev server access from remote IP addresses.
# If true, the vite dev server will listen on all addresses, including LAN
# and public addresses.
VITE_REMOTE_DEV=false
# The target ComfyUI checkout directory to deploy the frontend code to.
# The dist directory will be copied to {DEPLOY_COMFYUI_DIR}/custom_web_versions/main/dev
# Add `--front-end-root {DEPLOY_COMFYUI_DIR}/custom_web_versions/main/dev`
# to ComfyUI launch script to serve the custom web version.
DEPLOY_COMFYUI_DIR=/home/ComfyUI/web
# The directory containing the ComfyUI installation used to run Playwright tests.
# If you aren't using a separate install for testing, point this to your regular install.
TEST_COMFYUI_DIR=/home/ComfyUI
# The directory containing the ComfyUI_examples repo used to extract test workflows.
EXAMPLE_REPO_PATH=tests-ui/ComfyUI_examples
EXAMPLE_REPO_PATH=tests-ui/ComfyUI_examples
# Whether to enable minification of the frontend code.
ENABLE_MINIFY=true

7
.gitattributes vendored Normal file
View File

@@ -0,0 +1,7 @@
# Default
* text=auto
# Force TS to LF to make the unixy scripts not break on Windows
*.ts text eol=lf
*.vue text eol=lf
*.js text eol=lf

78
.github/ISSUE_TEMPLATE/bug-report.yaml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: Bug Report
description: "Something is not behaving as expected."
title: "[Bug]: "
labels: ['Potential Bug']
body:
- type: markdown
attributes:
value: |
Before submitting a **Bug Report**, please ensure the following:
- **1:** You are running the latest version of ComfyUI.
- **2:** You have looked at the existing bug reports and made sure this isn't already reported.
- **3:** You confirmed that the bug is not caused by a custom node. You can disable all custom nodes by passing
`--disable-all-custom-nodes` command line argument.
- type: textarea
attributes:
label: Frontend Version
description: 'What is the frontend version you are using? You can check this in the settings dialog'
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: 'What you expected to happen.'
validations:
required: true
- type: textarea
attributes:
label: Actual Behavior
description: 'What actually happened. Please include a screenshot / video clip of the issue if possible.'
validations:
required: true
- type: textarea
attributes:
label: Steps to Reproduce
description: "Describe how to reproduce the issue. Please be sure to attach a workflow JSON or PNG, ideally one that doesn't require custom nodes to test. If the bug open happens when certain custom nodes are used, most likely that custom node is what has the bug rather than ComfyUI, in which case it should be reported to the node's author."
validations:
required: true
- type: textarea
attributes:
label: Debug Logs
description: 'Please copy the output from your terminal logs here.'
render: powershell
validations:
required: true
- type: textarea
attributes:
label: Browser Logs
description: 'Please copy the output from your browser logs here. You can access this by pressing F12 to toggle the developer tools, then navigating to the Console tab.'
validations:
required: true
- type: textarea
attributes:
label: Setting JSON
description: 'Please upload the setting file here. The setting file is located at `user/default/comfy.settings.json`'
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: What browsers do you use to access the UI ?
multiple: true
options:
- Mozilla Firefox
- Google Chrome
- Brave
- Apple Safari
- Microsoft Edge
- Android
- iOS
- Other
- type: textarea
attributes:
label: Other
description: 'Any other additional information you think might be helpful.'
validations:
required: false

View File

@@ -0,0 +1,40 @@
name: Feature Request
description: Suggest an idea for this project
title: "[Feature Request]: "
labels: ["enhancement"]
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the feature you want, and that it's not implemented in a recent build/commit.
options:
- label: I have searched the existing issues and checked the recent builds/commits
required: true
- type: markdown
attributes:
value: |
*Please fill this form with as much information as possible, provide screenshots and/or illustrations of the feature if possible*
- type: textarea
id: feature
attributes:
label: What would your feature do ?
description: Tell us about your feature in a very clear and simple way, and what problem it would solve
validations:
required: true
- type: textarea
id: workflow
attributes:
label: Proposed workflow
description: Please provide us with step by step information on how you'd like the feature to be accessed and used
value: |
1. Go to ....
2. Press ....
3. ...
validations:
required: true
- type: textarea
id: misc
attributes:
label: Additional information
description: Add any other context or screenshots about the feature request here.

20
.github/workflows/eslint.yaml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: ESLint
on:
pull_request:
branches:
- main
- master
- 'dev*'
jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: lts/*
- run: npm ci
- run: npm run lint

23
.github/workflows/format.yaml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Prettier Check
on:
pull_request:
branches: [ main, master, dev* ]
jobs:
prettier:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Run Prettier check
run: npx prettier --check './**/*.{js,ts,tsx,vue}'

View File

@@ -5,52 +5,15 @@ name: Update Playwright Expectations
on:
pull_request:
types: [ labeled ]
branches: [ main, master ]
jobs:
test:
runs-on: ubuntu-latest
if: github.event.label.name == 'New Browser Test Expectations'
steps:
- name: Checkout ComfyUI
uses: actions/checkout@v4
with:
repository: "comfyanonymous/ComfyUI"
path: "ComfyUI"
ref: master
- name: Checkout ComfyUI_frontend
uses: actions/checkout@v4
with:
repository: "huchenlei/ComfyUI_frontend"
path: "ComfyUI_frontend"
ref: ${{ github.head_ref }}
- uses: actions/setup-node@v3
with:
node-version: lts/*
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install wait-for-it
working-directory: ComfyUI
- name: Build & Install ComfyUI_frontend
run: |
npm ci
npm run build
rm -rf ../ComfyUI/web/*
mv dist/* ../ComfyUI/web/
working-directory: ComfyUI_frontend
- name: Start ComfyUI server
run: |
python main.py --cpu &
wait-for-it --service 127.0.0.1:8188 -t 600
working-directory: ComfyUI
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
- name: Install Playwright Browsers
run: npx playwright install --with-deps
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Run Playwright tests and update snapshots
id: playwright-tests
@@ -72,6 +35,8 @@ jobs:
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@github.com'
git fetch origin ${{ github.head_ref }}
git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }}
git add browser_tests
git commit -m "Update test expectations [skip ci]"
git push origin HEAD:${{ github.head_ref }}

View File

@@ -5,7 +5,6 @@ on:
branches:
- main
- master
- 'dev*'
pull_request:
branches:
- main
@@ -13,75 +12,64 @@ on:
- 'dev*'
jobs:
test:
jest-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout ComfyUI
uses: actions/checkout@v4
with:
repository: "comfyanonymous/ComfyUI"
path: "ComfyUI"
ref: master
- name: Checkout ComfyUI_frontend
uses: actions/checkout@v4
with:
repository: "Comfy-Org/ComfyUI_frontend"
path: "ComfyUI_frontend"
- name: Get commit message
id: commit-message
run: echo "::set-output name=message::$(git log -1 --pretty=%B)"
working-directory: ComfyUI_frontend
- name: Checkout ComfyUI_examples
uses: actions/checkout@v4
with:
repository: "comfyanonymous/ComfyUI_examples"
path: "ComfyUI_frontend/tests-ui/ComfyUI_examples"
ref: master
- name: Skip CI
if: contains(steps.commit-message.outputs.message, '[skip ci]')
run: echo "Skipping CI as commit contains '[skip ci]'"
continue-on-error: true
working-directory: ComfyUI_frontend
- uses: actions/setup-node@v3
with:
node-version: lts/*
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install wait-for-it
working-directory: ComfyUI
- name: Build & Install ComfyUI_frontend
run: |
npm ci
npm run build
rm -rf ../ComfyUI/web/*
mv dist/* ../ComfyUI/web/
working-directory: ComfyUI_frontend
- name: Start ComfyUI server
run: |
python main.py --cpu &
wait-for-it --service 127.0.0.1:8188 -t 600
working-directory: ComfyUI
- name: Run UI tests
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
- name: Run Jest tests
run: |
npm run test:generate
npm run test:generate:examples
npm test -- --verbose
npm run test:jest:fast -- --verbose
working-directory: ComfyUI_frontend
playwright-tests-chromium:
runs-on: ubuntu-latest
steps:
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
- name: Install Playwright Browsers
run: npx playwright install --with-deps
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Run Playwright tests
run: npx playwright test
- name: Run Playwright tests (Chromium)
run: npx playwright test --project=chromium
working-directory: ComfyUI_frontend
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
name: playwright-report-chromium
path: ComfyUI_frontend/playwright-report/
retention-days: 30
playwright-tests-chromium-2x:
runs-on: ubuntu-latest
steps:
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Run Playwright tests (Chromium 2x)
run: npx playwright test --project=chromium-2x
working-directory: ComfyUI_frontend
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-chromium-2x
path: ComfyUI_frontend/playwright-report/
retention-days: 30
playwright-tests-mobile-chrome:
runs-on: ubuntu-latest
steps:
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v2.1
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Run Playwright tests (Mobile Chrome)
run: npx playwright test --project=mobile-chrome
working-directory: ComfyUI_frontend
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-mobile-chrome
path: ComfyUI_frontend/playwright-report/
retention-days: 30

31
.github/workflows/vitest.yaml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: Vitest Tests
on:
push:
branches:
- main
- master
- 'dev*'
pull_request:
branches:
- main
- master
- 'dev*'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Run Vitest tests
run: npm run test:component

2
.gitignore vendored
View File

@@ -23,6 +23,7 @@ dist-ssr
*.njsproj
*.sln
*.sw?
components.d.ts
# Ignore test data.
tests-ui/data/*
@@ -34,6 +35,7 @@ tests-ui/workflows/examples
/playwright-report/
/blob-report/
/playwright/.cache/
browser_tests/*/*-win32.png
.env

View File

@@ -1 +1,5 @@
npx lint-staged
if [[ "$OS" == "Windows_NT" ]]; then
npx.cmd lint-staged
else
npx lint-staged
fi

View File

@@ -1,4 +1,7 @@
{
"semi": true,
"trailingComma": "es5"
"singleQuote": true,
"tabWidth": 2,
"semi": false,
"trailingComma": "none",
"printWidth": 80
}

476
README.md
View File

@@ -1,50 +1,420 @@
<div align="center">
# ComfyUI_frontend
Front-end of [ComfyUI](https://github.com/comfyanonymous/ComfyUI) modernized. This repo is fully compatible with the existing extension system.
**Official front-end implementation of [ComfyUI](https://github.com/comfyanonymous/ComfyUI).**
## How To Use
[![Website][website-shield]][website-url]
[![Discord][discord-shield]][discord-url]
[![Matrix][matrix-shield]][matrix-url]
<br>
[![][github-release-shield]][github-release-link]
[![][github-release-date-shield]][github-release-link]
[![][github-downloads-shield]][github-downloads-link]
[![][github-downloads-latest-shield]][github-downloads-link]
Add command line argument `--front-end-version Comfy-Org/ComfyUI_frontend@latest` to your
ComfyUI launch script.
For Windows stand-alone build users, please edit the `run_cpu.bat` / `run_nvidia_gpu.bat` file as following
[github-release-shield]: https://img.shields.io/github/v/release/Comfy-Org/ComfyUI_frontend?style=flat&sort=semver
[github-release-link]: https://github.com/Comfy-Org/ComfyUI_frontend/releases
[github-release-date-shield]: https://img.shields.io/github/release-date/Comfy-Org/ComfyUI_frontend?style=flat
[github-downloads-shield]: https://img.shields.io/github/downloads/Comfy-Org/ComfyUI_frontend/total?style=flat
[github-downloads-latest-shield]: https://img.shields.io/github/downloads/Comfy-Org/ComfyUI_frontend/latest/total?style=flat&label=downloads%40latest
[github-downloads-link]: https://github.com/Comfy-Org/ComfyUI_frontend/releases
[matrix-shield]: https://img.shields.io/badge/Matrix-000000?style=flat&logo=matrix&logoColor=white
[matrix-url]: https://app.element.io/#/room/%23comfyui_space%3Amatrix.org
[website-shield]: https://img.shields.io/badge/ComfyOrg-4285F4?style=flat
[website-url]: https://www.comfy.org/
[discord-shield]: https://img.shields.io/discord/1218270712402415686?style=flat&logo=discord&logoColor=white&label=Discord
[discord-url]: https://www.comfy.org/discord
</div>
## Release Schedule
### Nightly Release
Nightly releases are published daily at [https://github.com/Comfy-Org/ComfyUI_frontend/releases](https://github.com/Comfy-Org/ComfyUI_frontend/releases).
To use the latest nightly release, add the following command line argument to your ComfyUI launch script:
```bat
--front-end-version Comfy-Org/ComfyUI_frontend@latest
```
#### For Windows Stand-alone Build Users
Edit your `run_cpu.bat` or `run_nvidia_gpu.bat` file as follows:
```bat
.\python_embeded\python.exe -s ComfyUI\main.py --windows-standalone-build --front-end-version Comfy-Org/ComfyUI_frontend@latest
pause
```
## Road Map
### Stable Release
### What has been done
Stable releases are published weekly in the ComfyUI main repository, aligned with ComfyUI backend's stable release schedule.
- Migrate all code to TypeScript with minimal change modification to the original logic.
- Bundle all code with vite's rollup build.
- Added a shim layer to be backward compatible with the existing extension system. <https://github.com/huchenlei/ComfyUI_frontend/pull/15>
- Front-end dev server.
- Zod schema for input validation on ComfyUI workflow.
- Make litegraph a npm dependency. <https://github.com/Comfy-Org/ComfyUI_frontend/pull/89>
- Introduce Vue to start managing part of the UI.
#### Feature Freeze
- Starting with node search box revamp ![image](https://github.com/user-attachments/assets/ef6ce019-5194-4e55-9f1e-91440e473920)
There will be a 2-day feature freeze before each stable release. During this period, no new major features will be merged.
- Easy install and version management (<https://github.com/comfyanonymous/ComfyUI/pull/3897>).
## Release Summary
### Major features
### What to be done
<details>
<summary>v1.3.22: Integrated server terminal</summary>
- Replace the existing ComfyUI front-end impl
- Remove `@ts-ignore`s.
- Turn on `strict` on `tsconfig.json`.
- Introduce a UI library to add more widget types for node developers.
- LLM streaming node.
- Linear mode (Similar to InvokeAI's linear mode).
- Better node management. Sherlock https://github.com/Nuked88/ComfyUI-N-Sidebar.
- Keybinding settings management. Register keybindings API for custom nodes.
- New extensions API for adding UI-related features.
Press Ctrl + ` to toggle integrated terminal.
https://github.com/user-attachments/assets/eddedc6a-07a3-4a83-9475-63b3977f6d94
</details>
<details>
<summary>v1.3.7: Keybinding customization</summary>
## Basic UI
![image](https://github.com/user-attachments/assets/c84a1609-3880-48e0-a746-011f36beda68)
## Reset button
![image](https://github.com/user-attachments/assets/4d2922da-bb4f-4f90-8017-a8e4a0db07c7)
## Edit Keybinding
![image](https://github.com/user-attachments/assets/77626b7a-cb46-48f8-9465-e03120aac66a)
![image](https://github.com/user-attachments/assets/79131a4e-75c6-4715-bd11-c6aaed887779)
[rec.webm](https://github.com/user-attachments/assets/a3984ed9-eb28-4d47-86c0-7fc3efc2b5d0)
</details>
<details>
<summary>v1.2.4: Node library sidebar tab</summary>
#### Drag & Drop
https://github.com/user-attachments/assets/853e20b7-bc0e-49c9-bbce-a2ba7566f92f
#### Filter
https://github.com/user-attachments/assets/4bbca3ee-318f-4cf0-be32-a5a5541066cf
</details>
<details>
<summary>v1.2.0: Queue/History sidebar tab</summary>
https://github.com/user-attachments/assets/86e264fe-4d26-4f07-aa9a-83bdd2d02b8f
</details>
<details>
<summary>v1.1.0: Node search box</summary>
#### Fuzzy search & Node preview
![image](https://github.com/user-attachments/assets/94733e32-ea4e-4a9c-b321-c1a05db48709)
#### Release link with shift
https://github.com/user-attachments/assets/a1b2b5c3-10d1-4256-b620-345de6858f25
</details>
### QoL changes
<details>
<summary>v1.3.32: **Litegraph** Nested group</summary>
https://github.com/user-attachments/assets/f51adeb1-028e-40af-81e4-0ac13075198a
</details>
<details>
<summary>v1.3.24: **Litegraph** Group selection</summary>
https://github.com/user-attachments/assets/e6230a94-411e-4fba-90cb-6c694200adaa
</details>
<details>
<summary>v1.3.6: **Litegraph** Toggle link visibility</summary>
[rec.webm](https://github.com/user-attachments/assets/34e460ac-fbbc-44ef-bfbb-99a84c2ae2be)
</details>
<details>
<summary>v1.3.4: **Litegraph** Auto widget to input conversion</summary>
Dropping a link of correct type on node widget will automatically convert the widget to input.
[rec.webm](https://github.com/user-attachments/assets/15cea0b0-b225-4bec-af50-2cdb16dc46bf)
</details>
<details>
<summary>v1.3.4: **Litegraph** Canvas pan mode</summary>
The canvas becomes readonly in pan mode. Pan mode is activated by clicking the pan mode button on the canvas menu
or by holding the space key.
[rec.webm](https://github.com/user-attachments/assets/c7872532-a2ac-44c1-9e7d-9e03b5d1a80b)
</details>
<details>
<summary>v1.3.1: **Litegraph** Shift drag link to create a new link</summary>
[rec.webm](https://github.com/user-attachments/assets/7e73aaf9-79e2-4c3c-a26a-911cba3b85e4)
</details>
<details>
<summary>v1.2.62: **Litegraph** Show optional input slots as donuts</summary>
![GYEIRidb0AYGO-v](https://github.com/user-attachments/assets/e6cde0b6-654b-4afd-a117-133657a410b1)
</details>
<details>
<summary>v1.2.44: **Litegraph** Double click group title to edit</summary>
https://github.com/user-attachments/assets/5bf0e2b6-8b3a-40a7-b44f-f0879e9ad26f
</details>
<details>
<summary>v1.2.39: **Litegraph** Group selected nodes with Ctrl + G</summary>
https://github.com/user-attachments/assets/7805dc54-0854-4a28-8bcd-4b007fa01151
</details>
<details>
<summary>v1.2.38: **Litegraph** Double click node title to edit</summary>
https://github.com/user-attachments/assets/d61d5d0e-f200-4153-b293-3e3f6a212b30
</details>
<details>
<summary>v1.2.7: **Litegraph** drags multiple links with shift pressed</summary>
https://github.com/user-attachments/assets/68826715-bb55-4b2a-be6e-675cfc424afe
https://github.com/user-attachments/assets/c142c43f-2fe9-4030-8196-b3bfd4c6977d
</details>
<details>
<summary>v1.2.2: **Litegraph** auto connects to correct slot</summary>
#### Before
https://github.com/user-attachments/assets/c253f778-82d5-4e6f-aec0-ea2ccf421651
#### After
https://github.com/user-attachments/assets/b6360ac0-f0d2-447c-9daa-8a2e20c0dc1d
</details>
<details>
<summary>v1.1.8: **Litegraph** hides text overflow on widget value</summary>
https://github.com/user-attachments/assets/5696a89d-4a47-4fcc-9e8c-71e1264943f2
</details>
### Developer APIs
<details>
<summary>v1.3.34: Register about panel badges</summary>
```js
app.registerExtension({
name: 'TestExtension1',
aboutPageBadges: [
{
label: 'Test Badge',
url: 'https://example.com',
icon: 'pi pi-box'
}
]
})
```
![image](https://github.com/user-attachments/assets/099e77ee-16ad-4141-b2fc-5e9d5075188b)
</details>
<details>
<summary>v1.3.22: Register bottom panel tabs</summary>
```js
app.registerExtension({
name: 'TestExtension',
bottomPanelTabs: [
{
id: 'TestTab',
title: 'Test Tab',
type: 'custom',
render: (el) => {
el.innerHTML = '<div>Custom tab</div>'
}
}
]
})
```
![image](https://github.com/user-attachments/assets/2114f8b8-2f55-414b-b027-78e61c870b64)
</details>
<details>
<summary>v1.3.22: New settings API</summary>
Legacy settings API.
```js
// Register a new setting
app.ui.settings.addSetting({
id: 'TestSetting',
name: 'Test Setting',
type: 'text',
defaultValue: 'Hello, world!'
})
// Get the value of a setting
const value = app.ui.settings.getSettingValue('TestSetting')
// Set the value of a setting
app.ui.settings.setSettingValue('TestSetting', 'Hello, universe!')
```
New settings API.
```js
// Register a new setting
app.registerExtension({
name: 'TestExtension1',
settings: [
{
id: 'TestSetting',
name: 'Test Setting',
type: 'text',
defaultValue: 'Hello, world!'
}
]
})
// Get the value of a setting
const value = app.extensionManager.setting.get('TestSetting')
// Set the value of a setting
app.extensionManager.setting.set('TestSetting', 'Hello, universe!')
```
</details>
<details>
<summary>v1.3.7: Register commands and keybindings</summary>
Extensions can call the following API to register commands and keybindings. Do
note that keybindings defined in core cannot be overwritten, and some keybindings
are reserved by the browser.
```js
app.registerExtension({
name: 'TestExtension1',
commands: [
{
id: 'TestCommand',
function: () => {
alert('TestCommand')
}
}
],
keybindings: [
{
combo: { key: 'k' },
commandId: 'TestCommand'
}
]
})
```
</details>
<details>
<summary>v1.3.1: Extension API to register custom topbar menu items</summary>
Extensions can call the following API to register custom topbar menu items.
```js
app.registerExtension({
name: 'TestExtension1',
commands: [
{
id: 'foo-id',
label: 'foo',
function: () => {
alert(1)
}
}
],
menuCommands: [
{
path: ['ext', 'ext2'],
commands: ['foo-id']
}
]
})
```
![image](https://github.com/user-attachments/assets/ae7b082f-7ce9-4549-a446-4563567102fe)
</details>
<details>
<summary>v1.2.27: Extension API to add toast message</summary>i
Extensions can call the following API to add toast messages.
```js
app.extensionManager.toast.add({
severity: 'info',
summary: 'Loaded!',
detail: 'Extension loaded!',
life: 3000
})
```
Documentation of all supported options can be found here: <https://primevue.org/toast/#api.toast.interfaces.ToastMessageOptions>
![image](https://github.com/user-attachments/assets/de02cd7e-cd81-43d1-a0b0-bccef92ff487)
</details>
<details>
<summary>v1.2.4: Extension API to register custom sidebar tab</summary>
Extensions now can call the following API to register a sidebar tab.
```js
app.extensionManager.registerSidebarTab({
id: "search",
icon: "pi pi-search",
title: "search",
tooltip: "search",
type: "custom",
render: (el) => {
el.innerHTML = "<div>Custom search tab</div>";
},
});
```
The list of supported icons can be found here: <https://primevue.org/icons/#list>
We will support custom icons later.
![image](https://github.com/user-attachments/assets/7bff028a-bf91-4cab-bf97-55c243b3f5e0)
</details>
## Development
### Tech Stack
- [Vue 3](https://vuejs.org/) with [TypeScript](https://www.typescriptlang.org/)
- [Pinia](https://pinia.vuejs.org/) for state management
- [PrimeVue](https://primevue.org/) with [TailwindCSS](https://tailwindcss.com/) for UI
- [Litegraph](https://github.com/Comfy-Org/litegraph.js) for node editor
- [zod](https://zod.dev/) for schema validation
### Git pre-commit hooks
Run `npm run prepare` to install Git pre-commit hooks. Currently, the pre-commit
@@ -55,11 +425,33 @@ hook is used to auto-format code on commit.
Note: The dev server will NOT load any extension from the ComfyUI server. Only
core extensions will be loaded.
- Run `npm install` to install the necessary packages
- Start local ComfyUI backend at `localhost:8188`
- Run `npm run dev` to start the dev server
- Run `npm run dev:electron` to start the dev server with electron API mocked
### Test
#### Access dev server on touch devices
Enable remote access to the dev server by setting `VITE_REMOTE_DEV` in `.env` to `true`.
After you start the dev server, you should see following logs:
```
> comfyui-frontend@1.3.42 dev
> vite
VITE v5.4.6 ready in 488 ms
➜ Local: http://localhost:5173/
➜ Network: http://172.21.80.1:5173/
➜ Network: http://192.168.2.20:5173/
➜ press h + enter to show help
```
Make sure your desktop machine and touch device are on the same network. On your touch device,
navigate to `http://<server_ip>:5173` (e.g. `http://192.168.2.20:5173` here), to access the ComfyUI frontend.
### Unit Test
- `git clone https://github.com/comfyanonymous/ComfyUI_examples.git` to `tests-ui/ComfyUI_examples` or the EXAMPLE_REPO_PATH location specified in .env
- `npm i` to install all dependencies
@@ -67,11 +459,35 @@ core extensions will be loaded.
- `npm run test:generate:examples` to extract the example workflows
- `npm run test` to execute all unit tests.
### Component Test
Component test verifies Vue components in `src/components/`.
- `npm run test:component` to execute all component tests.
### Playwright Test
Playwright test verifies the whole app. See <https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/browser_tests/README.md> for details.
### LiteGraph
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.
### Test litegraph changes
- Run `npm link` in the local litegraph repo.
- Run `npm link @comfyorg/litegraph` in this repo.
This will replace the litegraph package in this repo with the local litegraph repo.
## Deploy
- Option 1: Set `DEPLOY_COMFYUI_DIR` in `.env` and run `npm run deploy`.
- Option 2: Copy everything under `dist/` to `ComfyUI/web/` in your ComfyUI checkout manually.
## Breaking changes
## Publish release to ComfyUI main repo
- api.api_url now adds a prefix `api/` to every url going through the method. If the custom node registers a new api endpoint but does not offer the `api/` prefixed alt endpoint, it will have issue. Luckily there aren't many extensions that do that. We can perform an audit before launching to resolve this issue.
Run following command to publish a release to ComfyUI main repo. The script will create a new branch and do a commit to `web/` folder by checkout `dist.zip`
from GitHub release.
- `python scripts/main_repo_release.py <path_to_comfyui_main_repo> <version>`

View File

@@ -1,311 +0,0 @@
import type { Page, Locator } from "@playwright/test";
import { test as base } from "@playwright/test";
import dotenv from "dotenv";
dotenv.config();
interface Position {
x: number;
y: number;
}
interface Size {
width: number;
height: number;
}
class ComfyNodeSearchBox {
public readonly input: Locator;
public readonly dropdown: Locator;
constructor(public readonly page: Page) {
this.input = page.locator(
'.comfy-vue-node-search-container input[type="text"]'
);
this.dropdown = page.locator(
".comfy-vue-node-search-container .p-autocomplete-list"
);
}
async fillAndSelectFirstNode(nodeName: string) {
await this.input.waitFor({ state: "visible" });
await this.input.fill(nodeName);
await this.dropdown.waitFor({ state: "visible" });
await this.dropdown.locator("li").nth(0).click();
}
}
export class ComfyPage {
public readonly url: string;
// All canvas position operations are based on default view of canvas.
public readonly canvas: Locator;
public readonly widgetTextBox: Locator;
// Buttons
public readonly resetViewButton: Locator;
// Search box
public readonly searchBox: ComfyNodeSearchBox;
constructor(public readonly page: Page) {
this.url = process.env.PLAYWRIGHT_TEST_URL || "http://localhost:8188";
this.canvas = page.locator("#graph-canvas");
this.widgetTextBox = page.getByPlaceholder("text").nth(1);
this.resetViewButton = page.getByRole("button", { name: "Reset View" });
this.searchBox = new ComfyNodeSearchBox(page);
}
async goto() {
await this.page.goto(this.url);
}
async nextFrame() {
await this.page.evaluate(() => {
return new Promise<number>(requestAnimationFrame);
});
}
async resetView() {
await this.resetViewButton.click();
// Avoid "Reset View" button highlight.
await this.page.mouse.move(10, 10);
await this.nextFrame();
}
async clickTextEncodeNode1() {
await this.canvas.click({
position: {
x: 618,
y: 191,
},
});
await this.nextFrame();
}
async clickTextEncodeNode2() {
await this.canvas.click({
position: {
x: 622,
y: 400,
},
});
await this.nextFrame();
}
async clickEmptySpace() {
await this.canvas.click({
position: {
x: 35,
y: 31,
},
});
await this.nextFrame();
}
async dragAndDrop(source: Position, target: Position) {
await this.page.mouse.move(source.x, source.y);
await this.page.mouse.down();
await this.page.mouse.move(target.x, target.y);
await this.page.mouse.up();
await this.nextFrame();
}
async dragNode2() {
await this.dragAndDrop({ x: 622, y: 400 }, { x: 622, y: 300 });
await this.nextFrame();
}
async disconnectEdge() {
// CLIP input anchor
await this.page.mouse.move(427, 198);
await this.page.mouse.down();
await this.page.mouse.move(427, 98);
await this.page.mouse.up();
// Move out the way to avoid highlight of menu item.
await this.page.mouse.move(10, 10);
await this.nextFrame();
}
async connectEdge() {
// CLIP output anchor on Load Checkpoint Node.
await this.page.mouse.move(332, 509);
await this.page.mouse.down();
// CLIP input anchor on CLIP Text Encode Node.
await this.page.mouse.move(427, 198);
await this.page.mouse.up();
await this.nextFrame();
}
async adjustWidgetValue() {
// Adjust Empty Latent Image's width input.
const page = this.page;
await page.locator("#graph-canvas").click({
position: {
x: 724,
y: 645,
},
});
await page.locator('input[type="text"]').click();
await page.locator('input[type="text"]').fill("128");
await page.locator('input[type="text"]').press("Enter");
await this.nextFrame();
}
async zoom(deltaY: number) {
await this.page.mouse.move(10, 10);
await this.page.mouse.wheel(0, deltaY);
await this.nextFrame();
}
async pan(offset: Position) {
await this.page.mouse.move(10, 10);
await this.page.mouse.down();
await this.page.mouse.move(offset.x, offset.y);
await this.page.mouse.up();
await this.nextFrame();
}
async rightClickCanvas() {
await this.page.mouse.click(10, 10, { button: "right" });
await this.nextFrame();
}
async doubleClickCanvas() {
await this.page.mouse.dblclick(10, 10);
await this.nextFrame();
}
async clickEmptyLatentNode() {
await this.canvas.click({
position: {
x: 724,
y: 625,
},
});
this.page.mouse.move(10, 10);
await this.nextFrame();
}
async rightClickEmptyLatentNode() {
await this.canvas.click({
position: {
x: 724,
y: 645,
},
button: "right",
});
this.page.mouse.move(10, 10);
await this.nextFrame();
}
async select2Nodes() {
// Select 2 CLIP nodes.
await this.page.keyboard.down("Control");
await this.clickTextEncodeNode1();
await this.clickTextEncodeNode2();
await this.page.keyboard.up("Control");
await this.nextFrame();
}
async ctrlC() {
await this.page.keyboard.down("Control");
await this.page.keyboard.press("KeyC");
await this.page.keyboard.up("Control");
await this.nextFrame();
}
async ctrlV() {
await this.page.keyboard.down("Control");
await this.page.keyboard.press("KeyV");
await this.page.keyboard.up("Control");
await this.nextFrame();
}
async closeMenu() {
await this.page.click("button.comfy-close-menu-btn");
await this.nextFrame();
}
async resizeNode(nodePos: Position, nodeSize: Size, ratioX: number, ratioY: number, revertAfter: boolean = false) {
const bottomRight = {
x: nodePos.x + nodeSize.width,
y: nodePos.y + nodeSize.height,
}
const target = {
x: nodePos.x + nodeSize.width * ratioX,
y: nodePos.y + nodeSize.height * ratioY,
}
await this.dragAndDrop(bottomRight, target);
await this.nextFrame();
if (revertAfter) {
await this.dragAndDrop(target, bottomRight);
await this.nextFrame();
}
}
async resizeKsamplerNode(percentX: number, percentY: number, revertAfter: boolean = false) {
const ksamplerPos = {
x: 864,
y: 157
}
const ksamplerSize = {
width: 315,
height: 292,
}
this.resizeNode(ksamplerPos, ksamplerSize, percentX, percentY, revertAfter);
}
async resizeLoadCheckpointNode(percentX: number, percentY: number, revertAfter: boolean = false) {
const loadCheckpointPos = {
x: 25,
y: 440,
}
const loadCheckpointSize = {
width: 320,
height: 120,
}
this.resizeNode(loadCheckpointPos, loadCheckpointSize, percentX, percentY, revertAfter);
}
async resizeEmptyLatentNode(percentX: number, percentY: number, revertAfter: boolean = false) {
const emptyLatentPos = {
x: 475,
y: 580,
}
const emptyLatentSize = {
width: 303,
height: 132,
}
this.resizeNode(emptyLatentPos, emptyLatentSize, percentX, percentY, revertAfter);
}
}
export const comfyPageFixture = base.extend<{ comfyPage: ComfyPage }>({
comfyPage: async ({ page }, use) => {
const comfyPage = new ComfyPage(page);
await comfyPage.goto();
// Unify font for consistent screenshots.
await page.addStyleTag({
url: "https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap",
});
await page.addStyleTag({
url: "https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap",
});
await page.addStyleTag({
content: `
* {
font-family: 'Roboto Mono', 'Noto Color Emoji';
}`,
});
await page.waitForFunction(() => document.fonts.ready);
await page.waitForFunction(() => window["app"] !== undefined);
await page.evaluate(() => {
window["app"]["canvas"].show_info = false;
});
await comfyPage.nextFrame();
// Reset view to force re-rendering of canvas. So that info fields like fps
// become hidden.
await comfyPage.resetView();
await use(comfyPage);
},
});

49
browser_tests/README.md Normal file
View File

@@ -0,0 +1,49 @@
# Playwright Testing for ComfyUI_frontend
This document outlines the setup and usage of Playwright for testing the ComfyUI_frontend project.
## WARNING
The browser tests will change the ComfyUI backend state, such as user settings and saved workflows.
If `TEST_COMFYUI_DIR` in `.env` isn't set to your `(Comfy Path)/ComfyUI` directory, these changes won't be automatically restored.
## Setup
Clone <https://github.com/Comfy-Org/ComfyUI_devtools> to your `custom_nodes` directory.
ComfyUI_devtools adds additional API endpoints and nodes to ComfyUI for browser testing.
Ensure you have Node.js v20 or later installed. Then, set up the Chromium test driver:
```bash
npx playwright install chromium --with-deps
```
## Running Tests
There are two ways to run the tests:
1. **Headless mode with report generation:**
```bash
npx playwright test
```
This runs all tests without a visible browser and generates a comprehensive test report.
2. **UI mode for interactive testing:**
```bash
npx playwright test --ui
```
This opens a user interface where you can select specific tests to run and inspect the test execution timeline.
![Playwright UI Mode](https://github.com/user-attachments/assets/6a1ebef0-90eb-4157-8694-f5ee94d03755)
## Screenshot Expectations
Due to variations in system font rendering, screenshot expectations are platform-specific. Please note:
- We maintain Linux screenshot expectations as our GitHub Action runner operates in a Linux environment.
- To set new test expectations:
1. Create a pull request from a `Comfy-Org/ComfyUI_frontend` branch.
2. Add the `New Browser Test Expectation` tag to your pull request.
3. This will trigger a GitHub action to update the screenshot expectations automatically.
> **Note:** If you're making a pull request from a forked repository, the GitHub action won't be able to commit updated screenshot expectations directly to your PR branch.

View File

@@ -0,0 +1,125 @@
import type { Response } from '@playwright/test'
import type { StatusWsMessage } from '../src/types/apiTypes.ts'
import { expect, mergeTests } from '@playwright/test'
import { comfyPageFixture } from './fixtures/ComfyPage'
import { webSocketFixture } from './fixtures/ws.ts'
const test = mergeTests(comfyPageFixture, webSocketFixture)
test.describe('Actionbar', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
})
/**
* This test ensures that the autoqueue change mode can only queue one change at a time
*/
test('Does not auto-queue multiple changes at a time', async ({
comfyPage,
ws
}) => {
// Enable change auto-queue mode
const queueOpts = await comfyPage.actionbar.queueButton.toggleOptions()
expect(await queueOpts.getMode()).toBe('disabled')
await queueOpts.setMode('change')
await comfyPage.nextFrame()
expect(await queueOpts.getMode()).toBe('change')
await comfyPage.actionbar.queueButton.toggleOptions()
// Intercept the prompt queue endpoint
let promptNumber = 0
comfyPage.page.route('**/api/prompt', async (route, req) => {
await new Promise((r) => setTimeout(r, 100))
route.fulfill({
status: 200,
body: JSON.stringify({
prompt_id: promptNumber,
number: ++promptNumber,
node_errors: {},
// Include the request data to validate which prompt was queued so we can validate the width
__request: req.postDataJSON()
})
})
})
// Start watching for a message to prompt
const requestPromise = comfyPage.page.waitForResponse('**/api/prompt')
// Find and set the width on the latent node
const triggerChange = async (value: number) => {
return await comfyPage.page.evaluate((value) => {
const node = window['app'].graph._nodes.find(
(n) => n.type === 'EmptyLatentImage'
)
node.widgets[0].value = value
window[
'app'
].extensionManager.workflow.activeWorkflow.changeTracker.checkState()
}, value)
}
// Trigger a status websocket message
const triggerStatus = async (queueSize: number) => {
await ws.trigger({
type: 'status',
data: {
status: {
exec_info: {
queue_remaining: queueSize
}
}
}
} as StatusWsMessage)
}
// Extract the width from the queue response
const getQueuedWidth = async (resp: Promise<Response>) => {
const obj = await (await resp).json()
return obj['__request']['prompt']['5']['inputs']['width']
}
// Trigger a bunch of changes
const START = 32
const END = 64
for (let i = START; i <= END; i += 8) {
await triggerChange(i)
}
// Ensure the queued width is the first value
expect(
await getQueuedWidth(requestPromise),
'the first queued prompt should be the first change width'
).toBe(START)
// Ensure that no other changes are queued
await expect(
comfyPage.page.waitForResponse('**/api/prompt', { timeout: 250 })
).rejects.toThrow()
expect(
promptNumber,
'only 1 prompt should have been queued even though there were multiple changes'
).toBe(1)
// Trigger a status update so auto-queue re-runs
await triggerStatus(1)
await triggerStatus(0)
// Ensure the queued width is the last queued value
expect(
await getQueuedWidth(comfyPage.page.waitForResponse('**/api/prompt')),
'last queued prompt width should be the last change'
).toBe(END)
expect(promptNumber, 'queued prompt count should be 2').toBe(2)
})
test('Can dock actionbar into top menu', async ({ comfyPage }) => {
await comfyPage.page.dragAndDrop(
'.actionbar .drag-handle',
'.comfyui-menu',
{
targetPosition: { x: 0, y: 0 }
}
)
expect(await comfyPage.actionbar.isDocked()).toBe(true)
})
})

View File

@@ -0,0 +1,193 @@
{
"last_node_id": 10,
"last_link_id": 9,
"nodes": [
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
0,
92
],
"size": {
"0": 315,
"1": 98
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"3Guofeng3_v32Light.safetensors"
]
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
460,
92
],
"size": {
"0": 422.84503173828125,
"1": 164.31304931640625
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
460,
368
],
"size": {
"0": 425.27801513671875,
"1": 180.6060791015625
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 10,
"type": "CheckpointLoaderSimple",
"pos": [
0,
276
],
"size": {
"0": 315,
"1": 98
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"3Guofeng3_v32Light.safetensors"
]
}
],
"links": [
[
3,
4,
1,
6,
0,
"CLIP"
],
[
5,
4,
1,
7,
0,
"CLIP"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,135 @@
{
"last_node_id": 9,
"last_link_id": 9,
"nodes": [
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [413, 389],
"size": [425.27801513671875, 180.6060791015625],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [{ "name": "clip", "type": "CLIP", "link": 5 }],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [6],
"slot_index": 0
}
],
"properties": {},
"widgets_values": ["text, watermark"]
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [415, 186],
"size": [422.84503173828125, 164.31304931640625],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [{ "name": "clip", "type": "CLIP", "link": 3 }],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [4],
"slot_index": 0
}
],
"properties": {},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [473, 609],
"size": [315, 106],
"flags": {},
"order": 1,
"mode": 0,
"outputs": [{ "name": "LATENT", "type": "LATENT", "links": [2], "slot_index": 0 }],
"properties": {},
"widgets_values": [512, 512, 1]
},
{
"id": 3,
"type": "KSampler",
"pos": [863, 186],
"size": [315, 262],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{ "name": "model", "type": "MODEL", "link": 1 },
{ "name": "positive", "type": "CONDITIONING", "link": 4 },
{ "name": "negative", "type": "CONDITIONING", "link": 6 },
{ "name": "latent_image", "type": "LATENT", "link": 2 }
],
"outputs": [{ "name": "LATENT", "type": "LATENT", "links": [7], "slot_index": 0 }],
"properties": {},
"widgets_values": [156680208700286, true, 20, 8, "euler", "normal", 1]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [1209, 188],
"size": [210, 46],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{ "name": "samples", "type": "LATENT", "link": 7 },
{ "name": "vae", "type": "VAE", "link": 8 }
],
"outputs": [{ "name": "IMAGE", "type": "IMAGE", "links": [9], "slot_index": 0 }],
"properties": {}
},
{
"id": 9,
"type": "SaveImage",
"pos": [1451, 189],
"size": [210, 26],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [{ "name": "images", "type": "IMAGE", "link": 9 }],
"properties": {}
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [26, 474],
"size": [315, 98],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [1], "slot_index": 0 },
{ "name": "CLIP", "type": "CLIP", "links": [3, 5], "slot_index": 1 },
{ "name": "VAE", "type": "VAE", "links": [8], "slot_index": 2 }
],
"properties": {},
"widgets_values": ["v1-5-pruned-emaonly.ckpt"]
}
],
"links": [
[1, 4, 0, 3, 0, "MODEL"],
[2, 5, 0, 3, 3, "LATENT"],
[3, 4, 1, 6, 0, "CLIP"],
[4, 6, 0, 3, 1, "CONDITIONING"],
[5, 4, 1, 7, 0, "CLIP"],
[6, 7, 0, 3, 2, "CONDITIONING"],
[7, 3, 0, 8, 0, "LATENT"],
[8, 4, 2, 8, 1, "VAE"],
[9, 8, 0, 9, 0, "IMAGE"]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View File

@@ -0,0 +1,504 @@
{
"last_node_id": 13,
"last_link_id": 9,
"nodes": [
{
"id": 3,
"type": "KSampler",
"pos": {
"0": 863,
"1": 186
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": {
"0": 36,
"1": 172
},
"size": {
"0": 315,
"1": 98
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"Stable-diffusion/v1-5-pruned-emaonly.safetensors"
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": {
"0": 473,
"1": 609
},
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
],
"color": "#323",
"bgcolor": "#535"
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": {
"0": 415,
"1": 186
},
"size": {
"0": 422.84503173828125,
"1": 164.31304931640625
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
],
"color": "#233",
"bgcolor": "#355"
},
{
"id": 7,
"type": "CLIPTextEncode",
"pos": {
"0": 413,
"1": 389
},
"size": {
"0": 425.27801513671875,
"1": 180.6060791015625
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
],
"color": "#323",
"bgcolor": "#535"
},
{
"id": 8,
"type": "VAEDecode",
"pos": {
"0": 866,
"1": 502
},
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
9
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"widgets_values": [],
"color": "#222",
"bgcolor": "#000"
},
{
"id": 9,
"type": "SaveImage",
"pos": {
"0": 857,
"1": 611
},
"size": [
214.2000732421875,
59.4000244140625
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 9
}
],
"outputs": [],
"properties": {},
"widgets_values": [
"ComfyUI"
]
},
{
"id": 10,
"type": "CheckpointLoaderSimple",
"pos": {
"0": 42,
"1": 329
},
"size": {
"0": 315,
"1": 98
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": null
},
{
"name": "CLIP",
"type": "CLIP",
"links": null
},
{
"name": "VAE",
"type": "VAE",
"links": null
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"Stable-diffusion/v1-5-pruned-emaonly.safetensors"
],
"color": "#332922",
"bgcolor": "#593930"
},
{
"id": 11,
"type": "CheckpointLoaderSimple",
"pos": {
"0": 40,
"1": 494
},
"size": {
"0": 315,
"1": 98
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": null
},
{
"name": "CLIP",
"type": "CLIP",
"links": null
},
{
"name": "VAE",
"type": "VAE",
"links": null
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"Stable-diffusion/v1-5-pruned-emaonly.safetensors"
],
"color": "#223",
"bgcolor": "#335"
},
{
"id": 13,
"type": "ImageScale",
"pos": {
"0": 42,
"1": 650
},
"size": {
"0": 315,
"1": 130
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": null
}
],
"properties": {
"Node name for S&R": "ImageScale"
},
"widgets_values": [
"nearest-exact",
512,
512,
"disabled"
],
"color": "#2a363b",
"bgcolor": "#3f5159"
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
9,
8,
0,
9,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}

View File

@@ -0,0 +1,84 @@
{
"last_node_id": 17,
"last_link_id": 15,
"nodes": [
{
"id": 14,
"type": "PreviewImage",
"pos": {
"0": 300,
"1": 60
},
"size": {
"0": 213.8594970703125,
"1": 50.65289306640625
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 15
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 17,
"type": "DevToolsErrorRaiseNode",
"pos": {
"0": 20,
"1": 60
},
"size": {
"0": 210,
"1": 26
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
15
],
"slot_index": 0,
"shape": 3
}
],
"properties": {
"Node name for S&R": "DevToolsErrorRaiseNode"
}
}
],
"links": [
[
15,
17,
0,
14,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
117.20766722169206,
472.69035116826046
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,62 @@
{
"last_node_id": 5,
"last_link_id": 0,
"nodes": [
{
"id": 5,
"type": "DevToolsNodeWithForceInput",
"pos": {
"0": 9,
"1": 39
},
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "int_input",
"type": "INT",
"link": null,
"widget": {
"name": "int_input"
}
},
{
"name": "float_input",
"type": "FLOAT",
"link": null,
"widget": {
"name": "float_input"
},
"shape": 7
}
],
"outputs": [],
"properties": {
"Node name for S&R": "DevToolsNodeWithForceInput"
},
"widgets_values": [
0,
1,
0
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,404 @@
{
"last_node_id": 10,
"last_link_id": 9,
"nodes": [
{
"id": 10,
"type": "workflow>group_node",
"pos": {
"0": 26,
"1": 186
},
"size": {
"0": 400,
"1": 390
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {
"Node name for S&R": "workflow>group_node"
},
"widgets_values": [
512,
512,
1,
"v1-5-pruned-emaonly.ckpt",
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,",
"text, watermark",
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1,
"ComfyUI"
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"groupNodes": {
"group_node": {
"nodes": [
{
"id": -1,
"type": "EmptyLatentImage",
"pos": {
"0": 473,
"1": 609
},
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
],
"index": 0
},
{
"id": -1,
"type": "CheckpointLoaderSimple",
"pos": {
"0": 26,
"1": 474
},
"size": {
"0": 315,
"1": 98
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"v1-5-pruned-emaonly.ckpt"
],
"index": 1
},
{
"id": -1,
"type": "CLIPTextEncode",
"pos": {
"0": 415,
"1": 186
},
"size": {
"0": 422.84503173828125,
"1": 164.31304931640625
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": null
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
],
"index": 2
},
{
"id": -1,
"type": "CLIPTextEncode",
"pos": {
"0": 413,
"1": 389
},
"size": {
"0": 425.27801513671875,
"1": 180.6060791015625
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": null
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
],
"index": 3
},
{
"id": -1,
"type": "KSampler",
"pos": {
"0": 863,
"1": 186
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
],
"index": 4
},
{
"id": -1,
"type": "VAEDecode",
"pos": {
"0": 1209,
"1": 188
},
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": null
},
{
"name": "vae",
"type": "VAE",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"index": 5
},
{
"id": -1,
"type": "SaveImage",
"pos": {
"0": 1451,
"1": 189
},
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": null
}
],
"outputs": [],
"properties": {},
"widgets_values": [
"ComfyUI"
],
"index": 6
}
],
"links": [
[
1,
1,
2,
0,
4,
"CLIP"
],
[
1,
1,
3,
0,
4,
"CLIP"
],
[
1,
0,
4,
0,
4,
"MODEL"
],
[
2,
0,
4,
1,
6,
"CONDITIONING"
],
[
3,
0,
4,
2,
7,
"CONDITIONING"
],
[
0,
0,
4,
3,
5,
"LATENT"
],
[
4,
0,
5,
0,
3,
"LATENT"
],
[
1,
2,
5,
1,
4,
"VAE"
],
[
5,
0,
6,
0,
8,
"IMAGE"
]
],
"external": []
}
}
},
"version": 0.4
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

View File

@@ -0,0 +1,252 @@
{
"last_node_id": 15,
"last_link_id": 9,
"nodes": [
{
"id": 15,
"type": "workflow/hello",
"pos": {
"0": 566,
"1": 316
},
"size": {
"0": 468.5999755859375,
"1": 582
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null,
"label": "model"
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null,
"label": "positive"
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null,
"label": "negative"
},
{
"name": "latent_image",
"type": "LATENT",
"link": null,
"label": "latent_image"
},
{
"name": "KSampler model",
"type": "MODEL",
"link": null,
"label": "KSampler model"
},
{
"name": "KSampler positive",
"type": "CONDITIONING",
"link": null,
"label": "KSampler positive"
},
{
"name": "KSampler negative",
"type": "CONDITIONING",
"link": null,
"label": "KSampler negative"
},
{
"name": "KSampler latent_image",
"type": "LATENT",
"link": null,
"label": "KSampler latent_image"
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3,
"label": "LATENT"
},
{
"name": "KSampler LATENT",
"type": "LATENT",
"links": null,
"shape": 3,
"label": "KSampler LATENT"
}
],
"properties": {
"Node name for S&R": "workflow/hello"
},
"widgets_values": [
"enable",
0,
"randomize",
20,
8,
"euler",
"normal",
0,
10000,
"disable",
0,
"randomize",
20,
8,
"euler",
"normal",
1
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"groupNodes": {
"hello": {
"nodes": [
{
"id": -1,
"type": "KSamplerAdvanced",
"pos": {
"0": 351.3332824707031,
"1": 577.3333129882812
},
"size": {
"0": 315,
"1": 334
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null,
"label": "model"
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null,
"label": "positive"
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null,
"label": "negative"
},
{
"name": "latent_image",
"type": "LATENT",
"link": null,
"label": "latent_image"
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3,
"label": "LATENT"
}
],
"properties": {
"Node name for S&R": "KSamplerAdvanced"
},
"widgets_values": [
"enable",
0,
"randomize",
20,
8,
"euler",
"normal",
0,
10000,
"disable"
],
"index": 0
},
{
"id": -1,
"type": "KSampler",
"pos": {
"0": 636,
"1": 427
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null,
"label": "model"
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null,
"label": "positive"
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null,
"label": "negative"
},
{
"name": "latent_image",
"type": "LATENT",
"link": null,
"label": "latent_image"
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3,
"label": "LATENT"
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"randomize",
20,
8,
"euler",
"normal",
1
],
"index": 1
}
],
"links": [],
"external": []
}
}
},
"version": 0.4
}

View File

@@ -0,0 +1,25 @@
{
"last_node_id": 0,
"last_link_id": 0,
"nodes": [],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"models": [
{
"name": "fake_model.safetensors",
"url": "http://localhost:8188/api/devtools/fake_model.safetensors",
"directory": "clip"
}
],
"version": 0.4
}

View File

@@ -0,0 +1,61 @@
{
"last_node_id": 1,
"last_link_id": 0,
"nodes": [
{
"id": 1,
"type": "UNKNOWN NODE",
"pos": [
48,
86
],
"size": {
"0": 358.80780029296875,
"1": 314.7989501953125
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": null,
"slot_index": 0
}
],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [],
"slot_index": 0,
"shape": 6
}
],
"properties": {
"Node name for S&R": "UNKNOWN NODE"
},
"widgets_values": [
"wd-v1-4-moat-tagger-v2",
0.35,
0.85,
false,
false,
""
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0, 0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,227 @@
{
"last_node_id": 3,
"last_link_id": 0,
"nodes": [
{
"id": 2,
"type": "KSampler",
"pos": {
"0": 420,
"1": 130
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 3,
"type": "KSampler",
"pos": {
"0": 820,
"1": 130
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 1,
"type": "KSampler",
"pos": {
"0": 30,
"1": 130
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"randomize",
20,
8,
"euler",
"normal",
1
]
}
],
"links": [],
"groups": [
{
"id": 0,
"title": "Group",
"bounding": [
406.9701232910156,
59.079444885253906,
335,
345.6000061035156
],
"color": "#3f789e",
"font_size": 24,
"flags": {}
},
{
"id": 3,
"title": "Group Parent",
"bounding": [
796.9703979492188,
14.796443939208984,
355,
399.20001220703125
],
"color": "#3f789e",
"font_size": 24,
"flags": {}
},
{
"id": 2,
"title": "Group Child",
"bounding": [
806.9703979492188,
58.39643096923828,
335,
345.6000061035156
],
"color": "#3f789e",
"font_size": 24,
"flags": {}
}
],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

View File

@@ -0,0 +1,128 @@
{
"last_node_id": 2,
"last_link_id": 1,
"nodes": [
{
"id": 1,
"type": "ControlNetApplyAdvanced",
"pos": {
"0": 449,
"1": 204
},
"size": [
340.20001220703125,
166
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "control_net",
"type": "CONTROL_NET",
"link": null
},
{
"name": "image",
"type": "IMAGE",
"link": null
},
{
"name": "strength",
"type": "FLOAT",
"link": 1,
"widget": {
"name": "strength"
}
}
],
"outputs": [
{
"name": "positive",
"type": "CONDITIONING",
"links": null
},
{
"name": "negative",
"type": "CONDITIONING",
"links": null
}
],
"properties": {
"Node name for S&R": "ControlNetApplyAdvanced"
},
"widgets_values": [
1,
0,
1
]
},
{
"id": 2,
"type": "PrimitiveNode",
"pos": {
"0": 177,
"1": 265
},
"size": [
210,
82
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "FLOAT",
"type": "FLOAT",
"links": [
1
],
"widget": {
"name": "strength"
}
}
],
"properties": {
"Run widget replace on values": false
},
"widgets_values": [
1,
"fixed"
]
}
],
"links": [
[
1,
2,
0,
1,
4,
"FLOAT"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": {
"0": 47.541666666666515,
"1": 186.9375
}
}
},
"version": 0.4
}

View File

@@ -0,0 +1,44 @@
{
"last_node_id": 11,
"last_link_id": 9,
"nodes": [
{
"id": 11,
"type": "DevToolsNodeWithOnlyOptionalInput",
"pos": {
"0": 150,
"1": 464.2916564941406
},
"size": {
"0": 400,
"1": 200
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": null,
"shape": 7,
"label": "clip"
}
],
"outputs": [],
"properties": {
"Node name for S&R": "DevToolsNodeWithOnlyOptionalInput"
},
"widgets_values": [
""
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"groupNodes": {}
},
"version": 0.4
}

View File

@@ -0,0 +1,57 @@
{
"last_node_id": 5,
"last_link_id": 0,
"nodes": [
{
"id": 5,
"type": "DevToolsNodeWithOptionalInput",
"pos": {
"0": 19,
"1": 46
},
"size": {
"0": 302.4000244140625,
"1": 46
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "required_input",
"type": "IMAGE",
"link": null
},
{
"name": "optional_input",
"type": "IMAGE",
"link": null,
"shape": 7
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": null
}
],
"properties": {
"Node name for S&R": "DevToolsNodeWithOptionalInput"
}
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,56 @@
{
"last_node_id": 5,
"last_link_id": 0,
"nodes": [
{
"id": 5,
"type": "DevToolsNodeWithOptionalInput",
"pos": {
"0": 19,
"1": 46
},
"size": {
"0": 302.4000244140625,
"1": 46
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "required_input",
"type": "IMAGE",
"link": null
},
{
"name": "optional_input",
"type": "IMAGE",
"link": null
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": null
}
],
"properties": {
"Node name for S&R": "DevToolsNodeWithOptionalInput"
}
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,57 @@
{
"last_node_id": 5,
"last_link_id": 0,
"nodes": [
{
"id": 5,
"type": "DevToolsNodeWithOptionalInput",
"pos": {
"0": 19,
"1": 46
},
"size": {
"0": 302.4000244140625,
"1": 46
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "required_input",
"type": "IMAGE",
"link": null
},
{
"name": "optional_input",
"type": "IMAGE",
"link": null,
"shape": 6
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": null
}
],
"properties": {
"Node name for S&R": "DevToolsNodeWithOptionalInput"
}
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,90 @@
{
"last_node_id": 9,
"last_link_id": 9,
"nodes": [
{
"id": 3,
"type": "KSampler",
"pos": [
37,
98
],
"size": [
315,
262
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
}
],
"links": [],
"groups": [
{
"id": 1,
"title": "Group",
"bounding": [
23,
23,
900,
825
],
"color": "#3f789e",
"font_size": 24,
"flags": {}
}
],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,145 @@
{
"last_node_id": 2,
"last_link_id": 1,
"nodes": [
{
"id": 2,
"type": "KSampler",
"pos": {
"0": 521.0906982421875,
"1": 40.999996185302734,
"2": 0,
"3": 0,
"4": 0,
"5": 0,
"6": 0,
"7": 0,
"8": 0,
"9": 0
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
},
{
"name": "steps",
"type": "INT",
"link": 1,
"widget": {
"name": "steps"
}
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 1,
"type": "PrimitiveNode",
"pos": {
"0": 15,
"1": 46,
"2": 0,
"3": 0,
"4": 0,
"5": 0,
"6": 0,
"7": 0,
"8": 0,
"9": 0
},
"size": [
446.96645387135936,
108.34243389566905
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "INT",
"type": "INT",
"links": [
1
],
"slot_index": 0,
"widget": {
"name": "steps"
}
}
],
"properties": {
"Run widget replace on values": false
},
"widgets_values": [
20,
"fixed"
]
}
],
"links": [
[
1,
1,
0,
2,
4,
"INT"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,104 @@
{
"last_node_id": 2,
"last_link_id": 1,
"nodes": [
{
"id": 2,
"type": "KSampler",
"pos": {
"0": 304.3653259277344,
"1": 42.15586471557617
},
"size": [
315,
262
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 1,
"type": "PrimitiveNode",
"pos": {
"0": 14,
"1": 43
},
"size": [
203.1999969482422,
40.368401303242536
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "connect to widget input",
"type": "*",
"links": [],
"slot_index": 0
}
],
"properties": {
"Run widget replace on values": false
},
"widgets_values": []
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,88 @@
{
"last_node_id": 9,
"last_link_id": 13,
"nodes": [
{
"id": 3,
"type": "KSampler",
"pos": {
"0": 10.321063995361328,
"1": 73.14462280273438
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
}
],
"links": [],
"groups": [
{
"title": "Group",
"bounding": [
0,
0,
335,
346
],
"color": "#3f789e",
"font_size": 24
}
],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,30 @@
{
"last_node_id": 9,
"last_link_id": 13,
"nodes": [],
"links": [],
"groups": [
{
"title": "Group",
"bounding": [
0,
0,
335,
346
],
"color": "#3f789e",
"font_size": 24
}
],
"config": {},
"extra": {
"ds": {
"scale": 1.2100000000000006,
"offset": [
104.34159172650945,
241.35965953210126
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,76 @@
{
"last_node_id": 9,
"last_link_id": 13,
"nodes": [
{
"id": 3,
"type": "KSampler",
"pos": [
0,
30
],
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,46 @@
{
"last_node_id": 9,
"last_link_id": 9,
"nodes": [
{
"id": 9,
"type": "SaveImage",
"pos": {
"0": 64,
"1": 104
},
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": null
}
],
"outputs": [],
"properties": {},
"widgets_values": [
"ComfyUI"
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,164 @@
{
"last_node_id": 5,
"last_link_id": 5,
"nodes": [
{
"id": 1,
"type": "KSampler",
"pos": [
590,
40
],
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null,
"slot_index": 0
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 3,
"slot_index": 1
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null,
"slot_index": 2
},
{
"name": "latent_image",
"type": "LATENT",
"link": null,
"slot_index": 3
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 4,
"type": "CLIPTextEncode",
"pos": [
20,
50
],
"size": {
"0": 400,
"1": 200
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": null
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
3
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
""
]
},
{
"id": 5,
"type": "CLIPTextEncode",
"pos": [
20,
320
],
"size": {
"0": 400,
"1": 200
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": null
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
""
]
}
],
"links": [
[
3,
4,
0,
1,
1,
"CONDITIONING"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,42 @@
{
"last_node_id": 1,
"last_link_id": 0,
"nodes": [
{
"id": 1,
"type": "DevToolsNodeWithStringInput",
"pos": [
15,
48
],
"size": [
315,
58
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {
"Node name for S&R": "DevToolsNodeWithStringInput"
},
"widgets_values": [
""
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,377 @@
{
"last_node_id": 0,
"last_link_id": 18,
"nodes": [
{
"id": "CheckpointLoaderSimple.0",
"type": "CheckpointLoaderSimple",
"pos": {
"0": 100,
"1": 130
},
"size": {
"0": 315,
"1": 98
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
12
],
"shape": 3
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
10,
11
],
"shape": 3
},
{
"name": "VAE",
"type": "VAE",
"links": [
17
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"v1-5-pruned-emaonly.ckpt"
]
},
{
"id": "CLIPTextEncode.0",
"type": "CLIPTextEncode",
"pos": {
"0": 515,
"1": 130
},
"size": {
"0": 400,
"1": 200
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 10
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
13
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": "CLIPTextEncode.1",
"type": "CLIPTextEncode",
"pos": {
"0": 515,
"1": 460
},
"size": {
"0": 400,
"1": 200
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 11
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
14
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": "EmptyLatentImage.0",
"type": "EmptyLatentImage",
"pos": {
"0": 100,
"1": 358
},
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
15
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": "KSampler.0",
"type": "KSampler",
"pos": {
"0": 1015,
"1": 130
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 12
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 13
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 14
},
{
"name": "latent_image",
"type": "LATENT",
"link": 15
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
16
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
3,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": "VAEDecode.0",
"type": "VAEDecode",
"pos": {
"0": 1430,
"1": 130
},
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 16
},
{
"name": "vae",
"type": "VAE",
"link": 17
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
18
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"id": "SaveImage.0",
"type": "SaveImage",
"pos": {
"0": 1740,
"1": 130
},
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 18
}
],
"outputs": [],
"properties": {},
"widgets_values": [
"ComfyUI"
]
}
],
"links": [
[
10,
"CheckpointLoaderSimple.0",
1,
"CLIPTextEncode.0",
0,
"CLIP"
],
[
11,
"CheckpointLoaderSimple.0",
1,
"CLIPTextEncode.1",
0,
"CLIP"
],
[
12,
"CheckpointLoaderSimple.0",
0,
"KSampler.0",
0,
"MODEL"
],
[
13,
"CLIPTextEncode.0",
0,
"KSampler.0",
1,
"CONDITIONING"
],
[
14,
"CLIPTextEncode.1",
0,
"KSampler.0",
2,
"CONDITIONING"
],
[
15,
"EmptyLatentImage.0",
0,
"KSampler.0",
3,
"LATENT"
],
[
16,
"KSampler.0",
0,
"VAEDecode.0",
0,
"LATENT"
],
[
17,
"CheckpointLoaderSimple.0",
2,
"VAEDecode.0",
1,
"VAE"
],
[
18,
"VAEDecode.0",
0,
"SaveImage.0",
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
149.9747408641311,
383.8593224280729
]
}
},
"version": 0.4
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View File

@@ -0,0 +1,51 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './fixtures/ComfyPage'
test.describe('Browser tab title', () => {
test.describe('Beta Menu', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
})
test('Can display workflow name', async ({ comfyPage }) => {
const workflowName = await comfyPage.page.evaluate(async () => {
return window['app'].extensionManager.workflow.activeWorkflow.filename
})
expect(await comfyPage.page.title()).toBe(`*${workflowName} - ComfyUI`)
})
// Failing on CI
// Cannot reproduce locally
test.skip('Can display workflow name with unsaved changes', async ({
comfyPage
}) => {
const workflowName = await comfyPage.page.evaluate(async () => {
return window['app'].extensionManager.workflow.activeWorkflow.filename
})
expect(await comfyPage.page.title()).toBe(`${workflowName} - ComfyUI`)
await comfyPage.menu.topbar.saveWorkflow('test')
expect(await comfyPage.page.title()).toBe('test - ComfyUI')
const textBox = comfyPage.widgetTextBox
await textBox.fill('Hello World')
await comfyPage.clickEmptySpace()
expect(await comfyPage.page.title()).toBe(`*test - ComfyUI`)
// Delete the saved workflow for cleanup.
await comfyPage.page.evaluate(async () => {
return window['app'].extensionManager.workflow.activeWorkflow.delete()
})
})
})
test.describe('Legacy Menu', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
})
test('Can display default title', async ({ comfyPage }) => {
expect(await comfyPage.page.title()).toBe('ComfyUI')
})
})
})

View File

@@ -0,0 +1,177 @@
import {
ComfyPage,
comfyPageFixture as test,
comfyExpect as expect
} from './fixtures/ComfyPage'
import type { useWorkspaceStore } from '../src/stores/workspaceStore'
type WorkspaceStore = ReturnType<typeof useWorkspaceStore>
async function beforeChange(comfyPage: ComfyPage) {
await comfyPage.page.evaluate(() => {
window['app'].canvas.emitBeforeChange()
})
}
async function afterChange(comfyPage: ComfyPage) {
await comfyPage.page.evaluate(() => {
window['app'].canvas.emitAfterChange()
})
}
test.describe('Change Tracker', () => {
test.describe('Undo/Redo', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.setupWorkflowsDirectory({})
})
test('Can undo multiple operations', async ({ comfyPage }) => {
function isModified() {
return comfyPage.page.evaluate(async () => {
return !!(window['app'].extensionManager as WorkspaceStore).workflow
.activeWorkflow?.isModified
})
}
function getUndoQueueSize() {
return comfyPage.page.evaluate(() => {
const workflow = (window['app'].extensionManager as WorkspaceStore)
.workflow.activeWorkflow
return workflow?.changeTracker.undoQueue.length
})
}
function getRedoQueueSize() {
return comfyPage.page.evaluate(() => {
const workflow = (window['app'].extensionManager as WorkspaceStore)
.workflow.activeWorkflow
return workflow?.changeTracker.redoQueue.length
})
}
expect(await getUndoQueueSize()).toBe(0)
expect(await getRedoQueueSize()).toBe(0)
// Save, confirm no errors & workflow modified flag removed
await comfyPage.menu.topbar.saveWorkflow('undo-redo-test')
expect(await comfyPage.getToastErrorCount()).toBe(0)
expect(await isModified()).toBe(false)
expect(await getUndoQueueSize()).toBe(0)
expect(await getRedoQueueSize()).toBe(0)
const node = (await comfyPage.getFirstNodeRef())!
await node.click('title')
await node.click('collapse')
await expect(node).toBeCollapsed()
expect(await isModified()).toBe(true)
expect(await getUndoQueueSize()).toBe(1)
expect(await getRedoQueueSize()).toBe(0)
await comfyPage.ctrlB()
await expect(node).toBeBypassed()
expect(await isModified()).toBe(true)
expect(await getUndoQueueSize()).toBe(2)
expect(await getRedoQueueSize()).toBe(0)
await comfyPage.ctrlZ()
await expect(node).not.toBeBypassed()
expect(await isModified()).toBe(true)
expect(await getUndoQueueSize()).toBe(1)
expect(await getRedoQueueSize()).toBe(1)
await comfyPage.ctrlZ()
await expect(node).not.toBeCollapsed()
expect(await isModified()).toBe(false)
expect(await getUndoQueueSize()).toBe(0)
expect(await getRedoQueueSize()).toBe(2)
})
})
test('Can group multiple change actions into a single transaction', async ({
comfyPage
}) => {
const node = (await comfyPage.getFirstNodeRef())!
expect(node).toBeTruthy()
await expect(node).not.toBeCollapsed()
await expect(node).not.toBeBypassed()
// Make changes outside set
// Bypass + collapse node
await node.click('title')
await node.click('collapse')
await comfyPage.ctrlB()
await expect(node).toBeCollapsed()
await expect(node).toBeBypassed()
// Undo, undo, ensure both changes undone
await comfyPage.ctrlZ()
await expect(node).not.toBeBypassed()
await expect(node).toBeCollapsed()
await comfyPage.ctrlZ()
await expect(node).not.toBeBypassed()
await expect(node).not.toBeCollapsed()
// Prevent clicks registering a double-click
await comfyPage.clickEmptySpace()
await node.click('title')
// Run again, but within a change transaction
await beforeChange(comfyPage)
await node.click('collapse')
await comfyPage.ctrlB()
await expect(node).toBeCollapsed()
await expect(node).toBeBypassed()
// End transaction
await afterChange(comfyPage)
// Ensure undo reverts both changes
await comfyPage.ctrlZ()
await expect(node).not.toBeBypassed()
await expect(node).not.toBeCollapsed()
})
test('Can nest multiple change transactions without adding undo steps', async ({
comfyPage
}) => {
const node = (await comfyPage.getFirstNodeRef())!
const bypassAndPin = async () => {
await beforeChange(comfyPage)
await comfyPage.ctrlB()
await expect(node).toBeBypassed()
await comfyPage.page.keyboard.press('KeyP')
await comfyPage.nextFrame()
await expect(node).toBePinned()
await afterChange(comfyPage)
}
const collapse = async () => {
await beforeChange(comfyPage)
await node.click('collapse', { moveMouseToEmptyArea: true })
await expect(node).toBeCollapsed()
await afterChange(comfyPage)
}
const multipleChanges = async () => {
await beforeChange(comfyPage)
// Call other actions that uses begin/endChange
await node.click('title')
await collapse()
await bypassAndPin()
await afterChange(comfyPage)
}
await multipleChanges()
await comfyPage.ctrlZ()
await expect(node).not.toBeBypassed()
await expect(node).not.toBePinned()
await expect(node).not.toBeCollapsed()
await comfyPage.ctrlY()
await expect(node).toBeBypassed()
await expect(node).toBePinned()
await expect(node).toBeCollapsed()
})
})

View File

@@ -0,0 +1,239 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './fixtures/ComfyPage'
const customColorPalettes = {
obsidian: {
version: 102,
id: 'obsidian',
name: 'Obsidian',
colors: {
node_slot: {
CLIP: '#FFD500',
CLIP_VISION: '#A8DADC',
CLIP_VISION_OUTPUT: '#ad7452',
CONDITIONING: '#FFA931',
CONTROL_NET: '#6EE7B7',
IMAGE: '#64B5F6',
LATENT: '#FF9CF9',
MASK: '#81C784',
MODEL: '#B39DDB',
STYLE_MODEL: '#C2FFAE',
VAE: '#FF6E6E',
TAESD: '#DCC274',
PIPE_LINE: '#7737AA',
PIPE_LINE_SDXL: '#7737AA',
INT: '#29699C',
XYPLOT: '#74DA5D',
X_Y: '#38291f'
},
litegraph_base: {
BACKGROUND_IMAGE:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII=',
CLEAR_BACKGROUND_COLOR: '#222222',
NODE_TITLE_COLOR: 'rgba(255,255,255,.75)',
NODE_SELECTED_TITLE_COLOR: '#FFF',
NODE_TEXT_SIZE: 14,
NODE_TEXT_COLOR: '#b8b8b8',
NODE_SUBTEXT_SIZE: 12,
NODE_DEFAULT_COLOR: 'rgba(0,0,0,.8)',
NODE_DEFAULT_BGCOLOR: 'rgba(22,22,22,.8)',
NODE_DEFAULT_BOXCOLOR: 'rgba(255,255,255,.75)',
NODE_DEFAULT_SHAPE: 'box',
NODE_BOX_OUTLINE_COLOR: '#236692',
DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0)',
DEFAULT_GROUP_FONT: 24,
WIDGET_BGCOLOR: '#242424',
WIDGET_OUTLINE_COLOR: '#333',
WIDGET_TEXT_COLOR: '#a3a3a8',
WIDGET_SECONDARY_TEXT_COLOR: '#97979c',
LINK_COLOR: '#9A9',
EVENT_LINK_COLOR: '#A86',
CONNECTING_LINK_COLOR: '#AFA'
},
comfy_base: {
'fg-color': '#fff',
'bg-color': '#242424',
'comfy-menu-bg': 'rgba(24,24,24,.9)',
'comfy-input-bg': '#262626',
'input-text': '#ddd',
'descrip-text': '#999',
'drag-text': '#ccc',
'error-text': '#ff4444',
'border-color': '#29292c',
'tr-even-bg-color': 'rgba(28,28,28,.9)',
'tr-odd-bg-color': 'rgba(19,19,19,.9)'
}
}
},
obsidian_dark: {
version: 102,
id: 'obsidian_dark',
name: 'Obsidian Dark',
colors: {
node_slot: {
CLIP: '#FFD500',
CLIP_VISION: '#A8DADC',
CLIP_VISION_OUTPUT: '#ad7452',
CONDITIONING: '#FFA931',
CONTROL_NET: '#6EE7B7',
IMAGE: '#64B5F6',
LATENT: '#FF9CF9',
MASK: '#81C784',
MODEL: '#B39DDB',
STYLE_MODEL: '#C2FFAE',
VAE: '#FF6E6E',
TAESD: '#DCC274',
PIPE_LINE: '#7737AA',
PIPE_LINE_SDXL: '#7737AA',
INT: '#29699C',
XYPLOT: '#74DA5D',
X_Y: '#38291f'
},
litegraph_base: {
BACKGROUND_IMAGE:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMy0xMS0xM1QwMDoxODowMiswMTowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmIyYzRhNjA5LWJmYTctYTg0MC1iOGFlLTk3MzE2ZjM1ZGIyNyIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjk0ZmNlZGU4LTE1MTctZmQ0MC04ZGU3LWYzOTgxM2E3ODk5ZiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjIzMWIxMGIwLWI0ZmItMDI0ZS1iMTJlLTMwNTMwM2NkMDdjOCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MjMxYjEwYjAtYjRmYi0wMjRlLWIxMmUtMzA1MzAzY2QwN2M4IiBzdEV2dDp3aGVuPSIyMDIzLTExLTEzVDAwOjE4OjAyKzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjQ4OWY1NzlmLTJkNjUtZWQ0Zi04OTg0LTA4NGE2MGE1ZTMzNSIgc3RFdnQ6d2hlbj0iMjAyMy0xMS0xNVQwMjowNDo1OSswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpiMmM0YTYwOS1iZmE3LWE4NDAtYjhhZS05NzMxNmYzNWRiMjciIHN0RXZ0OndoZW49IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4xIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4OTe6GAAAAx0lEQVR42u3WMQoAIQxFwRzJys77X8vSLiRgITif7bYbgrwYc/mKXyBoY4VVBgsWLFiwYFmOlTv+9jfDOjHmr8u6eVkGCxYsWLBgmc5S8ApewXvgYRksWLBgKXidpeBdloL3wMOCBctgwVLwCl7BuyyDBQsWLFiwTGcpeAWv4D3wsAwWLFiwFLzOUvAuS8F74GHBgmWwYCl4Ba/gXZbBggULFixYprMUvIJX8B54WAYLFixYCl5nKXiXpeA98LBgwTJYsGC9tg1o8f4TTtqzNQAAAABJRU5ErkJggg==',
CLEAR_BACKGROUND_COLOR: '#000',
NODE_TITLE_COLOR: 'rgba(255,255,255,.75)',
NODE_SELECTED_TITLE_COLOR: '#FFF',
NODE_TEXT_SIZE: 14,
NODE_TEXT_COLOR: '#b8b8b8',
NODE_SUBTEXT_SIZE: 12,
NODE_DEFAULT_COLOR: 'rgba(0,0,0,.8)',
NODE_DEFAULT_BGCOLOR: 'rgba(22,22,22,.8)',
NODE_DEFAULT_BOXCOLOR: 'rgba(255,255,255,.75)',
NODE_DEFAULT_SHAPE: 'box',
NODE_BOX_OUTLINE_COLOR: '#236692',
DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0)',
DEFAULT_GROUP_FONT: 24,
WIDGET_BGCOLOR: '#242424',
WIDGET_OUTLINE_COLOR: '#333',
WIDGET_TEXT_COLOR: '#a3a3a8',
WIDGET_SECONDARY_TEXT_COLOR: '#97979c',
LINK_COLOR: '#9A9',
EVENT_LINK_COLOR: '#A86',
CONNECTING_LINK_COLOR: '#AFA'
},
comfy_base: {
'fg-color': '#fff',
'bg-color': '#242424',
'comfy-menu-bg': 'rgba(24,24,24,.9)',
'comfy-input-bg': '#262626',
'input-text': '#ddd',
'descrip-text': '#999',
'drag-text': '#ccc',
'error-text': '#ff4444',
'border-color': '#29292c',
'tr-even-bg-color': 'rgba(28,28,28,.9)',
'tr-odd-bg-color': 'rgba(19,19,19,.9)'
}
}
}
}
test.describe('Color Palette', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.CustomColorPalettes', customColorPalettes)
})
test('Can show custom color palette', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.ColorPalette', 'custom_obsidian_dark')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
'custom-color-palette-obsidian-dark.png'
)
await comfyPage.setSetting('Comfy.ColorPalette', 'dark')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('default-color-palette.png')
})
})
test.describe('Node Color Adjustments', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.loadWorkflow('every_node_color')
})
test('should adjust opacity via node opacity setting', async ({
comfyPage
}) => {
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
await comfyPage.page.waitForTimeout(128)
// Drag mouse to force canvas to redraw
await comfyPage.page.mouse.move(0, 0)
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-0.5.png')
await comfyPage.setSetting('Comfy.Node.Opacity', 1.0)
await comfyPage.page.waitForTimeout(128)
await comfyPage.page.mouse.move(8, 8)
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-1.png')
})
test('should persist color adjustments when changing themes', async ({
comfyPage
}) => {
await comfyPage.setSetting('Comfy.Node.Opacity', 0.2)
await comfyPage.setSetting('Comfy.ColorPalette', 'arc')
await comfyPage.nextFrame()
await comfyPage.page.mouse.move(0, 0)
await expect(comfyPage.canvas).toHaveScreenshot(
'node-opacity-0.2-arc-theme.png'
)
})
test('should not serialize color adjustments in workflow', async ({
comfyPage
}) => {
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
const saveWorkflowInterval = 1000
await comfyPage.page.waitForTimeout(saveWorkflowInterval)
const workflow = await comfyPage.page.evaluate(() => {
return localStorage.getItem('workflow')
})
for (const node of JSON.parse(workflow).nodes) {
if (node.bgcolor) expect(node.bgcolor).not.toMatch(/hsla/)
if (node.color) expect(node.color).not.toMatch(/hsla/)
}
})
test('should lighten node colors when switching to light theme', async ({
comfyPage
}) => {
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('node-lightened-colors.png')
})
test.describe('Context menu color adjustments', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
await comfyPage.setSetting('Comfy.Node.Opacity', 0.3)
const node = await comfyPage.getFirstNodeRef()
await node?.clickContextMenuOption('Colors')
})
test('should persist color adjustments when changing custom node colors', async ({
comfyPage
}) => {
await comfyPage.page
.locator('.litemenu-entry.submenu span:has-text("red")')
.click()
await expect(comfyPage.canvas).toHaveScreenshot(
'node-opacity-0.3-color-changed.png'
)
})
test('should persist color adjustments when removing custom node color', async ({
comfyPage
}) => {
await comfyPage.page
.locator('.litemenu-entry.submenu span:has-text("No color")')
.click()
await expect(comfyPage.canvas).toHaveScreenshot(
'node-opacity-0.3-color-removed.png'
)
})
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

View File

@@ -0,0 +1,49 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './fixtures/ComfyPage'
test.describe('Keybindings', () => {
test('Should execute command', async ({ comfyPage }) => {
await comfyPage.registerCommand('TestCommand', () => {
window['foo'] = true
})
await comfyPage.executeCommand('TestCommand')
expect(await comfyPage.page.evaluate(() => window['foo'])).toBe(true)
})
test('Should execute async command', async ({ comfyPage }) => {
await comfyPage.registerCommand('TestCommand', async () => {
await new Promise<void>((resolve) =>
setTimeout(() => {
window['foo'] = true
resolve()
}, 5)
)
})
await comfyPage.executeCommand('TestCommand')
expect(await comfyPage.page.evaluate(() => window['foo'])).toBe(true)
})
test('Should handle command errors', async ({ comfyPage }) => {
await comfyPage.registerCommand('TestCommand', () => {
throw new Error('Test error')
})
await comfyPage.executeCommand('TestCommand')
await expect(comfyPage.page.locator('.p-toast')).toBeVisible()
})
test('Should handle async command errors', async ({ comfyPage }) => {
await comfyPage.registerCommand('TestCommand', async () => {
await new Promise<void>((resolve, reject) =>
setTimeout(() => {
reject(new Error('Test error'))
}, 5)
)
})
await comfyPage.executeCommand('TestCommand')
await expect(comfyPage.page.locator('.p-toast')).toBeVisible()
})
})

View File

@@ -1,56 +1,108 @@
import { expect } from "@playwright/test";
import { comfyPageFixture as test } from "./ComfyPage";
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './fixtures/ComfyPage'
test.describe("Copy Paste", () => {
test("Can copy and paste node", async ({ comfyPage }) => {
await comfyPage.clickEmptyLatentNode();
await comfyPage.page.mouse.move(10, 10);
await comfyPage.ctrlC();
await comfyPage.ctrlV();
await expect(comfyPage.canvas).toHaveScreenshot("copied-node.png");
});
test.describe('Copy Paste', () => {
test('Can copy and paste node', async ({ comfyPage }) => {
await comfyPage.clickEmptyLatentNode()
await comfyPage.page.mouse.move(10, 10)
await comfyPage.ctrlC()
await comfyPage.ctrlV()
await expect(comfyPage.canvas).toHaveScreenshot('copied-node.png')
})
test("Can copy and paste text", async ({ comfyPage }) => {
const textBox = comfyPage.widgetTextBox;
await textBox.click();
const originalString = await textBox.inputValue();
await textBox.selectText();
await comfyPage.ctrlC();
await comfyPage.ctrlV();
await comfyPage.ctrlV();
const resultString = await textBox.inputValue();
expect(resultString).toBe(originalString + originalString);
});
test('Can copy and paste text', async ({ comfyPage }) => {
const textBox = comfyPage.widgetTextBox
await textBox.click()
const originalString = await textBox.inputValue()
await textBox.selectText()
await comfyPage.ctrlC(null)
await comfyPage.ctrlV(null)
await comfyPage.ctrlV(null)
const resultString = await textBox.inputValue()
expect(resultString).toBe(originalString + originalString)
})
test('Can copy and paste widget value', async ({ comfyPage }) => {
// Copy width value (512) from empty latent node to KSampler's seed.
// KSampler's seed
await comfyPage.canvas.click({
position: {
x: 1005,
y: 281
}
})
await comfyPage.ctrlC(null)
// Empty latent node's width
await comfyPage.canvas.click({
position: {
x: 718,
y: 643
}
})
await comfyPage.ctrlV(null)
await comfyPage.page.keyboard.press('Enter')
await expect(comfyPage.canvas).toHaveScreenshot('copied-widget-value.png')
})
/**
* https://github.com/Comfy-Org/ComfyUI_frontend/issues/98
*/
test("Paste in text area with node previously copied", async ({
comfyPage,
test('Paste in text area with node previously copied', async ({
comfyPage
}) => {
await comfyPage.clickEmptyLatentNode();
await comfyPage.ctrlC();
const textBox = comfyPage.widgetTextBox;
await textBox.click();
await textBox.inputValue();
await textBox.selectText();
await comfyPage.ctrlC();
await comfyPage.ctrlV();
await comfyPage.ctrlV();
await comfyPage.clickEmptyLatentNode()
await comfyPage.ctrlC(null)
const textBox = comfyPage.widgetTextBox
await textBox.click()
await textBox.inputValue()
await textBox.selectText()
await comfyPage.ctrlC(null)
await comfyPage.ctrlV(null)
await comfyPage.ctrlV(null)
await expect(comfyPage.canvas).toHaveScreenshot(
"paste-in-text-area-with-node-previously-copied.png"
);
});
'paste-in-text-area-with-node-previously-copied.png'
)
})
test("Copy text area does not copy node", async ({ comfyPage }) => {
const textBox = comfyPage.widgetTextBox;
await textBox.click();
await textBox.inputValue();
await textBox.selectText();
await comfyPage.ctrlC();
test('Copy text area does not copy node', async ({ comfyPage }) => {
const textBox = comfyPage.widgetTextBox
await textBox.click()
await textBox.inputValue()
await textBox.selectText()
await comfyPage.ctrlC(null)
// Unfocus textbox.
await comfyPage.page.mouse.click(10, 10);
await comfyPage.ctrlV();
await expect(comfyPage.canvas).toHaveScreenshot("no-node-copied.png");
});
});
await comfyPage.page.mouse.click(10, 10)
await comfyPage.ctrlV(null)
await expect(comfyPage.canvas).toHaveScreenshot('no-node-copied.png')
})
test('Copy node by dragging + alt', async ({ comfyPage }) => {
// TextEncodeNode1
await comfyPage.page.mouse.move(618, 191)
await comfyPage.page.keyboard.down('Alt')
await comfyPage.page.mouse.down()
await comfyPage.page.mouse.move(100, 100)
await comfyPage.page.mouse.up()
await comfyPage.page.keyboard.up('Alt')
await expect(comfyPage.canvas).toHaveScreenshot('drag-copy-copied-node.png')
})
test('Can undo paste multiple nodes as single action', async ({
comfyPage
}) => {
const initialCount = await comfyPage.getGraphNodesCount()
expect(initialCount).toBeGreaterThan(1)
await comfyPage.canvas.click()
await comfyPage.ctrlA()
await comfyPage.page.mouse.move(10, 10)
await comfyPage.ctrlC()
await comfyPage.ctrlV()
const pasteCount = await comfyPage.getGraphNodesCount()
expect(pasteCount).toBe(initialCount * 2)
await comfyPage.ctrlZ()
const undoCount = await comfyPage.getGraphNodesCount()
expect(undoCount).toBe(initialCount)
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

@@ -0,0 +1,101 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './fixtures/ComfyPage'
test.describe('Load workflow warning', () => {
test('Should display a warning when loading a workflow with missing nodes', async ({
comfyPage
}) => {
await comfyPage.loadWorkflow('missing_nodes')
// Wait for the element with the .comfy-missing-nodes selector to be visible
const missingNodesWarning = comfyPage.page.locator('.comfy-missing-nodes')
await expect(missingNodesWarning).toBeVisible()
})
})
test('Does not report warning on undo/redo', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
await comfyPage.loadWorkflow('missing_nodes')
await comfyPage.closeDialog()
// Make a change to the graph
await comfyPage.doubleClickCanvas()
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
// Undo and redo the change
await comfyPage.ctrlZ()
await expect(comfyPage.page.locator('.comfy-missing-nodes')).not.toBeVisible()
await comfyPage.ctrlY()
await expect(comfyPage.page.locator('.comfy-missing-nodes')).not.toBeVisible()
})
test.describe('Execution error', () => {
test('Should display an error message when an execution error occurs', async ({
comfyPage
}) => {
await comfyPage.loadWorkflow('execution_error')
await comfyPage.queueButton.click()
await comfyPage.nextFrame()
// Wait for the element with the .comfy-execution-error selector to be visible
const executionError = comfyPage.page.locator('.comfy-error-report')
await expect(executionError).toBeVisible()
})
})
test.describe('Missing models warning', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.Workflow.ShowMissingModelsWarning', true)
await comfyPage.page.evaluate((url: string) => {
return fetch(`${url}/api/devtools/cleanup_fake_model`)
}, comfyPage.url)
})
// Flaky test after parallelization
// https://github.com/Comfy-Org/ComfyUI_frontend/pull/1400
test.skip('Should display a warning when missing models are found', async ({
comfyPage
}) => {
// The fake_model.safetensors is served by
// https://github.com/Comfy-Org/ComfyUI_devtools/blob/main/__init__.py
await comfyPage.loadWorkflow('missing_models')
const missingModelsWarning = comfyPage.page.locator('.comfy-missing-models')
await expect(missingModelsWarning).toBeVisible()
const downloadButton = comfyPage.page.getByLabel('Download')
await expect(downloadButton).toBeVisible()
const downloadPromise = comfyPage.page.waitForEvent('download')
await downloadButton.click()
const download = await downloadPromise
expect(download.suggestedFilename()).toBe('fake_model.safetensors')
})
})
test.describe('Settings', () => {
test('@mobile Should be visible on mobile', async ({ comfyPage }) => {
await comfyPage.page.keyboard.press('Control+,')
const searchBox = comfyPage.page.locator('.settings-content')
await expect(searchBox).toBeVisible()
})
test('Can open settings with hotkey', async ({ comfyPage }) => {
await comfyPage.page.keyboard.down('ControlOrMeta')
await comfyPage.page.keyboard.press(',')
await comfyPage.page.keyboard.up('ControlOrMeta')
const settingsLocator = comfyPage.page.locator('.settings-container')
await expect(settingsLocator).toBeVisible()
await comfyPage.page.keyboard.press('Escape')
await expect(settingsLocator).not.toBeVisible()
})
test('Can change canvas zoom speed setting', async ({ comfyPage }) => {
const maxSpeed = 2.5
await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', maxSpeed)
test.step('Setting should persist', async () => {
expect(await comfyPage.getSetting('Comfy.Graph.ZoomSpeed')).toBe(maxSpeed)
})
})
})

View File

@@ -0,0 +1,160 @@
import { expect, Locator } from '@playwright/test'
import { comfyPageFixture as test } from './fixtures/ComfyPage'
test.describe('Topbar commands', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
})
test('Should allow registering topbar commands', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window['app'].registerExtension({
name: 'TestExtension1',
commands: [
{
id: 'foo',
label: 'foo-command',
function: () => {
window['foo'] = true
}
}
],
menuCommands: [
{
path: ['ext'],
commands: ['foo']
}
]
})
})
await comfyPage.menu.topbar.triggerTopbarCommand(['ext', 'foo-command'])
expect(await comfyPage.page.evaluate(() => window['foo'])).toBe(true)
})
test('Should not allow register command defined in other extension', async ({
comfyPage
}) => {
await comfyPage.registerCommand('foo', () => alert(1))
await comfyPage.page.evaluate(() => {
window['app'].registerExtension({
name: 'TestExtension1',
menuCommands: [
{
path: ['ext'],
commands: ['foo']
}
]
})
})
const menuItem = comfyPage.menu.topbar.getMenuItem('ext')
expect(await menuItem.count()).toBe(0)
})
test('Should allow registering keybindings', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
const app = window['app']
app.registerExtension({
name: 'TestExtension1',
commands: [
{
id: 'TestCommand',
function: () => {
window['TestCommand'] = true
}
}
],
keybindings: [
{
combo: { key: 'k' },
commandId: 'TestCommand'
}
]
})
})
await comfyPage.page.keyboard.press('k')
expect(await comfyPage.page.evaluate(() => window['TestCommand'])).toBe(
true
)
})
test.describe('Settings', () => {
test('Should allow adding settings', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window['app'].registerExtension({
name: 'TestExtension1',
settings: [
{
id: 'TestSetting',
name: 'Test Setting',
type: 'text',
defaultValue: 'Hello, world!',
onChange: () => {
window['changeCount'] = (window['changeCount'] ?? 0) + 1
}
}
]
})
})
// onChange is called when the setting is first added
expect(await comfyPage.page.evaluate(() => window['changeCount'])).toBe(1)
expect(await comfyPage.getSetting('TestSetting')).toBe('Hello, world!')
await comfyPage.setSetting('TestSetting', 'Hello, universe!')
expect(await comfyPage.getSetting('TestSetting')).toBe('Hello, universe!')
expect(await comfyPage.page.evaluate(() => window['changeCount'])).toBe(2)
})
test('Should allow setting boolean settings', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window['app'].registerExtension({
name: 'TestExtension1',
settings: [
{
id: 'Comfy.TestSetting',
name: 'Test Setting',
type: 'boolean',
defaultValue: false,
onChange: () => {
window['changeCount'] = (window['changeCount'] ?? 0) + 1
}
}
]
})
})
expect(await comfyPage.getSetting('Comfy.TestSetting')).toBe(false)
expect(await comfyPage.page.evaluate(() => window['changeCount'])).toBe(1)
await comfyPage.settingDialog.open()
await comfyPage.settingDialog.toggleBooleanSetting('Comfy.TestSetting')
expect(await comfyPage.getSetting('Comfy.TestSetting')).toBe(true)
expect(await comfyPage.page.evaluate(() => window['changeCount'])).toBe(2)
})
})
test.describe('About panel', () => {
test('Should allow adding badges', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window['app'].registerExtension({
name: 'TestExtension1',
aboutPageBadges: [
{
label: 'Test Badge',
url: 'https://example.com',
icon: 'pi pi-box'
}
]
})
})
await comfyPage.settingDialog.open()
await comfyPage.settingDialog.goToAboutPanel()
const badge = comfyPage.page.locator('.about-badge').last()
expect(badge).toBeDefined()
expect(await badge.textContent()).toContain('Test Badge')
})
})
})

View File

@@ -0,0 +1,849 @@
import type { Page, Locator, APIRequestContext } from '@playwright/test'
import { expect } from '@playwright/test'
import { test as base } from '@playwright/test'
import { ComfyActionbar } from '../helpers/actionbar'
import dotenv from 'dotenv'
dotenv.config()
import * as fs from 'fs'
import { NodeBadgeMode } from '../../src/types/nodeSource'
import type { NodeId } from '../../src/types/comfyWorkflow'
import type { KeyCombo } from '../../src/types/keyBindingTypes'
import { ComfyTemplates } from '../helpers/templates'
import { ComfyNodeSearchBox } from './components/ComfyNodeSearchBox'
import {
NodeLibrarySidebarTab,
WorkflowsSidebarTab
} from './components/SidebarTab'
import { Topbar } from './components/Topbar'
import { NodeReference } from './utils/litegraphUtils'
import type { Position, Size } from './types'
import { SettingDialog } from './components/SettingDialog'
class ComfyMenu {
public readonly sideToolbar: Locator
public readonly themeToggleButton: Locator
public readonly saveButton: Locator
constructor(public readonly page: Page) {
this.sideToolbar = page.locator('.side-tool-bar-container')
this.themeToggleButton = page.locator('.comfy-vue-theme-toggle')
this.saveButton = page
.locator('button[title="Save the current workflow"]')
.nth(0)
}
get nodeLibraryTab() {
return new NodeLibrarySidebarTab(this.page)
}
get workflowsTab() {
return new WorkflowsSidebarTab(this.page)
}
get topbar() {
return new Topbar(this.page)
}
async toggleTheme() {
await this.themeToggleButton.click()
await this.page.evaluate(() => {
return new Promise((resolve) => {
window['app'].ui.settings.addEventListener(
'Comfy.ColorPalette.change',
resolve,
{ once: true }
)
setTimeout(resolve, 5000)
})
})
}
async getThemeId() {
return await this.page.evaluate(async () => {
return await window['app'].ui.settings.getSettingValue(
'Comfy.ColorPalette'
)
})
}
}
type FolderStructure = {
[key: string]: FolderStructure | string
}
export class ComfyPage {
public readonly url: string
// All canvas position operations are based on default view of canvas.
public readonly canvas: Locator
public readonly widgetTextBox: Locator
// Buttons
public readonly resetViewButton: Locator
public readonly queueButton: Locator
// Inputs
public readonly workflowUploadInput: Locator
// Components
public readonly searchBox: ComfyNodeSearchBox
public readonly menu: ComfyMenu
public readonly actionbar: ComfyActionbar
public readonly templates: ComfyTemplates
public readonly settingDialog: SettingDialog
/** Worker index to test user ID */
public readonly userIds: string[] = []
/** Test user ID for the current context */
get id() {
return this.userIds[comfyPageFixture.info().parallelIndex]
}
constructor(
public readonly page: Page,
public readonly request: APIRequestContext
) {
this.url = process.env.PLAYWRIGHT_TEST_URL || 'http://localhost:8188'
this.canvas = page.locator('#graph-canvas')
this.widgetTextBox = page.getByPlaceholder('text').nth(1)
this.resetViewButton = page.getByRole('button', { name: 'Reset View' })
this.queueButton = page.getByRole('button', { name: 'Queue Prompt' })
this.workflowUploadInput = page.locator('#comfy-file-input')
this.searchBox = new ComfyNodeSearchBox(page)
this.menu = new ComfyMenu(page)
this.actionbar = new ComfyActionbar(page)
this.templates = new ComfyTemplates(page)
this.settingDialog = new SettingDialog(page)
}
convertLeafToContent(structure: FolderStructure): FolderStructure {
const result: FolderStructure = {}
for (const [key, value] of Object.entries(structure)) {
if (typeof value === 'string') {
const filePath = this.assetPath(value)
result[key] = fs.readFileSync(filePath, 'utf-8')
} else {
result[key] = this.convertLeafToContent(value)
}
}
return result
}
async getGraphNodesCount(): Promise<number> {
return await this.page.evaluate(() => {
return window['app']?.graph?.nodes?.length || 0
})
}
async getSelectedGraphNodesCount(): Promise<number> {
return await this.page.evaluate(() => {
return (
window['app']?.graph?.nodes?.filter(
(node: any) => node.is_selected === true
).length || 0
)
})
}
async setupWorkflowsDirectory(structure: FolderStructure) {
const resp = await this.request.post(
`${this.url}/api/devtools/setup_folder_structure`,
{
data: {
tree_structure: this.convertLeafToContent(structure),
base_path: `user/${this.id}/workflows`
}
}
)
if (resp.status() !== 200) {
throw new Error(
`Failed to setup workflows directory: ${await resp.text()}`
)
}
}
async setupUser(username: string) {
const res = await this.request.get(`${this.url}/api/users`)
if (res.status() !== 200)
throw new Error(`Failed to retrieve users: ${await res.text()}`)
const apiRes = await res.json()
const user = Object.entries(apiRes?.users ?? {}).find(
([, name]) => name === username
)
const id = user?.[0]
return id ? id : await this.createUser(username)
}
async createUser(username: string) {
const resp = await this.request.post(`${this.url}/api/users`, {
data: { username }
})
if (resp.status() !== 200)
throw new Error(`Failed to create user: ${await resp.text()}`)
return await resp.json()
}
async setupSettings(settings: Record<string, any>) {
const resp = await this.request.post(
`${this.url}/api/devtools/set_settings`,
{
data: settings
}
)
if (resp.status() !== 200) {
throw new Error(`Failed to setup settings: ${await resp.text()}`)
}
}
async setup({ clearStorage = true }: { clearStorage?: boolean } = {}) {
await this.goto()
if (clearStorage) {
await this.page.evaluate((id) => {
localStorage.clear()
sessionStorage.clear()
localStorage.setItem('Comfy.userId', id)
}, this.id)
}
await this.goto()
// Unify font for consistent screenshots.
await this.page.addStyleTag({
url: 'https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'
})
await this.page.addStyleTag({
url: 'https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'
})
await this.page.addStyleTag({
content: `
* {
font-family: 'Roboto Mono', 'Noto Color Emoji';
}`
})
await this.page.waitForFunction(() => document.fonts.ready)
await this.page.waitForFunction(
() =>
// window['app'] => GraphCanvas ready
// window['app'].extensionManager => GraphView ready
window['app'] && window['app'].extensionManager
)
await this.page.waitForSelector('.p-blockui-mask', { state: 'hidden' })
await this.nextFrame()
}
public assetPath(fileName: string) {
return `./browser_tests/assets/${fileName}`
}
async executeCommand(commandId: string) {
await this.page.evaluate((id: string) => {
return window['app'].extensionManager.command.execute(id)
}, commandId)
}
async registerCommand(
commandId: string,
command: (() => void) | (() => Promise<void>)
) {
await this.page.evaluate(
({ commandId, commandStr }) => {
const app = window['app']
const randomSuffix = Math.random().toString(36).substring(2, 8)
const extensionName = `TestExtension_${randomSuffix}`
app.registerExtension({
name: extensionName,
commands: [
{
id: commandId,
function: eval(commandStr)
}
]
})
},
{ commandId, commandStr: command.toString() }
)
}
async registerKeybinding(keyCombo: KeyCombo, command: () => void) {
await this.page.evaluate(
({ keyCombo, commandStr }) => {
const app = window['app']
const randomSuffix = Math.random().toString(36).substring(2, 8)
const extensionName = `TestExtension_${randomSuffix}`
const commandId = `TestCommand_${randomSuffix}`
app.registerExtension({
name: extensionName,
keybindings: [
{
combo: keyCombo,
commandId: commandId
}
],
commands: [
{
id: commandId,
function: eval(commandStr)
}
]
})
},
{ keyCombo, commandStr: command.toString() }
)
}
async setSetting(settingId: string, settingValue: any) {
return await this.page.evaluate(
async ({ id, value }) => {
await window['app'].extensionManager.setting.set(id, value)
},
{ id: settingId, value: settingValue }
)
}
async getSetting(settingId: string) {
return await this.page.evaluate(async (id) => {
return await window['app'].extensionManager.setting.get(id)
}, settingId)
}
async reload({ clearStorage = true }: { clearStorage?: boolean } = {}) {
await this.page.reload({ timeout: 15000 })
await this.setup({ clearStorage })
}
async goto() {
await this.page.goto(this.url)
}
async nextFrame() {
await this.page.evaluate(() => {
return new Promise<number>(requestAnimationFrame)
})
}
async delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
async loadWorkflow(workflowName: string) {
await this.workflowUploadInput.setInputFiles(
this.assetPath(`${workflowName}.json`)
)
await this.nextFrame()
}
async resetView() {
if (await this.resetViewButton.isVisible()) {
await this.resetViewButton.click()
}
// Avoid "Reset View" button highlight.
await this.page.mouse.move(10, 10)
await this.nextFrame()
}
async getToastErrorCount() {
return await this.page
.locator('.p-toast-message.p-toast-message-error')
.count()
}
async getVisibleToastCount() {
return await this.page.locator('.p-toast:visible').count()
}
async clickTextEncodeNode1() {
await this.canvas.click({
position: {
x: 618,
y: 191
}
})
await this.nextFrame()
}
async clickTextEncodeNodeToggler() {
await this.canvas.click({
position: {
x: 430,
y: 171
}
})
await this.nextFrame()
}
async clickTextEncodeNode2() {
await this.canvas.click({
position: {
x: 622,
y: 400
}
})
await this.nextFrame()
}
async clickEmptySpace() {
await this.canvas.click({
position: {
x: 35,
y: 31
}
})
await this.nextFrame()
}
async dragAndDrop(source: Position, target: Position) {
await this.page.mouse.move(source.x, source.y)
await this.page.mouse.down()
await this.page.mouse.move(target.x, target.y)
await this.page.mouse.up()
await this.nextFrame()
}
async dragAndDropFile(fileName: string) {
const filePath = this.assetPath(fileName)
// Read the file content
const buffer = fs.readFileSync(filePath)
// Get file type
const getFileType = (fileName: string) => {
if (fileName.endsWith('.png')) return 'image/png'
if (fileName.endsWith('.webp')) return 'image/webp'
if (fileName.endsWith('.json')) return 'application/json'
return 'application/octet-stream'
}
const fileType = getFileType(fileName)
await this.page.evaluate(
async ({ buffer, fileName, fileType }) => {
const file = new File([new Uint8Array(buffer)], fileName, {
type: fileType
})
const dataTransfer = new DataTransfer()
dataTransfer.items.add(file)
const dropEvent = new DragEvent('drop', {
bubbles: true,
cancelable: true,
dataTransfer
})
Object.defineProperty(dropEvent, 'preventDefault', {
value: () => {},
writable: false
})
Object.defineProperty(dropEvent, 'stopPropagation', {
value: () => {},
writable: false
})
document.dispatchEvent(dropEvent)
},
{ buffer: [...new Uint8Array(buffer)], fileName, fileType }
)
await this.nextFrame()
}
async dragNode2() {
await this.dragAndDrop({ x: 622, y: 400 }, { x: 622, y: 300 })
await this.nextFrame()
}
// Default graph positions
get clipTextEncodeNode1InputSlot(): Position {
return { x: 427, y: 198 }
}
get clipTextEncodeNode2InputSlot(): Position {
return { x: 422, y: 402 }
}
// A point on input edge.
get clipTextEncodeNode2InputLinkPath(): Position {
return {
x: 395,
y: 422
}
}
get loadCheckpointNodeClipOutputSlot(): Position {
return { x: 332, y: 509 }
}
get emptySpace(): Position {
return { x: 427, y: 98 }
}
async disconnectEdge() {
await this.dragAndDrop(this.clipTextEncodeNode1InputSlot, this.emptySpace)
}
async connectEdge() {
await this.dragAndDrop(
this.loadCheckpointNodeClipOutputSlot,
this.clipTextEncodeNode1InputSlot
)
}
async adjustWidgetValue() {
// Adjust Empty Latent Image's width input.
const page = this.page
await page.locator('#graph-canvas').click({
position: {
x: 724,
y: 645
}
})
const dialogInput = page.locator('.graphdialog input[type="text"]')
await dialogInput.click()
await dialogInput.fill('128')
await dialogInput.press('Enter')
await this.nextFrame()
}
async zoom(deltaY: number, steps: number = 1) {
await this.page.mouse.move(10, 10)
for (let i = 0; i < steps; i++) {
await this.page.mouse.wheel(0, deltaY)
}
await this.nextFrame()
}
async pan(offset: Position, safeSpot?: Position) {
safeSpot = safeSpot || { x: 10, y: 10 }
await this.page.mouse.move(safeSpot.x, safeSpot.y)
await this.page.mouse.down()
await this.page.mouse.move(offset.x + safeSpot.x, offset.y + safeSpot.y)
await this.page.mouse.up()
await this.nextFrame()
}
async panWithTouch(offset: Position, safeSpot?: Position) {
safeSpot = safeSpot || { x: 10, y: 10 }
const client = await this.page.context().newCDPSession(this.page)
await client.send('Input.dispatchTouchEvent', {
type: 'touchStart',
touchPoints: [safeSpot]
})
await client.send('Input.dispatchTouchEvent', {
type: 'touchMove',
touchPoints: [{ x: offset.x + safeSpot.x, y: offset.y + safeSpot.y }]
})
await client.send('Input.dispatchTouchEvent', {
type: 'touchEnd',
touchPoints: []
})
await this.nextFrame()
}
async rightClickCanvas() {
await this.page.mouse.click(10, 10, { button: 'right' })
await this.nextFrame()
}
async clickContextMenuItem(name: string): Promise<void> {
await this.page.getByRole('menuitem', { name }).click()
await this.nextFrame()
}
async doubleClickCanvas() {
await this.page.mouse.dblclick(10, 10, { delay: 5 })
await this.nextFrame()
}
async clickEmptyLatentNode() {
await this.canvas.click({
position: {
x: 724,
y: 625
}
})
this.page.mouse.move(10, 10)
await this.nextFrame()
}
async rightClickEmptyLatentNode() {
await this.canvas.click({
position: {
x: 724,
y: 645
},
button: 'right'
})
this.page.mouse.move(10, 10)
await this.nextFrame()
}
async select2Nodes() {
// Select 2 CLIP nodes.
await this.page.keyboard.down('Control')
await this.clickTextEncodeNode1()
await this.clickTextEncodeNode2()
await this.page.keyboard.up('Control')
await this.nextFrame()
}
async ctrlSend(keyToPress: string, locator: Locator | null = this.canvas) {
const target = locator ?? this.page.keyboard
await target.press(`Control+${keyToPress}`)
await this.nextFrame()
}
async ctrlA(locator?: Locator | null) {
await this.ctrlSend('KeyA', locator)
}
async ctrlB(locator?: Locator | null) {
await this.ctrlSend('KeyB', locator)
}
async ctrlC(locator?: Locator | null) {
await this.ctrlSend('KeyC', locator)
}
async ctrlV(locator?: Locator | null) {
await this.ctrlSend('KeyV', locator)
}
async ctrlZ(locator?: Locator | null) {
await this.ctrlSend('KeyZ', locator)
}
async ctrlY(locator?: Locator | null) {
await this.ctrlSend('KeyY', locator)
}
async ctrlArrowUp(locator?: Locator | null) {
await this.ctrlSend('ArrowUp', locator)
}
async ctrlArrowDown(locator?: Locator | null) {
await this.ctrlSend('ArrowDown', locator)
}
async closeMenu() {
await this.page.click('button.comfy-close-menu-btn')
await this.nextFrame()
}
async closeDialog() {
await this.page.locator('.p-dialog-close-button').click()
await expect(this.page.locator('.p-dialog')).toBeHidden()
}
async resizeNode(
nodePos: Position,
nodeSize: Size,
ratioX: number,
ratioY: number,
revertAfter: boolean = false
) {
const bottomRight = {
x: nodePos.x + nodeSize.width,
y: nodePos.y + nodeSize.height
}
const target = {
x: nodePos.x + nodeSize.width * ratioX,
y: nodePos.y + nodeSize.height * ratioY
}
// -1 to be inside the node. -2 because nodes currently get an arbitrary +1 to width.
await this.dragAndDrop(
{ x: bottomRight.x - 2, y: bottomRight.y - 1 },
target
)
await this.nextFrame()
if (revertAfter) {
await this.dragAndDrop({ x: target.x - 2, y: target.y - 1 }, bottomRight)
await this.nextFrame()
}
}
async resizeKsamplerNode(
percentX: number,
percentY: number,
revertAfter: boolean = false
) {
const ksamplerPos = {
x: 863,
y: 156
}
const ksamplerSize = {
width: 315,
height: 292
}
return this.resizeNode(
ksamplerPos,
ksamplerSize,
percentX,
percentY,
revertAfter
)
}
async resizeLoadCheckpointNode(
percentX: number,
percentY: number,
revertAfter: boolean = false
) {
const loadCheckpointPos = {
x: 26,
y: 444
}
const loadCheckpointSize = {
width: 315,
height: 127
}
return this.resizeNode(
loadCheckpointPos,
loadCheckpointSize,
percentX,
percentY,
revertAfter
)
}
async resizeEmptyLatentNode(
percentX: number,
percentY: number,
revertAfter: boolean = false
) {
const emptyLatentPos = {
x: 473,
y: 579
}
const emptyLatentSize = {
width: 315,
height: 136
}
return this.resizeNode(
emptyLatentPos,
emptyLatentSize,
percentX,
percentY,
revertAfter
)
}
async confirmDialog(prompt: string, text: string = 'Yes') {
const modal = this.page.locator(
`.comfy-modal-content:has-text("${prompt}")`
)
await expect(modal).toBeVisible()
await modal
.locator('.comfyui-button', {
hasText: text
})
.click()
await expect(modal).toBeHidden()
}
async convertAllNodesToGroupNode(groupNodeName: string) {
this.page.on('dialog', async (dialog) => {
await dialog.accept(groupNodeName)
})
await this.canvas.press('Control+a')
const node = await this.getFirstNodeRef()
await node!.clickContextMenuOption('Convert to Group Node')
await this.nextFrame()
}
async convertOffsetToCanvas(pos: [number, number]) {
return this.page.evaluate((pos) => {
return window['app'].canvas.ds.convertOffsetToCanvas(pos)
}, pos)
}
async getNodeRefById(id: NodeId) {
return new NodeReference(id, this)
}
async getNodeRefsByType(type: string): Promise<NodeReference[]> {
return Promise.all(
(
await this.page.evaluate((type) => {
return window['app'].graph.nodes
.filter((n) => n.type === type)
.map((n) => n.id)
}, type)
).map((id: NodeId) => this.getNodeRefById(id))
)
}
async getFirstNodeRef(): Promise<NodeReference | null> {
const id = await this.page.evaluate(() => {
return window['app'].graph.nodes[0]?.id
})
if (!id) return null
return this.getNodeRefById(id)
}
async moveMouseToEmptyArea() {
await this.page.mouse.move(10, 10)
}
}
export const comfyPageFixture = base.extend<{ comfyPage: ComfyPage }>({
comfyPage: async ({ page, request }, use) => {
const comfyPage = new ComfyPage(page, request)
const { parallelIndex } = comfyPageFixture.info()
const username = `playwright-test-${parallelIndex}`
const userId = await comfyPage.setupUser(username)
comfyPage.userIds[parallelIndex] = userId
await comfyPage.setupSettings({
'Comfy.UseNewMenu': 'Disabled',
// Hide canvas menu/info by default.
'Comfy.Graph.CanvasInfo': false,
'Comfy.Graph.CanvasMenu': false,
// Hide all badges by default.
'Comfy.NodeBadge.NodeIdBadgeMode': NodeBadgeMode.None,
'Comfy.NodeBadge.NodeSourceBadgeMode': NodeBadgeMode.None,
// Disable tooltips by default to avoid flakiness.
'Comfy.EnableTooltips': false,
'Comfy.userId': userId
})
await comfyPage.setup()
await use(comfyPage)
}
})
const makeMatcher = function <T>(
getValue: (node: NodeReference) => Promise<T> | T,
type: string
) {
return async function (
node: NodeReference,
options?: { timeout?: number; intervals?: number[] }
) {
const value = await getValue(node)
let assertion = expect(
value,
'Node is ' + (this.isNot ? '' : 'not ') + type
)
if (this.isNot) {
assertion = assertion.not
}
await expect(async () => {
assertion.toBeTruthy()
}).toPass({ timeout: 250, ...options })
return {
pass: !this.isNot,
message: () => 'Node is ' + (this.isNot ? 'not ' : '') + type
}
}
}
export const comfyExpect = expect.extend({
toBePinned: makeMatcher((n) => n.isPinned(), 'pinned'),
toBeBypassed: makeMatcher((n) => n.isBypassed(), 'bypassed'),
toBeCollapsed: makeMatcher((n) => n.isCollapsed(), 'collapsed')
})

View File

@@ -0,0 +1,79 @@
import { Locator, Page } from '@playwright/test'
export class ComfyNodeSearchFilterSelectionPanel {
constructor(public readonly page: Page) {}
async selectFilterType(filterType: string) {
await this.page
.locator(
`.filter-type-select .p-togglebutton-label:has-text("${filterType}")`
)
.click()
}
async selectFilterValue(filterValue: string) {
await this.page.locator('.filter-value-select .p-select-dropdown').click()
await this.page
.locator(
`.p-select-overlay .p-select-list .p-select-option-label:text-is("${filterValue}")`
)
.click()
}
async addFilter(filterValue: string, filterType: string) {
await this.selectFilterType(filterType)
await this.selectFilterValue(filterValue)
await this.page.locator('.p-button-label:has-text("Add")').click()
}
}
export class ComfyNodeSearchBox {
public readonly input: Locator
public readonly dropdown: Locator
public readonly filterSelectionPanel: ComfyNodeSearchFilterSelectionPanel
constructor(public readonly page: Page) {
this.input = page.locator(
'.comfy-vue-node-search-container input[type="text"]'
)
this.dropdown = page.locator(
'.comfy-vue-node-search-container .p-autocomplete-list'
)
this.filterSelectionPanel = new ComfyNodeSearchFilterSelectionPanel(page)
}
get filterButton() {
return this.page.locator('.comfy-vue-node-search-container .filter-button')
}
async fillAndSelectFirstNode(
nodeName: string,
options?: { suggestionIndex: number }
) {
await this.input.waitFor({ state: 'visible' })
await this.input.fill(nodeName)
await this.dropdown.waitFor({ state: 'visible' })
// Wait for some time for the auto complete list to update.
// The auto complete list is debounced and may take some time to update.
await this.page.waitForTimeout(500)
await this.dropdown
.locator('li')
.nth(options?.suggestionIndex || 0)
.click()
}
async addFilter(filterValue: string, filterType: string) {
await this.filterButton.click()
await this.filterSelectionPanel.addFilter(filterValue, filterType)
}
get filterChips() {
return this.page.locator(
'.comfy-vue-node-search-container .p-autocomplete-chip-item'
)
}
async removeFilter(index: number) {
await this.filterChips.nth(index).locator('.p-chip-remove-icon').click()
}
}

View File

@@ -0,0 +1,40 @@
import { Page } from '@playwright/test'
export class SettingDialog {
constructor(public readonly page: Page) {}
async open() {
const button = this.page.locator('button.comfy-settings-btn:visible')
await button.click()
await this.page.waitForSelector('div.settings-container')
}
/**
* Set the value of a text setting
* @param id - The id of the setting
* @param value - The value to set
*/
async setStringSetting(id: string, value: string) {
const settingInputDiv = this.page.locator(
`div.settings-container div[id="${id}"]`
)
await settingInputDiv.locator('input').fill(value)
}
/**
* Toggle the value of a boolean setting
* @param id - The id of the setting
*/
async toggleBooleanSetting(id: string) {
const settingInputDiv = this.page.locator(
`div.settings-container div[id="${id}"]`
)
await settingInputDiv.locator('input').click()
}
async goToAboutPanel() {
const aboutButton = this.page.locator('li[aria-label="About"]')
await aboutButton.click()
await this.page.waitForSelector('div.about-container')
}
}

View File

@@ -0,0 +1,145 @@
import { Locator, Page } from '@playwright/test'
class SidebarTab {
constructor(
public readonly page: Page,
public readonly tabId: string
) {}
get tabButton() {
return this.page.locator(`.${this.tabId}-tab-button`)
}
get selectedTabButton() {
return this.page.locator(
`.${this.tabId}-tab-button.side-bar-button-selected`
)
}
async open() {
if (await this.selectedTabButton.isVisible()) {
return
}
await this.tabButton.click()
}
}
export class NodeLibrarySidebarTab extends SidebarTab {
constructor(public readonly page: Page) {
super(page, 'node-library')
}
get nodeLibrarySearchBoxInput() {
return this.page.locator('.node-lib-search-box input[type="text"]')
}
get nodeLibraryTree() {
return this.page.locator('.node-lib-tree-explorer')
}
get nodePreview() {
return this.page.locator('.node-lib-node-preview')
}
get tabContainer() {
return this.page.locator('.sidebar-content-container')
}
get newFolderButton() {
return this.tabContainer.locator('.new-folder-button')
}
async open() {
await super.open()
await this.nodeLibraryTree.waitFor({ state: 'visible' })
}
async close() {
if (!this.tabButton.isVisible()) {
return
}
await this.tabButton.click()
await this.nodeLibraryTree.waitFor({ state: 'hidden' })
}
folderSelector(folderName: string) {
return `.p-tree-node-content:has(> .tree-explorer-node-label:has(.tree-folder .node-label:has-text("${folderName}")))`
}
getFolder(folderName: string) {
return this.page.locator(this.folderSelector(folderName))
}
nodeSelector(nodeName: string) {
return `.p-tree-node-content:has(> .tree-explorer-node-label:has(.tree-leaf .node-label:has-text("${nodeName}")))`
}
getNode(nodeName: string) {
return this.page.locator(this.nodeSelector(nodeName))
}
}
export class WorkflowsSidebarTab extends SidebarTab {
constructor(public readonly page: Page) {
super(page, 'workflows')
}
get browseGalleryButton() {
return this.page.locator('.browse-templates-button')
}
get newBlankWorkflowButton() {
return this.page.locator('.new-blank-workflow-button')
}
get openWorkflowButton() {
return this.page.locator('.open-workflow-button')
}
async getOpenedWorkflowNames() {
return await this.page
.locator('.comfyui-workflows-open .node-label')
.allInnerTexts()
}
async getActiveWorkflowName() {
return await this.page
.locator('.comfyui-workflows-open .p-tree-node-selected .node-label')
.innerText()
}
async getTopLevelSavedWorkflowNames() {
return await this.page
.locator('.comfyui-workflows-browse .node-label')
.allInnerTexts()
}
async switchToWorkflow(workflowName: string) {
const workflowLocator = this.getOpenedItem(workflowName)
await workflowLocator.click()
await this.page.waitForTimeout(300)
}
getOpenedItem(name: string) {
return this.page.locator('.comfyui-workflows-open .node-label', {
hasText: name
})
}
getPersistedItem(name: string) {
return this.page.locator('.comfyui-workflows-browse .node-label', {
hasText: name
})
}
async renameWorkflow(locator: Locator, newName: string) {
await locator.click({ button: 'right' })
await this.page
.locator('.p-contextmenu-item-content', { hasText: 'Rename' })
.click()
await this.page.keyboard.type(newName)
await this.page.keyboard.press('Enter')
await this.page.waitForTimeout(300)
}
}

View File

@@ -0,0 +1,85 @@
import { Locator, Page } from '@playwright/test'
export class Topbar {
constructor(public readonly page: Page) {}
async getTabNames(): Promise<string[]> {
return await this.page
.locator('.workflow-tabs .workflow-label')
.allInnerTexts()
}
async openSubmenuMobile() {
await this.page.locator('.p-menubar-mobile .p-menubar-button').click()
}
getMenuItem(itemLabel: string): Locator {
return this.page.locator(`.p-menubar-item-label:text-is("${itemLabel}")`)
}
getWorkflowTab(tabName: string): Locator {
return this.page
.locator(`.workflow-tabs .workflow-label:has-text("${tabName}")`)
.locator('..')
}
async closeWorkflowTab(tabName: string) {
const tab = this.getWorkflowTab(tabName)
await tab.locator('.close-button').click({ force: true })
}
getSaveDialog(): Locator {
return this.page.locator('.p-dialog-content input')
}
saveWorkflow(workflowName: string): Promise<void> {
return this._saveWorkflow(workflowName, 'Save')
}
saveWorkflowAs(workflowName: string): Promise<void> {
return this._saveWorkflow(workflowName, 'Save As')
}
async _saveWorkflow(workflowName: string, command: 'Save' | 'Save As') {
await this.triggerTopbarCommand(['Workflow', command])
await this.getSaveDialog().fill(workflowName)
await this.page.keyboard.press('Enter')
// Wait for workflow service to finish saving
await this.page.waitForFunction(
() => !window['app'].extensionManager.workflow.isBusy,
undefined,
{ timeout: 3000 }
)
// Wait for the dialog to close.
await this.getSaveDialog().waitFor({ state: 'hidden', timeout: 500 })
}
async triggerTopbarCommand(path: string[]) {
if (path.length < 2) {
throw new Error('Path is too short')
}
const tabName = path[0]
const topLevelMenu = this.page.locator(
`.top-menubar .p-menubar-item-label:text-is("${tabName}")`
)
await topLevelMenu.waitFor({ state: 'visible' })
await topLevelMenu.click()
for (let i = 1; i < path.length; i++) {
const commandName = path[i]
const menuItem = this.page
.locator(
`.top-menubar .p-menubar-submenu .p-menubar-item:has-text("${commandName}")`
)
.first()
await menuItem.waitFor({ state: 'visible' })
await menuItem.hover()
if (i === path.length - 1) {
await menuItem.click()
}
}
}
}

View File

@@ -0,0 +1,9 @@
export interface Position {
x: number
y: number
}
export interface Size {
width: number
height: number
}

View File

@@ -0,0 +1,257 @@
import { ManageGroupNode } from '../../helpers/manageGroupNode'
import type { NodeId } from '../../../src/types/comfyWorkflow'
import type { Page } from '@playwright/test'
import type { ComfyPage } from '../ComfyPage'
import type { Position, Size } from '../types'
export class NodeSlotReference {
constructor(
readonly type: 'input' | 'output',
readonly index: number,
readonly node: NodeReference
) {}
async getPosition() {
const pos: [number, number] = await this.node.comfyPage.page.evaluate(
([type, id, index]) => {
const node = window['app'].graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
return window['app'].canvas.ds.convertOffsetToCanvas(
node.getConnectionPos(type === 'input', index)
)
},
[this.type, this.node.id, this.index] as const
)
return {
x: pos[0],
y: pos[1]
}
}
async getLinkCount() {
return await this.node.comfyPage.page.evaluate(
([type, id, index]) => {
const node = window['app'].graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
if (type === 'input') {
return node.inputs[index].link == null ? 0 : 1
}
return node.outputs[index].links?.length ?? 0
},
[this.type, this.node.id, this.index] as const
)
}
async removeLinks() {
await this.node.comfyPage.page.evaluate(
([type, id, index]) => {
const node = window['app'].graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
if (type === 'input') {
node.disconnectInput(index)
} else {
node.disconnectOutput(index)
}
},
[this.type, this.node.id, this.index] as const
)
}
}
export class NodeWidgetReference {
constructor(
readonly index: number,
readonly node: NodeReference
) {}
async getPosition(): Promise<Position> {
const pos: [number, number] = await this.node.comfyPage.page.evaluate(
([id, index]) => {
const node = window['app'].graph.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
const widget = node.widgets[index]
if (!widget) throw new Error(`Widget ${index} not found.`)
const [x, y, w, h] = node.getBounding()
return window['app'].canvas.ds.convertOffsetToCanvas([
x + w / 2,
y + window['LiteGraph']['NODE_TITLE_HEIGHT'] + widget.last_y + 1
])
},
[this.node.id, this.index] as const
)
return {
x: pos[0],
y: pos[1]
}
}
}
export class NodeReference {
constructor(
readonly id: NodeId,
readonly comfyPage: ComfyPage
) {}
async exists(): Promise<boolean> {
return await this.comfyPage.page.evaluate((id) => {
const node = window['app'].graph.getNodeById(id)
return !!node
}, this.id)
}
getType(): Promise<string> {
return this.getProperty('type')
}
async getPosition(): Promise<Position> {
const pos = await this.comfyPage.convertOffsetToCanvas(
await this.getProperty<[number, number]>('pos')
)
return {
x: pos[0],
y: pos[1]
}
}
async getBounding(): Promise<Position & Size> {
const [x, y, width, height]: [number, number, number, number] =
await this.comfyPage.page.evaluate((id) => {
const node = window['app'].graph.getNodeById(id)
if (!node) throw new Error('Node not found')
return node.getBounding()
}, this.id)
return {
x,
y,
width,
height
}
}
async getSize(): Promise<Size> {
const size = await this.getProperty<[number, number]>('size')
return {
width: size[0],
height: size[1]
}
}
async getFlags(): Promise<{ collapsed?: boolean; pinned?: boolean }> {
return await this.getProperty('flags')
}
async isPinned() {
return !!(await this.getFlags()).pinned
}
async isCollapsed() {
return !!(await this.getFlags()).collapsed
}
async isBypassed() {
return (await this.getProperty<number | null | undefined>('mode')) === 4
}
async getProperty<T>(prop: string): Promise<T> {
return await this.comfyPage.page.evaluate(
([id, prop]) => {
const node = window['app'].graph.getNodeById(id)
if (!node) throw new Error('Node not found')
return node[prop]
},
[this.id, prop] as const
)
}
async getOutput(index: number) {
return new NodeSlotReference('output', index, this)
}
async getInput(index: number) {
return new NodeSlotReference('input', index, this)
}
async getWidget(index: number) {
return new NodeWidgetReference(index, this)
}
async click(
position: 'title' | 'collapse',
options?: Parameters<Page['click']>[1] & { moveMouseToEmptyArea?: boolean }
) {
const nodePos = await this.getPosition()
const nodeSize = await this.getSize()
let clickPos: Position
switch (position) {
case 'title':
clickPos = { x: nodePos.x + nodeSize.width / 2, y: nodePos.y - 15 }
break
case 'collapse':
clickPos = { x: nodePos.x + 5, y: nodePos.y - 10 }
break
default:
throw new Error(`Invalid click position ${position}`)
}
const moveMouseToEmptyArea = options?.moveMouseToEmptyArea
if (options) {
delete options.moveMouseToEmptyArea
}
await this.comfyPage.canvas.click({
...options,
position: clickPos
})
await this.comfyPage.nextFrame()
if (moveMouseToEmptyArea) {
await this.comfyPage.moveMouseToEmptyArea()
}
}
async copy() {
await this.click('title')
await this.comfyPage.ctrlC()
await this.comfyPage.nextFrame()
}
async connectWidget(
originSlotIndex: number,
targetNode: NodeReference,
targetWidgetIndex: number
) {
const originSlot = await this.getOutput(originSlotIndex)
const targetWidget = await targetNode.getWidget(targetWidgetIndex)
await this.comfyPage.dragAndDrop(
await originSlot.getPosition(),
await targetWidget.getPosition()
)
return originSlot
}
async connectOutput(
originSlotIndex: number,
targetNode: NodeReference,
targetSlotIndex: number
) {
const originSlot = await this.getOutput(originSlotIndex)
const targetSlot = await targetNode.getInput(targetSlotIndex)
await this.comfyPage.dragAndDrop(
await originSlot.getPosition(),
await targetSlot.getPosition()
)
return originSlot
}
async getContextMenuOptionNames() {
await this.click('title', { button: 'right' })
const ctx = this.comfyPage.page.locator('.litecontextmenu')
return await ctx.locator('.litemenu-entry').allInnerTexts()
}
async clickContextMenuOption(optionText: string) {
await this.click('title', { button: 'right' })
const ctx = this.comfyPage.page.locator('.litecontextmenu')
await ctx.getByText(optionText).click()
}
async convertToGroupNode(groupNodeName: string = 'GroupNode') {
this.comfyPage.page.once('dialog', async (dialog) => {
await dialog.accept(groupNodeName)
})
await this.clickContextMenuOption('Convert to Group Node')
await this.comfyPage.nextFrame()
const nodes = await this.comfyPage.getNodeRefsByType(
`workflow>${groupNodeName}`
)
if (nodes.length !== 1) {
throw new Error(`Did not find single group node (found=${nodes.length})`)
}
return nodes[0]
}
async manageGroupNode() {
await this.clickContextMenuOption('Manage Group Node')
await this.comfyPage.nextFrame()
return new ManageGroupNode(
this.comfyPage.page,
this.comfyPage.page.locator('.comfy-group-manage')
)
}
}

View File

@@ -0,0 +1,51 @@
import { test as base } from '@playwright/test'
export const webSocketFixture = base.extend<{
ws: { trigger(data: any, url?: string): Promise<void> }
}>({
ws: [
async ({ page }, use) => {
// Each time a page loads, to catch navigations
page.on('load', async () => {
await page.evaluate(function () {
// Create a wrapper for WebSocket that stores them globally
// so we can look it up to trigger messages
const store: Record<string, WebSocket> = ((window as any).__ws__ = {})
window.WebSocket = class extends window.WebSocket {
constructor() {
// @ts-expect-error
super(...arguments)
store[this.url] = this
}
}
})
})
await use({
async trigger(data, url) {
// Trigger a websocket event on the page
await page.evaluate(
function ([data, url]) {
if (!url) {
// If no URL specified, use page URL
const u = new URL(window.location.toString())
u.protocol = 'ws:'
u.pathname = '/'
url = u.toString() + 'ws'
}
const ws: WebSocket = (window as any).__ws__[url]
ws.dispatchEvent(
new MessageEvent('message', {
data
})
)
},
[JSON.stringify(data), url]
)
}
})
},
// We need this to run automatically as the first thing so it adds handlers as soon as the page loads
{ auto: true }
]
})

View File

@@ -0,0 +1,20 @@
import { FullConfig } from '@playwright/test'
import { backupPath } from './utils/backupUtils'
import dotenv from 'dotenv'
dotenv.config()
export default function globalSetup(config: FullConfig) {
if (!process.env.CI) {
if (process.env.TEST_COMFYUI_DIR) {
backupPath([process.env.TEST_COMFYUI_DIR, 'user'])
backupPath([process.env.TEST_COMFYUI_DIR, 'models'], {
renameAndReplaceWithScaffolding: true
})
} else {
console.warn(
'Set TEST_COMFYUI_DIR in .env to prevent user data (settings, workflows, etc.) from being overwritten'
)
}
}
}

View File

@@ -0,0 +1,12 @@
import { FullConfig } from '@playwright/test'
import { restorePath } from './utils/backupUtils'
import dotenv from 'dotenv'
dotenv.config()
export default function globalTeardown(config: FullConfig) {
if (!process.env.CI && process.env.TEST_COMFYUI_DIR) {
restorePath([process.env.TEST_COMFYUI_DIR, 'user'])
restorePath([process.env.TEST_COMFYUI_DIR, 'models'])
}
}

View File

@@ -0,0 +1,38 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './fixtures/ComfyPage'
test.describe('Graph Canvas Menu', () => {
test.beforeEach(async ({ comfyPage }) => {
// Set link render mode to spline to make sure it's not affected by other tests'
// side effects.
await comfyPage.setSetting('Comfy.LinkRenderMode', 2)
})
test('Can toggle link visibility', async ({ comfyPage }) => {
// Note: `Comfy.Graph.CanvasMenu` is disabled in comfyPage setup.
// so no cleanup is needed.
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', true)
const button = comfyPage.page.getByTestId('toggle-link-visibility-button')
await button.click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
'canvas-with-hidden-links.png'
)
const hiddenLinkRenderMode = await comfyPage.page.evaluate(() => {
return window['LiteGraph'].HIDDEN_LINK
})
expect(await comfyPage.getSetting('Comfy.LinkRenderMode')).toBe(
hiddenLinkRenderMode
)
await button.click()
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
'canvas-with-visible-links.png'
)
expect(await comfyPage.getSetting('Comfy.LinkRenderMode')).not.toBe(
hiddenLinkRenderMode
)
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

View File

@@ -0,0 +1,264 @@
import { expect } from '@playwright/test'
import { ComfyPage, comfyPageFixture as test } from './fixtures/ComfyPage'
import type { NodeReference } from './fixtures/utils/litegraphUtils'
test.describe('Group Node', () => {
test.describe('Node library sidebar', () => {
const groupNodeName = 'DefautWorkflowGroupNode'
const groupNodeCategory = 'group nodes>workflow'
const groupNodeBookmarkName = `workflow>${groupNodeName}`
let libraryTab
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
libraryTab = comfyPage.menu.nodeLibraryTab
await comfyPage.convertAllNodesToGroupNode(groupNodeName)
await libraryTab.open()
})
test('Is added to node library sidebar', async ({ comfyPage }) => {
expect(await libraryTab.getFolder('group nodes').count()).toBe(1)
})
test('Can be added to canvas using node library sidebar', async ({
comfyPage
}) => {
const initialNodeCount = await comfyPage.getGraphNodesCount()
// Add group node from node library sidebar
await libraryTab.getFolder(groupNodeCategory).click()
await libraryTab.getNode(groupNodeName).click()
// Verify the node is added to the canvas
expect(await comfyPage.getGraphNodesCount()).toBe(initialNodeCount + 1)
})
test('Can be bookmarked and unbookmarked', async ({ comfyPage }) => {
await libraryTab.getFolder(groupNodeCategory).click()
await libraryTab
.getNode(groupNodeName)
.locator('.bookmark-button')
.click()
// Verify the node is added to the bookmarks tab
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual([groupNodeBookmarkName])
// Verify the bookmark node with the same name is added to the tree
expect(await libraryTab.getNode(groupNodeName).count()).not.toBe(0)
// Unbookmark the node
await libraryTab
.getNode(groupNodeName)
.locator('.bookmark-button')
.first()
.click()
// Verify the node is removed from the bookmarks tab
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toHaveLength(0)
})
test('Displays preview on bookmark hover', async ({ comfyPage }) => {
await libraryTab.getFolder(groupNodeCategory).click()
await libraryTab
.getNode(groupNodeName)
.locator('.bookmark-button')
.click()
await comfyPage.page.hover('.p-tree-node-label.tree-explorer-node-label')
expect(await comfyPage.page.isVisible('.node-lib-node-preview')).toBe(
true
)
await libraryTab
.getNode(groupNodeName)
.locator('.bookmark-button')
.first()
.click()
})
})
// The 500ms fixed delay on the search results is causing flakiness
// Potential solution: add a spinner state when the search is in progress,
// and observe that state from the test. Blocker: the PrimeVue AutoComplete
// does not have a v-model on the query, so we cannot observe the raw
// query update, and thus cannot set the spinning state between the raw query
// update and the debounced search update.
test.skip('Can be added to canvas using search', async ({ comfyPage }) => {
const groupNodeName = 'DefautWorkflowGroupNode'
await comfyPage.convertAllNodesToGroupNode(groupNodeName)
await comfyPage.doubleClickCanvas()
await comfyPage.nextFrame()
await comfyPage.searchBox.fillAndSelectFirstNode(groupNodeName)
await expect(comfyPage.canvas).toHaveScreenshot(
'group-node-copy-added-from-search.png'
)
})
test('Displays tooltip on title hover', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.EnableTooltips', true)
await comfyPage.convertAllNodesToGroupNode('Group Node')
await comfyPage.page.mouse.move(47, 173)
const tooltipTimeout = 500
await comfyPage.page.waitForTimeout(tooltipTimeout + 16)
await expect(comfyPage.page.locator('.node-tooltip')).toBeVisible()
})
test('Reconnects inputs after configuration changed via manage dialog save', async ({
comfyPage
}) => {
const expectSingleNode = async (type: string) => {
const nodes = await comfyPage.getNodeRefsByType(type)
expect(nodes).toHaveLength(1)
return nodes[0]
}
const latent = await expectSingleNode('EmptyLatentImage')
const sampler = await expectSingleNode('KSampler')
// Remove existing link
const samplerInput = await sampler.getInput(0)
await samplerInput.removeLinks()
// Group latent + sampler
await latent.click('title', {
modifiers: ['Shift']
})
await sampler.click('title', {
modifiers: ['Shift']
})
const groupNode = await sampler.convertToGroupNode()
// Connect node to group
const ckpt = await expectSingleNode('CheckpointLoaderSimple')
const input = await ckpt.connectOutput(0, groupNode, 0)
expect(await input.getLinkCount()).toBe(1)
// Modify the group node via manage dialog
const manage = await groupNode.manageGroupNode()
await manage.selectNode('KSampler')
await manage.changeTab('Inputs')
await manage.setLabel('model', 'test')
await manage.save()
await manage.close()
// Ensure the link is still present
expect(await input.getLinkCount()).toBe(1)
})
test('Loads from a workflow using the legacy path separator ("/")', async ({
comfyPage
}) => {
await comfyPage.loadWorkflow('legacy_group_node')
expect(await comfyPage.getGraphNodesCount()).toBe(1)
await expect(
comfyPage.page.locator('.comfy-missing-nodes')
).not.toBeVisible()
})
test.describe('Copy and paste', () => {
let groupNode: NodeReference | null
const WORKFLOW_NAME = 'group_node_v1.3.3'
const GROUP_NODE_CATEGORY = 'group nodes>workflow'
const GROUP_NODE_PREFIX = 'workflow>'
const GROUP_NODE_NAME = 'group_node' // Node name in given workflow
const GROUP_NODE_TYPE = `${GROUP_NODE_PREFIX}${GROUP_NODE_NAME}`
const isRegisteredLitegraph = async (comfyPage: ComfyPage) => {
return await comfyPage.page.evaluate((nodeType: string) => {
return !!window['LiteGraph'].registered_node_types[nodeType]
}, GROUP_NODE_TYPE)
}
const isRegisteredNodeDefStore = async (comfyPage: ComfyPage) => {
const groupNodesFolderCt = await comfyPage.menu.nodeLibraryTab
.getFolder(GROUP_NODE_CATEGORY)
.count()
return groupNodesFolderCt === 1
}
const verifyNodeLoaded = async (
comfyPage: ComfyPage,
expectedCount: number
) => {
expect(await comfyPage.getNodeRefsByType(GROUP_NODE_TYPE)).toHaveLength(
expectedCount
)
expect(await isRegisteredLitegraph(comfyPage)).toBe(true)
expect(await isRegisteredNodeDefStore(comfyPage)).toBe(true)
}
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.loadWorkflow(WORKFLOW_NAME)
await comfyPage.menu.nodeLibraryTab.open()
groupNode = await comfyPage.getFirstNodeRef()
if (!groupNode)
throw new Error(`Group node not found in workflow ${WORKFLOW_NAME}`)
await groupNode.copy()
})
test('Copies and pastes group node within the same workflow', async ({
comfyPage
}) => {
await comfyPage.ctrlV()
await verifyNodeLoaded(comfyPage, 2)
})
test('Copies and pastes group node after clearing workflow', async ({
comfyPage
}) => {
await comfyPage.menu.topbar.triggerTopbarCommand([
'Edit',
'Clear Workflow'
])
await comfyPage.ctrlV()
await verifyNodeLoaded(comfyPage, 1)
})
test('Copies and pastes group node into a newly created blank workflow', async ({
comfyPage
}) => {
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
await comfyPage.ctrlV()
await verifyNodeLoaded(comfyPage, 1)
})
test('Copies and pastes group node across different workflows', async ({
comfyPage
}) => {
await comfyPage.loadWorkflow('default')
await comfyPage.ctrlV()
await verifyNodeLoaded(comfyPage, 1)
})
test('Serializes group node after copy and paste across workflows', async ({
comfyPage
}) => {
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
await comfyPage.ctrlV()
const currentGraphState = await comfyPage.page.evaluate(() =>
window['app'].graph.serialize()
)
await test.step('Load workflow containing a group node pasted from a different workflow', async () => {
await comfyPage.page.evaluate(
(workflow) => window['app'].loadGraphData(workflow),
currentGraphState
)
await comfyPage.nextFrame()
await verifyNodeLoaded(comfyPage, 1)
})
})
})
test.describe('Keybindings', () => {
test('Convert to group node, no selection', async ({ comfyPage }) => {
expect(await comfyPage.getVisibleToastCount()).toBe(0)
await comfyPage.page.keyboard.press('Alt+g')
await comfyPage.page.waitForTimeout(300)
expect(await comfyPage.getVisibleToastCount()).toBe(1)
})
test('Convert to group node, selected 1 node', async ({ comfyPage }) => {
expect(await comfyPage.getVisibleToastCount()).toBe(0)
await comfyPage.clickTextEncodeNode1()
await comfyPage.page.keyboard.press('Alt+g')
await comfyPage.page.waitForTimeout(300)
expect(await comfyPage.getVisibleToastCount()).toBe(1)
})
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -0,0 +1,49 @@
import type { Page, Locator } from '@playwright/test'
import type { AutoQueueMode } from '../../src/stores/queueStore'
export class ComfyActionbar {
public readonly root: Locator
public readonly queueButton: ComfyQueueButton
constructor(public readonly page: Page) {
this.root = page.locator('.actionbar')
this.queueButton = new ComfyQueueButton(this)
}
async isDocked() {
const className = await this.root.getAttribute('class')
return className?.includes('is-docked') ?? false
}
}
class ComfyQueueButton {
public readonly root: Locator
public readonly primaryButton: Locator
public readonly dropdownButton: Locator
constructor(public readonly actionbar: ComfyActionbar) {
this.root = actionbar.root.getByTestId('queue-button')
this.primaryButton = this.root.locator('.p-splitbutton-button')
this.dropdownButton = this.root.locator('.p-splitbutton-dropdown')
}
public async toggleOptions() {
await this.dropdownButton.click()
return new ComfyQueueButtonOptions(this.actionbar.page)
}
}
class ComfyQueueButtonOptions {
constructor(public readonly page: Page) {}
public async setMode(mode: AutoQueueMode) {
await this.page.evaluate((mode) => {
window['app'].extensionManager.queueSettings.mode = mode
}, mode)
}
public async getMode() {
return await this.page.evaluate(() => {
return window['app'].extensionManager.queueSettings.mode
})
}
}

Some files were not shown because too many files have changed in this diff Show More