Cleanup: Properties Panel (#7137)

## Summary

- Code cleanup
- Copy, padding, color, alignment of components
- Subgraph Edit mode changes
- Partial fix for the Node Info location (need to do context menu still)
- Editing node title

### Still to-do

- Bi-directionality in values

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7137-WIP-Cleanup-Properties-Panel-2be6d73d3650813e9430f6bcb09dfb4d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Alexander Brown
2025-12-05 21:33:52 -08:00
committed by GitHub
parent cde49d5b64
commit f74c176423
45 changed files with 419 additions and 386 deletions

View File

@@ -27,18 +27,32 @@ dotenv.config()
type WorkspaceStore = ReturnType<typeof useWorkspaceStore>
class ComfyPropertiesPanel {
readonly root: Locator
readonly panelTitle: Locator
readonly searchBox: Locator
constructor(readonly page: Page) {
this.root = page.getByTestId('properties-panel')
this.panelTitle = this.root.locator('h3')
this.searchBox = this.root.getByPlaceholder('Search...')
}
}
class ComfyMenu {
private _nodeLibraryTab: NodeLibrarySidebarTab | null = null
private _workflowsTab: WorkflowsSidebarTab | null = null
private _topbar: Topbar | null = null
public readonly sideToolbar: Locator
public readonly propertiesPanel: ComfyPropertiesPanel
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.propertiesPanel = new ComfyPropertiesPanel(page)
this.saveButton = page
.locator('button[title="Save the current workflow"]')
.nth(0)

View File

@@ -5,14 +5,18 @@ import type { AutoQueueMode } from '../../src/stores/queueStore'
export class ComfyActionbar {
public readonly root: Locator
public readonly queueButton: ComfyQueueButton
public readonly propertiesButton: Locator
constructor(public readonly page: Page) {
this.root = page.locator('.actionbar')
this.root = page.locator('.actionbar-container')
this.queueButton = new ComfyQueueButton(this)
this.propertiesButton = this.root.getByLabel('Toggle properties panel')
}
async isDocked() {
const className = await this.root.getAttribute('class')
const className = await this.root
.locator('.actionbar')
.getAttribute('class')
return className?.includes('static') ?? false
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -52,13 +52,10 @@ test.describe('Node Help', () => {
await expect(helpButton).toBeVisible()
await helpButton.click()
// Verify that the node library sidebar is opened
await expect(
comfyPage.menu.nodeLibraryTab.selectedTabButton
).toBeVisible()
// Verify that the help page is shown for the correct node
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
await expect(helpPage).toContainText('KSampler')
await expect(helpPage.locator('.node-help-content')).toBeVisible()
})
@@ -170,7 +167,9 @@ test.describe('Node Help', () => {
await helpButton.click()
// Verify loading spinner is shown
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
await expect(helpPage.locator('.p-progressspinner')).toBeVisible()
// Wait for content to load
@@ -200,7 +199,9 @@ test.describe('Node Help', () => {
await helpButton.click()
// Verify fallback content is shown (description, inputs, outputs)
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
await expect(helpPage).toContainText('Description')
await expect(helpPage).toContainText('Inputs')
await expect(helpPage).toContainText('Outputs')
@@ -233,7 +234,9 @@ test.describe('Node Help', () => {
)
await helpButton.click()
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
await expect(helpPage).toContainText('KSampler Documentation')
// Check that relative image paths are prefixed correctly
@@ -281,7 +284,9 @@ test.describe('Node Help', () => {
)
await helpButton.click()
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
// Check relative video paths are prefixed
const relativeVideo = helpPage.locator('video[src*="demo.mp4"]')
@@ -354,7 +359,9 @@ This is documentation for a custom node.
if (await helpButton.isVisible()) {
await helpButton.click()
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
await expect(helpPage).toContainText('Custom Node Documentation')
// Check image path for custom nodes
@@ -394,7 +401,9 @@ This is documentation for a custom node.
)
await helpButton.click()
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
// Dangerous elements should be removed
await expect(helpPage.locator('script')).toHaveCount(0)
@@ -461,7 +470,9 @@ This is English documentation.
)
await helpButton.click()
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
await expect(helpPage).toContainText('KSamplerード')
await expect(helpPage).toContainText('これは日本語のドキュメントです')
@@ -484,7 +495,9 @@ This is English documentation.
)
await helpButton.click()
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
// Should show fallback content (node description)
await expect(helpPage).toBeVisible()
@@ -528,7 +541,9 @@ This is English documentation.
)
await helpButton.click()
const helpPage = comfyPage.page.locator('.sidebar-content-container')
const helpPage = comfyPage.page.locator(
'[data-testid="properties-panel"]'
)
await expect(helpPage).toContainText('KSampler Help')
await expect(helpPage).toContainText('This is KSampler documentation')

View File

@@ -0,0 +1,35 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '../../fixtures/ComfyPage'
test.describe('Properties panel', () => {
test('opens and updates title based on selection', async ({ comfyPage }) => {
await comfyPage.actionbar.propertiesButton.click()
const { propertiesPanel } = comfyPage.menu
await expect(propertiesPanel.panelTitle).toContainText(
'No node(s) selected'
)
await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)'])
await expect(propertiesPanel.panelTitle).toContainText('3 nodes selected')
await expect(propertiesPanel.root.getByText('KSampler')).toHaveCount(1)
await expect(
propertiesPanel.root.getByText('CLIP Text Encode (Prompt)')
).toHaveCount(2)
await propertiesPanel.searchBox.fill('seed')
await expect(propertiesPanel.root.getByText('KSampler')).toHaveCount(1)
await expect(
propertiesPanel.root.getByText('CLIP Text Encode (Prompt)')
).toHaveCount(0)
await propertiesPanel.searchBox.fill('')
await expect(propertiesPanel.root.getByText('KSampler')).toHaveCount(1)
await expect(
propertiesPanel.root.getByText('CLIP Text Encode (Prompt)')
).toHaveCount(2)
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB