[backport] Refactor app menu items and update side toolbar (#4665, #4946) (#5030)

* Refactor app menu items (#4665)

* Restructures the application menu
- rename Workflow to File
- move new & template items to top level
- add View menu and related sub items

Commands
- add "active" state getter shown as checkmark in the menu

Node side panel
- add refresh node defs
- change reset view icon

Help center
- change to use store for visibility

Fixes
- Fix bug with mouse down where if you drag mouse out, mouse up wasn't caught
- Fix issue with canvas info setting not triggering a redraw on change

* Fix missing translation warnings

* Add separator under new

* tidy

* Update locales [skip ci]

* fix some tests

* fix

* Hide icon if there is an active state within the menu item group

* Update locales [skip ci]

* Fix tests

* Implement feedback
- Remove queue, node lib, model lib, workflows, manager, help center
- Add minimap, link visibility

* Update locales [skip ci]

* Add plus icon on "New" menu item

* Update locales [skip ci]

* Fix test

* Fix translations

* Update locales [skip ci]

* Update locales [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>

* Update side toolbar menu (#4946)

Side toolbar menu UI updates

- Currently the template modal is very hidden. Many users do not find it
- The current icons are quite aleatory

 **What**:
- Add templates shortcut button
- Add item label in normal size
- Use custom icon

Critical design decisions or edge cases that need attention:
- Sidebar tabs registered using custom icons will have their associated
command registed with an undefined icon (currently only string icons are
accepted, not components). I couldn't see anywhere directly using this
icon, but we should consider autogenerating an icon font so we can use
classes for our custom icons (or locating and updating locations to
support both icon types)

Normal mode:
<img width="621" height="1034" alt="image"
src="https://github.com/user-attachments/assets/c1d1cee2-004e-4ff8-b3fa-197329b0d2ae"
/>

Small mode:
<img width="176" height="325" alt="image"
src="https://github.com/user-attachments/assets/3824b8f6-bc96-4e62-aece-f0265113d2e3"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-4946-Update-side-toolbar-menu-24d6d73d365081c5b2bdc0ee8b61dc50)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>

---------

Co-authored-by: pythongosssss <125205205+pythongosssss@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Christian Byrne
2025-08-15 19:25:59 -07:00
committed by GitHub
parent 2ee5541b6a
commit 0f4057c8b2
48 changed files with 1477 additions and 155 deletions

View File

@@ -50,7 +50,7 @@ export class Topbar {
workflowName: string,
command: 'Save' | 'Save As' | 'Export'
) {
await this.triggerTopbarCommand(['Workflow', command])
await this.triggerTopbarCommand(['File', command])
await this.getSaveDialog().fill(workflowName)
await this.page.keyboard.press('Enter')
@@ -72,8 +72,8 @@ export class Topbar {
}
async triggerTopbarCommand(path: string[]) {
if (path.length < 2) {
throw new Error('Path is too short')
if (path.length < 1) {
throw new Error('Path cannot be empty')
}
const menu = await this.openTopbarMenu()
@@ -85,6 +85,13 @@ export class Topbar {
.locator('.p-tieredmenu-item')
.filter({ has: topLevelMenuItem })
await topLevelMenu.waitFor({ state: 'visible' })
// Handle top-level commands (like "New")
if (path.length === 1) {
await topLevelMenuItem.click()
return
}
await topLevelMenu.hover()
let currentMenu = topLevelMenu

View File

@@ -268,10 +268,7 @@ test.describe('Group Node', () => {
await comfyPage.setSetting('Comfy.ConfirmClear', false)
// Clear workflow
await comfyPage.menu.topbar.triggerTopbarCommand([
'Edit',
'Clear Workflow'
])
await comfyPage.executeCommand('Comfy.ClearWorkflow')
await comfyPage.ctrlV()
await verifyNodeLoaded(comfyPage, 1)
@@ -280,7 +277,7 @@ test.describe('Group Node', () => {
test('Copies and pastes group node into a newly created blank workflow', async ({
comfyPage
}) => {
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
await comfyPage.ctrlV()
await verifyNodeLoaded(comfyPage, 1)
})
@@ -296,7 +293,7 @@ test.describe('Group Node', () => {
test('Serializes group node after copy and paste across workflows', async ({
comfyPage
}) => {
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
await comfyPage.ctrlV()
const currentGraphState = await comfyPage.page.evaluate(() =>
window['app'].graph.serialize()

View File

@@ -684,7 +684,7 @@ test.describe('Load workflow', () => {
workflowA = generateUniqueFilename()
await comfyPage.menu.topbar.saveWorkflow(workflowA)
workflowB = generateUniqueFilename()
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
await comfyPage.menu.topbar.saveWorkflow(workflowB)
// Wait for localStorage to persist the workflow paths before reloading

View File

@@ -75,7 +75,7 @@ test.describe('Menu', () => {
test('Displays keybinding next to item', async ({ comfyPage }) => {
await comfyPage.menu.topbar.openTopbarMenu()
const workflowMenuItem = comfyPage.menu.topbar.getMenuItem('Workflow')
const workflowMenuItem = comfyPage.menu.topbar.getMenuItem('File')
await workflowMenuItem.hover()
const exportTag = comfyPage.page.locator('.keybinding-tag', {
hasText: 'Ctrl + s'

View File

@@ -18,7 +18,7 @@ test.describe('Reroute Node', () => {
[workflowName]: workflowName
})
await comfyPage.setup()
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
// Insert the workflow
const workflowsTab = comfyPage.menu.workflowsTab

View File

@@ -63,7 +63,7 @@ test.describe('Workflow Tab Thumbnails', () => {
test('Should show thumbnail when hovering over a non-active tab', async ({
comfyPage
}) => {
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
const thumbnailImg = await getTabThumbnailImage(
comfyPage,
0,
@@ -73,7 +73,7 @@ test.describe('Workflow Tab Thumbnails', () => {
})
test('Should not show thumbnail for active tab', async ({ comfyPage }) => {
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
const thumbnailImg = await getTabThumbnailImage(
comfyPage,
1,
@@ -105,7 +105,7 @@ test.describe('Workflow Tab Thumbnails', () => {
await comfyPage.nextFrame()
// Create a new workflow (tab 1) which will be empty
await comfyPage.menu.topbar.triggerTopbarCommand(['Workflow', 'New'])
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
await comfyPage.nextFrame()
// Now we have two tabs: tab 0 (default workflow with nodes) and tab 1 (empty)