Compare commits

...

3 Commits

Author SHA1 Message Date
GitHub Action
82f873b1a9 [automated] Apply ESLint and Oxfmt fixes 2026-06-12 23:44:10 +00:00
GitHub Action
46d1e0bcad [automated] Apply ESLint and Oxfmt fixes 2026-06-12 23:39:00 +00:00
Glary-Bot
0f549a9729 style: enable oxlint eslint/curly rule and fix violations
Enables the eslint/curly rule with ["multi-or-nest", "consistent"] options in .oxlintrc.json. Braces are required only for multi-line or nested blocks, with consistent brace use within if-else chains. All 3019 violations were resolved by oxlint --fix.
2026-06-12 23:35:09 +00:00
820 changed files with 3962 additions and 7649 deletions

View File

@@ -27,6 +27,7 @@
],
"rules": {
"no-async-promise-executor": "off",
"curly": ["error", "multi-or-nest", "consistent"],
"no-console": [
"error",
{

View File

@@ -278,9 +278,8 @@ export class ComfyPage {
}
)
if (resp.status() !== 200) {
if (resp.status() !== 200)
throw new Error(`Failed to setup settings: ${await resp.text()}`)
}
}
async setup({
@@ -358,9 +357,7 @@ export class ComfyPage {
}
async idleFrames(count: number) {
for (let i = 0; i < count; i++) {
await this.nextFrame()
}
for (let i = 0; i < count; i++) await this.nextFrame()
}
async delay(ms: number) {
@@ -382,9 +379,7 @@ export class ComfyPage {
const { runInCI = false, fullPage = false } = options
// Skip in CI unless explicitly requested
if (process.env.CI && !runInCI) {
return
}
if (process.env.CI && !runInCI) return
const testInfo = comfyPageFixture.info()
await testInfo.attach(name, {
@@ -486,9 +481,7 @@ export const comfyPageFixture = base.extend<{
initialSettings: [{}, { option: true }],
page: async ({ page, browserName }, use) => {
if (browserName !== 'chromium' || !COLLECT_COVERAGE) {
return use(page)
}
if (browserName !== 'chromium' || !COLLECT_COVERAGE) return use(page)
await page.coverage.startJSCoverage({ resetOnNavigation: false })
await use(page)
@@ -547,19 +540,14 @@ export const comfyPageFixture = base.extend<{
console.error(e)
}
if (testInfo.tags.includes('@cloud')) {
await comfyPage.cloudAuth.mockAuth()
}
if (testInfo.tags.includes('@cloud')) await comfyPage.cloudAuth.mockAuth()
if (Object.keys(initialFeatureFlags).length > 0) {
if (Object.keys(initialFeatureFlags).length > 0)
await comfyPage.featureFlags.seedFlags(initialFeatureFlags)
}
await comfyPage.setup()
if (isVueNodes) {
await comfyPage.vueNodes.waitForNodes()
}
if (isVueNodes) await comfyPage.vueNodes.waitForNodes()
const needsPerf =
testInfo.tags.includes('@perf') || testInfo.tags.includes('@audit')

View File

@@ -42,9 +42,8 @@ class ComfyQueueButton {
public async openOptions() {
const options = new ComfyQueueButtonOptions(this.actionbar.page)
if (!(await options.menu.isVisible())) {
await this.dropdownButton.click()
}
if (!(await options.menu.isVisible())) await this.dropdownButton.click()
return options
}
}

View File

@@ -67,16 +67,14 @@ export class BottomPanel {
async toggleLogs() {
await this.toggleButton.click()
await this.logs.tab.waitFor({ state: 'visible' })
if ((await this.logs.tab.getAttribute('aria-selected')) !== 'true') {
if ((await this.logs.tab.getAttribute('aria-selected')) !== 'true')
await this.logs.tab.click()
}
}
async resizeByDragging(deltaY: number): Promise<void> {
const gutterBox = await this.resizeGutter.boundingBox()
if (!gutterBox) {
if (!gutterBox)
throw new Error('Bottom panel resize gutter should have layout')
}
const gutterCenterX = gutterBox.x + gutterBox.width / 2
const gutterCenterY = gutterBox.y + gutterBox.height / 2

View File

@@ -19,15 +19,13 @@ class SidebarTab {
}
async open() {
if (await this.selectedTabButton.isVisible()) {
return
}
if (await this.selectedTabButton.isVisible()) return
await this.tabButton.click()
}
async close() {
if (!this.tabButton.isVisible()) {
return
}
if (!this.tabButton.isVisible()) return
await this.tabButton.click()
}
}
@@ -54,9 +52,7 @@ export class NodeLibrarySidebarTab extends SidebarTab {
}
override async close() {
if (!this.tabButton.isVisible()) {
return
}
if (!this.tabButton.isVisible()) return
await this.tabButton.click()
await this.nodeLibraryTree.waitFor({ state: 'hidden' })
@@ -124,9 +120,7 @@ export class NodeLibrarySidebarTabV2 extends SidebarTab {
async expandFolder(folderName: string) {
const folder = this.getFolder(folderName)
const isExpanded = await folder.getAttribute('aria-expanded')
if (isExpanded !== 'true') {
await folder.click()
}
if (isExpanded !== 'true') await folder.click()
}
override async open() {
@@ -395,17 +389,15 @@ export class AssetsSidebarTab extends SidebarTab {
await this.dismissToasts()
await super.open()
await this.generatedTab.waitFor({ state: 'visible' })
if (waitForAssets) {
await this.waitForAssets()
}
if (waitForAssets) await this.waitForAssets()
}
/** Dismiss all visible toast notifications by clicking their close buttons. */
async dismissToasts() {
const closeButtons = this.page.locator('.p-toast-close-button')
for (const btn of await closeButtons.all()) {
for (const btn of await closeButtons.all())
await btn.click().catch(() => {})
}
// Wait for all toast elements to fully animate out and detach from DOM
await expect(this.page.locator('.p-toast-message'))
.toHaveCount(0)
@@ -466,10 +458,8 @@ export class AssetsSidebarTab extends SidebarTab {
}
async waitForAssets(count?: number) {
if (count !== undefined) {
await expect(this.assetCards).toHaveCount(count)
} else {
if (count !== undefined) await expect(this.assetCards).toHaveCount(count)
else
await this.assetCards.first().waitFor({ state: 'visible', timeout: 5000 })
}
}
}

View File

@@ -36,9 +36,8 @@ export class Topbar {
* Get a menu item by its label, optionally within a specific parent container
*/
getMenuItem(itemLabel: string, parent?: Locator): Locator {
if (parent) {
if (parent)
return parent.locator(`.p-tieredmenu-item:has-text("${itemLabel}")`)
}
return this.page.locator(`.p-menubar-item-label:text-is("${itemLabel}")`)
}
@@ -119,9 +118,7 @@ export class Topbar {
const confirmationDialog = this.page
.getByRole('dialog')
.filter({ hasText: 'Overwrite' })
if (await confirmationDialog.isVisible()) {
return
}
if (await confirmationDialog.isVisible()) return
}
async openTopbarMenu() {
@@ -201,9 +198,7 @@ export class Topbar {
}
async triggerTopbarCommand(path: string[]) {
if (path.length < 1) {
throw new Error('Path cannot be empty')
}
if (path.length < 1) throw new Error('Path cannot be empty')
const menu = await this.openTopbarMenu()
const tabName = path[0]

View File

@@ -37,9 +37,8 @@ type AssetOperator = (config: AssetConfig) => AssetConfig
function addAssets(config: AssetConfig, newAssets: Asset[]): AssetConfig {
const merged = new Map(config.assets)
for (const asset of newAssets) {
merged.set(asset.id, asset)
}
for (const asset of newAssets) merged.set(asset.id, asset)
return { ...config, assets: merged }
}
export function withModels(
@@ -221,9 +220,7 @@ export class AssetHelper {
const offset = parseInt(url.searchParams.get('offset') ?? '0', 10)
let filtered = this.getFilteredAssets(includeTags, excludeTags)
if (limit > 0) {
filtered = filtered.slice(offset, offset + limit)
}
if (limit > 0) filtered = filtered.slice(offset, offset + limit)
const response: ListAssetsResponse = {
assets: filtered,
@@ -288,9 +285,9 @@ export class AssetHelper {
}
async clearMocks(): Promise<void> {
for (const { pattern, handler } of this.routeHandlers) {
for (const { pattern, handler } of this.routeHandlers)
await this.page.unroute(pattern, handler)
}
this.routeHandlers = []
this.store.clear()
this.mutations = []

View File

@@ -136,17 +136,15 @@ export function createJobsWithExecutionTimes(
function parseLimit(url: URL, total: number): number {
const value = Number(url.searchParams.get('limit'))
if (!Number.isInteger(value) || value <= 0) {
return total
}
if (!Number.isInteger(value) || value <= 0) return total
return value
}
function parseOffset(url: URL): number {
const value = Number(url.searchParams.get('offset'))
if (!Number.isInteger(value) || value < 0) {
return 0
}
if (!Number.isInteger(value) || value < 0) return 0
return value
}
@@ -181,9 +179,7 @@ export class AssetsHelper {
async mockOutputHistory(jobs: RawJobListItem[]): Promise<void> {
this.generatedJobs = [...jobs]
if (this.jobsRouteHandler) {
return
}
if (this.jobsRouteHandler) return
this.jobsRouteHandler = async (route: Route) => {
const url = new URL(route.request().url())
@@ -254,9 +250,7 @@ export class AssetsHelper {
async mockCloudAssets(response: ListAssetsResponse): Promise<void> {
this.cloudAssetsResponse = response
if (this.cloudAssetsRouteHandler) {
return
}
if (this.cloudAssetsRouteHandler) return
this.cloudAssetsRouteHandler = async (route: Route) => {
await route.fulfill({
@@ -286,9 +280,7 @@ export class AssetsHelper {
this.assetExportRequests = []
this.assetExportResponse = response
if (this.assetExportRouteHandler) {
return this.assetExportRequests
}
if (this.assetExportRouteHandler) return this.assetExportRequests
this.assetExportRouteHandler = async (route: Route) => {
this.assetExportRequests.push(
@@ -311,9 +303,7 @@ export class AssetsHelper {
const pattern = `**/api/jobs/${encodeURIComponent(jobId)}`
const existingHandler = this.jobDetailRouteHandlers.get(pattern)
if (existingHandler) {
await this.page.unroute(pattern, existingHandler)
}
if (existingHandler) await this.page.unroute(pattern, existingHandler)
const handler = async (route: Route) => {
await route.fulfill({
@@ -330,9 +320,7 @@ export class AssetsHelper {
async mockInputFiles(files: string[]): Promise<void> {
this.importedFiles = [...files]
if (this.inputFilesRouteHandler) {
return
}
if (this.inputFilesRouteHandler) return
this.inputFilesRouteHandler = async (route: Route) => {
await route.fulfill({
@@ -424,9 +412,9 @@ export class AssetsHelper {
this.deleteHistoryRouteHandler = null
}
for (const [pattern, handler] of this.jobDetailRouteHandlers) {
for (const [pattern, handler] of this.jobDetailRouteHandlers)
await this.page.unroute(pattern, handler)
}
this.jobDetailRouteHandlers.clear()
}
}

View File

@@ -12,18 +12,17 @@ export class CanvasHelper {
) {}
async resetView(): Promise<void> {
if (await this.resetViewButton.isVisible()) {
if (await this.resetViewButton.isVisible())
await this.resetViewButton.click()
}
await this.page.mouse.move(10, 10)
await nextFrame(this.page)
}
async zoom(deltaY: number, steps: number = 1): Promise<void> {
await this.page.mouse.move(10, 10)
for (let i = 0; i < steps; i++) {
await this.page.mouse.wheel(0, deltaY)
}
for (let i = 0; i < steps; i++) await this.page.mouse.wheel(0, deltaY)
await nextFrame(this.page)
}

View File

@@ -75,9 +75,8 @@ export class CloudAuthHelper {
request.onerror = () => reject(request.error)
request.onupgradeneeded = () => {
const db = request.result
if (!db.objectStoreNames.contains(STORE_NAME)) {
if (!db.objectStoreNames.contains(STORE_NAME))
db.createObjectStore(STORE_NAME)
}
}
request.onsuccess = () => {
const db = request.result
@@ -87,9 +86,8 @@ export class CloudAuthHelper {
upgradeReq.onerror = () => reject(upgradeReq.error)
upgradeReq.onupgradeneeded = () => {
const upgradedDb = upgradeReq.result
if (!upgradedDb.objectStoreNames.contains(STORE_NAME)) {
if (!upgradedDb.objectStoreNames.contains(STORE_NAME))
upgradedDb.createObjectStore(STORE_NAME)
}
}
upgradeReq.onsuccess = () => {
const upgradedDb = upgradeReq.result

View File

@@ -147,9 +147,7 @@ export class DragDropHelper {
}
}, evaluateParams)
if (uploadResponsePromise) {
await uploadResponsePromise
}
if (uploadResponsePromise) await uploadResponsePromise
await nextFrame(this.page)
}

View File

@@ -15,9 +15,8 @@ export class FeatureFlagHelper {
*/
async seedFlags(flags: Record<string, unknown>): Promise<void> {
await this.page.addInitScript((flagMap: Record<string, unknown>) => {
for (const [key, value] of Object.entries(flagMap)) {
for (const [key, value] of Object.entries(flagMap))
localStorage.setItem(`ff:${key}`, JSON.stringify(value))
}
}, flags)
}
@@ -28,9 +27,8 @@ export class FeatureFlagHelper {
*/
async setFlags(flags: Record<string, unknown>): Promise<void> {
await this.page.evaluate((flagMap: Record<string, unknown>) => {
for (const [key, value] of Object.entries(flagMap)) {
for (const [key, value] of Object.entries(flagMap))
localStorage.setItem(`ff:${key}`, JSON.stringify(value))
}
}, flags)
}

View File

@@ -94,9 +94,9 @@ class MaskEditorHelper {
if (!ctx) return null
const data = ctx.getImageData(0, 0, canvas.width, canvas.height)
let nonTransparentPixels = 0
for (let i = 3; i < data.data.length; i += 4) {
for (let i = 3; i < data.data.length; i += 4)
if (data.data[i] > 0) nonTransparentPixels++
}
return { nonTransparentPixels, totalPixels: data.data.length / 4 }
}, canvasIndex)
}

View File

@@ -97,9 +97,8 @@ export class ModelLibraryHelper {
async mockFoldersWithFiles(config: Record<string, string[]>): Promise<void> {
const folderNames = Object.keys(config)
await this.mockModelFolders(createMockModelFolders(folderNames))
for (const [folder, files] of Object.entries(config)) {
for (const [folder, files] of Object.entries(config))
await this.mockModelFiles(folder, createMockModelFiles(files))
}
}
async clearMocks(): Promise<void> {

View File

@@ -157,9 +157,7 @@ export class NodeOperationsHelper {
try {
for (const nodeTitle of nodeTitles) {
const nodes = await this.getNodeRefsByTitle(nodeTitle)
for (const node of nodes) {
await node.click('title')
}
for (const node of nodes) await node.click('title')
}
} finally {
await this.page.keyboard.up('Control')

View File

@@ -93,9 +93,9 @@ export class PerformanceHelper {
if (!state) return 0
// Flush any queued-but-undelivered entries into our accumulator
for (const entry of state.observer.takeRecords()) {
for (const entry of state.observer.takeRecords())
if (entry.duration > 50) state.tbtMs += entry.duration - 50
}
const result = state.tbtMs
state.tbtMs = 0
return result
@@ -124,9 +124,9 @@ export class PerformanceHelper {
return
}
const durations: number[] = []
for (let i = 1; i < timestamps.length; i++) {
for (let i = 1; i < timestamps.length; i++)
durations.push(timestamps[i] - timestamps[i - 1])
}
resolve(durations)
}
}
@@ -153,9 +153,8 @@ export class PerformanceHelper {
observer: PerformanceObserver
tbtMs: number
}
for (const entry of list.getEntries()) {
for (const entry of list.getEntries())
if (entry.duration > 50) self.tbtMs += entry.duration - 50
}
}),
tbtMs: 0
}

View File

@@ -189,9 +189,9 @@ class PublishApiHelper {
}
async cleanup(): Promise<void> {
for (const { pattern, handler } of this.routeHandlers) {
for (const { pattern, handler } of this.routeHandlers)
await this.page.unroute(pattern, handler)
}
this.routeHandlers = []
}
@@ -207,9 +207,9 @@ class PublishApiHelper {
const handlers = this.routeHandlers.filter(
(route) => route.pattern === pattern
)
for (const { handler } of handlers) {
for (const { handler } of handlers)
await this.page.unroute(pattern, handler)
}
this.routeHandlers = this.routeHandlers.filter(
(route) => route.pattern !== pattern
)

View File

@@ -61,13 +61,10 @@ export class SubgraphHelper {
slotType === 'input' ? subgraph.inputNode : subgraph.outputNode
const slots = slotType === 'input' ? subgraph.inputs : subgraph.outputs
if (!node) {
throw new Error(`No ${slotType} node found in subgraph`)
}
if (!node) throw new Error(`No ${slotType} node found in subgraph`)
if (!slots || slots.length === 0) {
if (!slots || slots.length === 0)
throw new Error(`No ${slotType} slots found in subgraph`)
}
// Filter slots based on target name and action type
const slotsToTry = targetSlotName
@@ -115,9 +112,8 @@ export class SubgraphHelper {
} else if (action === 'doubleClick') {
// Double-click: use first slot with bounding rect center
const slot = slotsToTry[0]
if (!slot.boundingRect) {
if (!slot.boundingRect)
throw new Error(`${slotType} slot bounding rect not found`)
}
const rect = slot.boundingRect
const testX = rect[0] + rect[2] / 2 // x + width/2
@@ -398,9 +394,7 @@ export class SubgraphHelper {
await this.comfyPage.nextFrame()
await expect.poll(async () => this.isInSubgraph()).toBe(false)
if (this.comfyPage.isVueNodes) {
await this.comfyPage.vueNodes.waitForNodes()
}
if (this.comfyPage.isVueNodes) await this.comfyPage.vueNodes.waitForNodes()
}
async countGraphPseudoPreviewEntries(): Promise<number> {
@@ -461,11 +455,9 @@ export class SubgraphHelper {
}
async removeSlot(type: 'input' | 'output', slotName?: string): Promise<void> {
if (type === 'input') {
await this.rightClickInputSlot(slotName)
} else {
await this.rightClickOutputSlot(slotName)
}
if (type === 'input') await this.rightClickInputSlot(slotName)
else await this.rightClickOutputSlot(slotName)
await this.comfyPage.contextMenu.clickLitegraphMenuItem('Remove Slot')
await this.comfyPage.contextMenu.waitForHidden()
}
@@ -592,9 +584,7 @@ export class SubgraphHelper {
const warnings: string[] = []
const handler = (msg: ConsoleMessage) => {
const text = msg.text()
if (patterns.some((p) => text.includes(p))) {
warnings.push(text)
}
if (patterns.some((p) => text.includes(p))) warnings.push(text)
}
page.on('console', handler)
return { warnings, dispose: () => page.off('console', handler) }

View File

@@ -113,9 +113,9 @@ export class TemplateHelper {
}
async clearMocks(): Promise<void> {
for (const { pattern, handler } of this.routeHandlers) {
for (const { pattern, handler } of this.routeHandlers)
await this.page.unroute(pattern, handler)
}
this.routeHandlers = []
this.templates = []
this.index = null

View File

@@ -27,9 +27,7 @@ export class ToastHelper {
const toastCloseButtons = await this.page
.locator('.p-toast-close-button')
.all()
for (const button of toastCloseButtons) {
await button.click()
}
for (const button of toastCloseButtons) await button.click()
// Assert all toasts are closed
await expect(this.visibleToasts).toHaveCount(0)

View File

@@ -18,9 +18,10 @@ export class UserDataHelper {
`${this.baseUrl}/api/userdata/${encodeURIComponent(file)}`,
{ method: 'DELETE', headers: { 'Comfy-User': this.userId } }
)
if (!res.ok() && res.status() !== 404)
if (!res.ok() && res.status() !== 404) {
throw new Error(
`Failed to delete userdata file "${file}": HTTP ${res.status()}`
)
}
}
}

View File

@@ -83,9 +83,8 @@ export class WorkflowHelper {
if (
typeof index.updatedAt === 'number' &&
index.updatedAt >= indexUpdatedSince
) {
)
return true
}
} catch {
// Ignore malformed storage while waiting for persistence.
}
@@ -120,9 +119,8 @@ export class WorkflowHelper {
)
await this.waitForWorkflowIdle()
await this.comfyPage.nextFrame()
if (test.info().tags.includes('@vue-nodes')) {
if (test.info().tags.includes('@vue-nodes'))
await this.comfyPage.vueNodes.waitForNodes()
}
}
async deleteWorkflow(

View File

@@ -145,12 +145,10 @@ class JobsRouteMocker {
}
async mockJobsScenario({ history, queue }: JobsScenario): Promise<void> {
if (history) {
if (history)
await this.mockJobsHistory(history, defaultScenarioHistoryLimit)
}
if (queue) {
await this.mockJobsQueue(queue)
}
if (queue) await this.mockJobsQueue(queue)
}
async mockJobsList(route: JobsListRoute): Promise<void> {

View File

@@ -219,9 +219,8 @@ async function mockSharedWorkflowImportFlow(
body: JSON.stringify(response)
})
if (isAfterImportPublicInclusiveInputAssetRequest) {
if (isAfterImportPublicInclusiveInputAssetRequest)
resolvePublicInclusiveInputAssetResponseAfterImport()
}
})
return {

View File

@@ -56,9 +56,8 @@ export async function measureSelectionBounds(
const node = window.app!.graph._nodes.find(
(n: { id: number | string }) => String(n.id) === id
)
if (!node) {
throw new Error(`Node ${id} not found in graph`)
}
if (!node) throw new Error(`Node ${id} not found in graph`)
const rect = node.boundingRect
nodeVisualBounds[id] = {
x: rect[0],
@@ -74,9 +73,8 @@ export async function measureSelectionBounds(
'[data-testid="subgraph-enter-button"], [data-testid="node-footer"]'
)
let bottom = domRect.bottom
for (const footerEl of footerEls) {
for (const footerEl of footerEls)
bottom = Math.max(bottom, footerEl.getBoundingClientRect().bottom)
}
nodeVisualBounds[id] = {
x: (domRect.left - canvasRect.left) / ds.scale - ds.offset[0],

View File

@@ -46,9 +46,8 @@ export async function setupBuilder(
await appMode.enterBuilder()
await appMode.steps.goToInputs()
for (const name of inputWidgets) {
for (const name of inputWidgets)
await appMode.select.selectInputWidget(inputNodeTitle, name)
}
await appMode.steps.goToOutputs()
await appMode.select.selectOutputNode('Save Image')

View File

@@ -14,11 +14,8 @@ function makeMatcher<T>(
) {
await expect(async () => {
const value = await getValue(node)
if (this.isNot) {
expect(value, 'Node is ' + type).not.toBeTruthy()
} else {
expect(value, 'Node is not ' + type).toBeTruthy()
}
if (this.isNot) expect(value, 'Node is ' + type).not.toBeTruthy()
else expect(value, 'Node is not ' + type).toBeTruthy()
}).toPass({ timeout: 5000, ...options })
return {
pass: !this.isNot,

View File

@@ -28,9 +28,9 @@ export async function fitToViewInstant(
const canvas = app.canvas
const items = (() => {
if (selectionOnly && canvas.selectedItems?.size) {
if (selectionOnly && canvas.selectedItems?.size)
return Array.from(canvas.selectedItems)
}
try {
return Array.from(canvas.positionableItems ?? [])
} catch {

View File

@@ -35,22 +35,18 @@ export class SubgraphSlotReference {
const slots =
type === 'input' ? currentGraph.inputs : currentGraph.outputs
if (!slots || slots.length === 0) {
if (!slots || slots.length === 0)
throw new Error(`No ${type} slots found in subgraph`)
}
// Find the specific slot or use the first one if no name specified
const slot = slotName
? slots.find((s) => s.name === slotName)
: slots[0]
if (!slot) {
throw new Error(`${type} slot '${slotName}' not found`)
}
if (!slot) throw new Error(`${type} slot '${slotName}' not found`)
if (!slot.pos) {
if (!slot.pos)
throw new Error(`${type} slot '${slotName}' has no position`)
}
// Convert from offset to canvas coordinates
const canvasPos = window.app!.canvas.ds.convertOffsetToCanvas([
@@ -83,9 +79,7 @@ export class SubgraphSlotReference {
const node =
type === 'input' ? currentGraph.inputNode : currentGraph.outputNode
if (!node) {
throw new Error(`No ${type} node found in subgraph`)
}
if (!node) throw new Error(`No ${type} node found in subgraph`)
// Convert from offset to canvas coordinates
const canvasPos = window.app!.canvas.ds.convertOffsetToCanvas([
@@ -148,9 +142,8 @@ class NodeSlotReference {
([type, id, index]) => {
const node = window.app!.canvas.graph!.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
if (type === 'input') {
return node.inputs[index].link == null ? 0 : 1
}
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
@@ -161,11 +154,8 @@ class NodeSlotReference {
([type, id, index]) => {
const node = window.app!.canvas.graph!.getNodeById(id)
if (!node) throw new Error(`Node ${id} not found.`)
if (type === 'input') {
node.disconnectInput(index)
} else {
node.disconnectOutput(index)
}
if (type === 'input') node.disconnectInput(index)
else node.disconnectOutput(index)
},
[this.type, this.node.id, this.index] as const
)
@@ -426,9 +416,8 @@ export class NodeReference {
const widgetIndex =
node.widgets?.findIndex((widget) => widget.name === widgetName) ?? -1
if (widgetIndex < 0) {
if (widgetIndex < 0)
throw new Error(`Widget "${widgetName}" not found on node ${id}`)
}
return widgetIndex
},
@@ -460,14 +449,11 @@ export class NodeReference {
}
const moveMouseToEmptyArea = options?.moveMouseToEmptyArea
if (options) {
delete options.moveMouseToEmptyArea
}
if (options) delete options.moveMouseToEmptyArea
await this.comfyPage.canvasOps.mouseClickAt(clickPos, options)
if (moveMouseToEmptyArea) {
if (moveMouseToEmptyArea)
await this.comfyPage.canvasOps.moveMouseToEmptyArea()
}
}
async copy() {
await this.click('title')

View File

@@ -27,9 +27,8 @@ export async function hasCanvasContent(canvas: Locator): Promise<boolean> {
const ctx = el.getContext('2d')
if (!ctx) return false
const { data } = ctx.getImageData(0, 0, el.width, el.height)
for (let i = 3; i < data.length; i += 4) {
if (data[i] > 0) return true
}
for (let i = 3; i < data.length; i += 4) if (data[i] > 0) return true
return false
})
}
@@ -58,14 +57,12 @@ export async function triggerSerialization(page: Page): Promise<void> {
}
const widgetIndex = node.widgets?.findIndex((w) => w.name === 'mask') ?? -1
if (widgetIndex === -1) {
if (widgetIndex === -1)
throw new Error('Widget "mask" not found on target node 1.')
}
const widget = node.widgets?.[widgetIndex]
if (!widget) {
if (!widget)
throw new Error(`Widget index ${widgetIndex} not found on target node 1.`)
}
if (typeof widget.serializeValue !== 'function') {
throw new Error(

View File

@@ -53,15 +53,14 @@ export function preview3dRestoreCameraStatesMatch(
a: unknown,
b: unknown
): boolean {
if (!isPreview3dCameraStatePayload(a) || !isPreview3dCameraStatePayload(b)) {
if (!isPreview3dCameraStatePayload(a) || !isPreview3dCameraStatePayload(b))
return false
}
if (a.cameraType !== b.cameraType) return false
const zoomA = typeof a.zoom === 'number' ? a.zoom : 0
const zoomB = typeof b.zoom === 'number' ? b.zoom : 0
if (Math.abs(zoomA - zoomB) > PREVIEW3D_CAMERA_ZOOM_RESTORE_EPS) {
return false
}
if (Math.abs(zoomA - zoomB) > PREVIEW3D_CAMERA_ZOOM_RESTORE_EPS) return false
return (
vecWithinEps(a.position, b.position, PREVIEW3D_CAMERA_AXIS_RESTORE_EPS) &&
vecWithinEps(a.target, b.target, PREVIEW3D_CAMERA_AXIS_RESTORE_EPS)
@@ -73,9 +72,9 @@ export function preview3dCameraStatesDiffer(
b: unknown,
eps: number
): boolean {
if (!isPreview3dCameraStatePayload(a) || !isPreview3dCameraStatePayload(b)) {
if (!isPreview3dCameraStatePayload(a) || !isPreview3dCameraStatePayload(b))
return true
}
if (a.cameraType !== b.cameraType) return true
const zoomA = typeof a.zoom === 'number' ? a.zoom : 0
const zoomB = typeof b.zoom === 'number' ? b.zoom : 0

View File

@@ -7,9 +7,7 @@ export async function openMoreOptionsMenu(
nodeTitle: string
) {
const nodes = await comfyPage.nodeOps.getNodeRefsByTitle(nodeTitle)
if (nodes.length === 0) {
throw new Error(`No "${nodeTitle}" nodes found`)
}
if (nodes.length === 0) throw new Error(`No "${nodeTitle}" nodes found`)
await nodes[0].centerOnNode()
await nodes[0].click('title')

View File

@@ -103,9 +103,8 @@ export class VueNodeFixture {
}> {
await this.header.click()
const box = await this.boundingBox()
if (!box) {
throw new Error('Node bounding box not found after select')
}
if (!box) throw new Error('Node bounding box not found after select')
return box
}

View File

@@ -83,9 +83,7 @@ test.describe('Actionbar', { tag: '@ui' }, () => {
// Trigger a bunch of changes
const START = 32
const END = 64
for (let i = START; i <= END; i += 8) {
await triggerChange(i)
}
for (let i = START; i <= END; i += 8) await triggerChange(i)
// Ensure the queued width is the first value
expect(

View File

@@ -39,9 +39,8 @@ function isClippedByAnyAncestor(el: Element): boolean {
child.bottom > p.bottom ||
child.left < p.left ||
child.right > p.right
) {
)
return true
}
}
parent = parent.parentElement
}

View File

@@ -87,8 +87,7 @@ test.describe('App mode widget values in prompt', { tag: '@ui' }, () => {
const prompt = await appMode.widgets.runAndCapturePrompt()
for (const { nodeId, widgetName, expected } of WIDGET_TEST_DATA) {
for (const { nodeId, widgetName, expected } of WIDGET_TEST_DATA)
expect(prompt[nodeId].inputs[widgetName]).toBe(expected)
}
})
})

View File

@@ -99,9 +99,9 @@ test.describe('CanvasModeSelector', { tag: '@canvas' }, () => {
test(`clicking "${mode.label}" sets canvas readOnly=${mode.isReadOnly}`, async ({
comfyPage
}) => {
if (!mode.isReadOnly) {
if (!mode.isReadOnly)
await comfyPage.command.executeCommand('Comfy.Canvas.Lock')
}
const { trigger, menu, selectItem, handItem } = getLocators(
comfyPage.page
)

View File

@@ -41,9 +41,8 @@ async function getChangeTrackerDebugState(comfyPage: ComfyPage) {
const workflow = workflowStore.workflow
.activeWorkflow as ActiveWorkflowLike | null
const tracker = workflow?.changeTracker
if (!workflow || !tracker) {
if (!workflow || !tracker)
throw new Error('Active workflow change tracker is not available')
}
const currentState = JSON.parse(
JSON.stringify(window.app!.rootGraph.serialize())

View File

@@ -46,9 +46,7 @@ test.describe('CSS Containment Audit', { tag: ['@audit'] }, () => {
test('scan large graph for containment candidates', async ({ comfyPage }) => {
await comfyPage.workflow.loadWorkflow('large-graph-workflow')
for (let i = 0; i < STABILIZATION_FRAMES; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < STABILIZATION_FRAMES; i++) await comfyPage.nextFrame()
// Walk the DOM and find candidates
const candidates = await comfyPage.page.evaluate((): ContainCandidate[] => {
@@ -147,9 +145,8 @@ test.describe('CSS Containment Audit', { tag: ['@audit'] }, () => {
// Measure baseline performance (idle)
await comfyPage.perf.startMeasuring()
for (let i = 0; i < STABILIZATION_FRAMES; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < STABILIZATION_FRAMES; i++) await comfyPage.nextFrame()
const baseline = await comfyPage.perf.stopMeasuring('baseline-idle')
// Take a baseline screenshot for visual comparison
@@ -177,15 +174,12 @@ test.describe('CSS Containment Audit', { tag: ['@audit'] }, () => {
if (applied === 0) continue
for (let i = 0; i < SETTLE_FRAMES; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < SETTLE_FRAMES; i++) await comfyPage.nextFrame()
// Measure with containment
await comfyPage.perf.startMeasuring()
for (let i = 0; i < STABILIZATION_FRAMES; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < STABILIZATION_FRAMES; i++) await comfyPage.nextFrame()
const withContain = await comfyPage.perf.stopMeasuring(
`contain-${candidate.selector}`
)
@@ -199,15 +193,11 @@ test.describe('CSS Containment Audit', { tag: ['@audit'] }, () => {
// Remove containment
await comfyPage.page.evaluate((sel: string) => {
document.querySelectorAll(sel).forEach((el) => {
if (el instanceof HTMLElement) {
el.style.contain = ''
}
if (el instanceof HTMLElement) el.style.contain = ''
})
}, candidate.selector)
for (let i = 0; i < SETTLE_FRAMES; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < SETTLE_FRAMES; i++) await comfyPage.nextFrame()
results.push({
candidate,

View File

@@ -52,9 +52,8 @@ test.describe('Queue Clear History Dialog', { tag: '@ui' }, () => {
let clearCalled = false
await comfyPage.page.route('**/api/history', (route) => {
if (route.request().method() === 'POST') {
clearCalled = true
}
if (route.request().method() === 'POST') clearCalled = true
return route.continue()
})
@@ -75,9 +74,8 @@ test.describe('Queue Clear History Dialog', { tag: '@ui' }, () => {
let clearCalled = false
await comfyPage.page.route('**/api/history', (route) => {
if (route.request().method() === 'POST') {
clearCalled = true
}
if (route.request().method() === 'POST') clearCalled = true
return route.continue()
})

View File

@@ -22,9 +22,8 @@ test.describe('DOM Widget', { tag: '@widget' }, () => {
await expect(lastMultiline).toBeVisible()
const nodes = await comfyPage.nodeOps.getNodeRefsByType('CLIPTextEncode')
for (const node of nodes) {
await node.click('collapse')
}
for (const node of nodes) await node.click('collapse')
await expect(firstMultiline).toBeHidden()
await expect(lastMultiline).toBeHidden()
})

View File

@@ -42,9 +42,8 @@ function buildPreviewAnyValidationError(): NodeError {
function expectPartialExecutionRootNodes(requestBody: unknown): void {
const prompt = (requestBody as PromptRequestBody).prompt ?? {}
for (const nodeId of PARTIAL_EXECUTION_ROOT_NODE_IDS) {
for (const nodeId of PARTIAL_EXECUTION_ROOT_NODE_IDS)
expect(prompt[nodeId]).toMatchObject({ class_type: 'PreviewAny' })
}
}
async function getValidationErrorMessage(comfyPage: ComfyPage) {

View File

@@ -26,9 +26,8 @@ test.describe('Feature Flags', { tag: ['@slow', '@settings'] }, () => {
WebSocket.prototype.send = function (data) {
try {
const parsed = JSON.parse(data as string)
if (parsed.type === 'feature_flags') {
if (parsed.type === 'feature_flags')
window.__capturedMessages!.clientFeatureFlags = parsed
}
} catch (e) {
// Not JSON, ignore
}

View File

@@ -40,8 +40,7 @@ test.describe('i18n locale fallback', () => {
.allTextContents()
expect(labelTexts.length).toBeGreaterThan(0)
for (const text of labelTexts) {
for (const text of labelTexts)
expect(text).not.toContain('sideToolbar.labels')
}
})
})

View File

@@ -54,9 +54,9 @@ test.describe('Image Compare', { tag: ['@widget', '@vue-nodes'] }, () => {
})
.toBe(true)
const box = await containerLocator.boundingBox()
if (!box || box.width <= 0 || box.height <= 0) {
if (!box || box.width <= 0 || box.height <= 0)
throw new Error('Slider move target has no layout box')
}
await page.mouse.move(
box.x + box.width * (percentage / 100),
box.y + box.height / 2
@@ -261,9 +261,8 @@ test.describe('Image Compare', { tag: ['@widget', '@vue-nodes'] }, () => {
.toBe(true)
const box = await compareArea.boundingBox()
if (!box || box.width <= 0 || box.height <= 0) {
if (!box || box.width <= 0 || box.height <= 0)
throw new Error('Compare viewport layout not ready')
}
await comfyPage.page.mouse.move(box.x, box.y + box.height / 2)
await expect

View File

@@ -57,9 +57,9 @@ test.describe('Node Interaction', () => {
}) => {
const clipNodes =
await comfyPage.nodeOps.getNodeRefsByType('CLIPTextEncode')
for (const node of clipNodes) {
for (const node of clipNodes)
await node.click('title', { modifiers: [modifier] })
}
await expect
.poll(() => comfyPage.nodeOps.getSelectedGraphNodesCount())
.toBe(clipNodes.length)
@@ -854,9 +854,8 @@ test.describe('Load workflow', { tag: '@screenshot' }, () => {
if (!json) continue
try {
const index = JSON.parse(json)
if (typeof index.updatedAt === 'number' && index.updatedAt >= since) {
if (typeof index.updatedAt === 'number' && index.updatedAt >= since)
return true
}
} catch {
// ignore
}
@@ -889,9 +888,7 @@ test.describe('Load workflow', { tag: '@screenshot' }, () => {
await comfyPage.page.waitForFunction(() => {
for (let i = 0; i < window.sessionStorage.length; i++) {
const key = window.sessionStorage.key(i)
if (key?.startsWith('Comfy.Workflow.OpenPaths:')) {
return true
}
if (key?.startsWith('Comfy.Workflow.OpenPaths:')) return true
}
return false
})
@@ -962,9 +959,7 @@ test.describe('Load workflow', { tag: '@screenshot' }, () => {
await comfyPage.page.waitForFunction(() => {
for (let i = 0; i < window.localStorage.length; i++) {
const key = window.localStorage.key(i)
if (key?.startsWith('Comfy.Workflow.LastOpenPaths:')) {
return true
}
if (key?.startsWith('Comfy.Workflow.LastOpenPaths:')) return true
}
return false
})
@@ -1104,9 +1099,7 @@ test.describe('Viewport settings', () => {
await changeTab(tabB)
await comfyMouse.move(DefaultGraphPositions.emptySpace)
for (let i = 0; i < 4; i++) {
await comfyMouse.wheel(0, 60)
}
for (let i = 0; i < 4; i++) await comfyMouse.wheel(0, 60)
await comfyPage.nextFrame()
const screenshotB = (await comfyPage.canvas.screenshot()).toString('base64')

View File

@@ -103,9 +103,8 @@ test.describe('Keybinding Presets', { tag: '@keyboard' }, () => {
const discardButton = page.getByRole('button', {
name: /Discard and Switch/i
})
if (await discardButton.isVisible({ timeout: 2000 }).catch(() => false)) {
if (await discardButton.isVisible({ timeout: 2000 }).catch(() => false))
await discardButton.click()
}
await expect(presetTrigger).toContainText('Default Preset')

View File

@@ -15,9 +15,8 @@ function hasCanvasContent(canvas: Locator): Promise<boolean> {
const ctx = el.getContext('2d')
if (!ctx) return false
const { data } = ctx.getImageData(0, 0, el.width, el.height)
for (let i = 3; i < data.length; i += 4) {
if (data[i] > 0) return true
}
for (let i = 3; i < data.length; i += 4) if (data[i] > 0) return true
return false
})
}

View File

@@ -19,9 +19,8 @@ test.describe('Node Badge', { tag: ['@screenshot', '@smoke', '@node'] }, () => {
const graph = app.graph
const nodes = graph.nodes
for (const node of nodes) {
for (const node of nodes)
node.badges = [new LGraphBadge({ text: 'Test Badge' })]
}
graph.setDirtyCanvas(true, true)
})

View File

@@ -18,9 +18,7 @@ test.describe(
async function openMoreOptions(comfyPage: ComfyPage) {
const ksamplerNodes =
await comfyPage.nodeOps.getNodeRefsByTitle('KSampler')
if (ksamplerNodes.length === 0) {
throw new Error('No KSampler nodes found')
}
if (ksamplerNodes.length === 0) throw new Error('No KSampler nodes found')
// Drag the KSampler toward the lower-left so the menu has limited space below it.
const nodePos = await ksamplerNodes[0].getPosition()

View File

@@ -7,9 +7,7 @@ type ComfyPage = Parameters<Parameters<typeof test>[2]>[0]['comfyPage']
async function setVueMode(comfyPage: ComfyPage, enabled: boolean) {
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', enabled)
if (enabled) {
await comfyPage.vueNodes.waitForNodes()
}
if (enabled) await comfyPage.vueNodes.waitForNodes()
}
async function addGhostAtCenter(comfyPage: ComfyPage) {

View File

@@ -43,9 +43,8 @@ async function setLocaleAndWaitForWorkflowReload(
const workflow = (window.app!.extensionManager as WorkspaceStore).workflow
.activeWorkflow
if (!workflow) {
if (!workflow)
throw new Error('No active workflow while waiting for locale reload')
}
const changeTracker = workflow.changeTracker.constructor as unknown as {
isLoadingGraph: boolean
@@ -56,9 +55,7 @@ async function setLocaleAndWaitForWorkflowReload(
const timeoutAt = performance.now() + 5000
const tick = () => {
if (changeTracker.isLoadingGraph) {
sawLoading = true
}
if (changeTracker.isLoadingGraph) sawLoading = true
if (sawLoading && !changeTracker.isLoadingGraph) {
resolve()
@@ -99,9 +96,8 @@ test.describe('Node Help', { tag: ['@slow', '@ui'] }, () => {
// Select a single node (KSampler) using node references
const ksamplerNodes =
await comfyPage.nodeOps.getNodeRefsByType('KSampler')
if (ksamplerNodes.length === 0) {
if (ksamplerNodes.length === 0)
throw new Error('No KSampler nodes found in the workflow')
}
// Select the node with panning to ensure toolbox is visible
await selectNodeWithPan(comfyPage, ksamplerNodes[0])

View File

@@ -248,9 +248,7 @@ test.describe('Painter', { tag: ['@widget', '@vue-nodes'] }, () => {
await expect(sizeDisplay).toHaveText('20')
await sizeSlider.focus()
for (let i = 0; i < 10; i++) {
await sizeSlider.press('ArrowRight')
}
for (let i = 0; i < 10; i++) await sizeSlider.press('ArrowRight')
await expect(sizeDisplay).toHaveText('30')
})
@@ -268,9 +266,7 @@ test.describe('Painter', { tag: ['@widget', '@vue-nodes'] }, () => {
await expect(hardnessDisplay).toHaveText('100%')
await hardnessSlider.focus()
for (let i = 0; i < 10; i++) {
await hardnessSlider.press('ArrowLeft')
}
for (let i = 0; i < 10; i++) await hardnessSlider.press('ArrowLeft')
await expect(hardnessDisplay).toHaveText('90%')
})
@@ -344,9 +340,9 @@ test.describe('Painter', { tag: ['@widget', '@vue-nodes'] }, () => {
const cx = Math.floor(el.width / 2)
const cy = Math.floor(el.height / 2)
const { data } = ctx.getImageData(cx - 20, cy - 20, 40, 40)
for (let i = 3; i < data.length; i += 4) {
for (let i = 3; i < data.length; i += 4)
if (data[i] > 50 && data[i] < 230) return true
}
return false
}),
{
@@ -677,9 +673,7 @@ test.describe('Painter', { tag: ['@widget', '@vue-nodes'] }, () => {
).toHaveText('20')
await sizeSlider.focus()
for (let i = 0; i < 10; i++) {
await sizeSlider.press('ArrowRight')
}
for (let i = 0; i < 10; i++) await sizeSlider.press('ArrowRight')
await expect
.poll(
@@ -764,9 +758,8 @@ test.describe('Painter', { tag: ['@widget', '@vue-nodes'] }, () => {
if (!ctx) return false
const cy = Math.floor(el.height * y)
const { data } = ctx.getImageData(0, cy - 5, el.width, 10)
for (let i = 3; i < data.length; i += 4) {
if (data[i] > 0) return true
}
for (let i = 3; i < data.length; i += 4) if (data[i] > 0) return true
return false
}, yFraction)

View File

@@ -13,9 +13,7 @@ test.describe('Performance', { tag: ['@perf'] }, () => {
// Let the canvas idle for 2 seconds — no user interaction.
// Measures baseline style recalcs from reactive state + render loop.
for (let i = 0; i < 120; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < 120; i++) await comfyPage.nextFrame()
const m = await comfyPage.perf.stopMeasuring('canvas-idle')
recordMeasurement(m)
@@ -77,9 +75,7 @@ test.describe('Performance', { tag: ['@perf'] }, () => {
await comfyPage.workflow.loadWorkflow('subgraphs/nested-subgraph')
await comfyPage.perf.startMeasuring()
for (let i = 0; i < 120; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < 120; i++) await comfyPage.nextFrame()
const m = await comfyPage.perf.stopMeasuring('subgraph-idle')
recordMeasurement(m)
@@ -118,9 +114,7 @@ test.describe('Performance', { tag: ['@perf'] }, () => {
// Let the large graph idle for 2 seconds — measures compositor and
// style recalculation cost at scale (245 nodes).
for (let i = 0; i < 120; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < 120; i++) await comfyPage.nextFrame()
const m = await comfyPage.perf.stopMeasuring('large-graph-idle')
recordMeasurement(m)
@@ -263,9 +257,7 @@ test.describe('Performance', { tag: ['@perf'] }, () => {
await comfyPage.perf.startMeasuring()
// Idle for 2 seconds with minimap open and 245 nodes
for (let i = 0; i < 120; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < 120; i++) await comfyPage.nextFrame()
const m = await comfyPage.perf.stopMeasuring('minimap-idle')
recordMeasurement(m)
@@ -284,9 +276,7 @@ test.describe('Performance', { tag: ['@perf'] }, () => {
test('idle', async ({ comfyPage }) => {
await comfyPage.perf.startMeasuring()
for (let i = 0; i < 120; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < 120; i++) await comfyPage.nextFrame()
const m = await comfyPage.perf.stopMeasuring('vue-large-graph-idle')
recordMeasurement(m)
@@ -324,9 +314,7 @@ test.describe('Performance', { tag: ['@perf'] }, () => {
// Zoom out far enough that nodes become < 4px screen size
// (triggers size-based culling in isNodeInViewport)
for (let i = 0; i < 20; i++) {
await comfyPage.canvasOps.zoom(100)
}
for (let i = 0; i < 20; i++) await comfyPage.canvasOps.zoom(100)
// Verify we actually entered the culling regime.
// isNodeTooSmall triggers when max(width, height) * scale < 4px.
@@ -334,14 +322,10 @@ test.describe('Performance', { tag: ['@perf'] }, () => {
await expect.poll(() => comfyPage.canvasOps.getScale()).toBeLessThan(0.02)
// Idle at extreme zoom-out — most nodes should be culled
for (let i = 0; i < 60; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < 60; i++) await comfyPage.nextFrame()
// Zoom back in
for (let i = 0; i < 20; i++) {
await comfyPage.canvasOps.zoom(-100)
}
for (let i = 0; i < 20; i++) await comfyPage.canvasOps.zoom(-100)
const m = await comfyPage.perf.stopMeasuring('vue-zoom-culling')
recordMeasurement(m)

View File

@@ -20,9 +20,8 @@ test.describe('Preview as Text node', () => {
await comfyPage.page.evaluate(() => {
const node = window.app!.graph.nodes.find((n) => n.type === 'PreviewAny')!
for (const widget of node.widgets ?? []) {
if (widget.name?.startsWith('preview_')) {
if (widget.name?.startsWith('preview_'))
widget.value = 'rendered preview content from previous execution'
}
}
})

View File

@@ -98,28 +98,22 @@ function setComboInputOptions(
values: string[]
) {
const nodeInfo = objectInfo[nodeType]
if (!nodeInfo) {
throw new Error(`Missing object_info entry for ${nodeType}`)
}
if (!nodeInfo) throw new Error(`Missing object_info entry for ${nodeType}`)
const requiredInputs = nodeInfo.input?.required
if (!requiredInputs) {
if (!requiredInputs)
throw new Error(`Missing required inputs for ${nodeType}`)
}
const input = requiredInputs[inputName]
if (!Array.isArray(input)) {
if (!Array.isArray(input))
throw new Error(`Expected ${nodeType}.${inputName} to be a combo input`)
}
const [valuesOrType, options] = input
const optionsObject =
options && typeof options === 'object' && !Array.isArray(options)
if (Array.isArray(valuesOrType)) {
input[0] = values
} else if (valuesOrType !== 'COMBO') {
if (Array.isArray(valuesOrType)) input[0] = values
else if (valuesOrType !== 'COMBO')
throw new Error(`Expected ${nodeType}.${inputName} to have combo options`)
}
if (optionsObject) {
Object.assign(options, { options: values })

View File

@@ -390,9 +390,8 @@ test.describe('Errors tab - Mode-aware errors', { tag: '@ui' }, () => {
await comfyPage.page.evaluate((value) => {
const hostNode = window.app!.graph!.getNodeById(2)
if (!hostNode?.isSubgraphNode()) {
if (!hostNode?.isSubgraphNode())
throw new Error('Expected subgraph host node')
}
const interiorNode = hostNode.subgraph.getNodeById(1)
const widget = interiorNode?.widgets?.find(
@@ -410,9 +409,8 @@ test.describe('Errors tab - Mode-aware errors', { tag: '@ui' }, () => {
}
const settableWidget = widget as SettableWidget | undefined
if (!settableWidget?.setValue) {
if (!settableWidget?.setValue)
throw new Error('Expected concrete ckpt_name widget')
}
settableWidget.setValue(value, {
e: new PointerEvent('pointerup'),

View File

@@ -151,9 +151,8 @@ test.describe('Remote COMBO Widget', { tag: '@widget' }, () => {
let requestWasMade = false
comfyPage.page.on('request', (request) => {
if (request.url().includes('/api/models/checkpoints')) {
if (request.url().includes('/api/models/checkpoints'))
requestWasMade = true
}
})
expect(requestWasMade).toBe(false)

View File

@@ -62,9 +62,8 @@ async function expectNodePositionStable(
async function setVueMode(comfyPage: ComfyPage, enabled: boolean) {
await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', enabled)
if (enabled) {
await comfyPage.vueNodes.waitForNodes()
}
if (enabled) await comfyPage.vueNodes.waitForNodes()
await comfyPage.nextFrame()
}

View File

@@ -123,9 +123,8 @@ test.describe(
const canvas = window['app']?.canvas
if (!canvas?.renderedPaths) return null
for (const segment of canvas.renderedPaths) {
if (segment.id === 5 && segment._pos) {
if (segment.id === 5 && segment._pos)
return { x: segment._pos[0], y: segment._pos[1] }
}
}
return null
})

View File

@@ -98,9 +98,7 @@ test.describe(
await expect(renameItem).toBeVisible()
// Wait for multiple frames to allow PrimeVue's outside click handler to initialize
for (let i = 0; i < 30; i++) {
await comfyPage.nextFrame()
}
for (let i = 0; i < 30; i++) await comfyPage.nextFrame()
await comfyPage.canvasOps.mouseClickAt({ x: 0, y: 50 })
await expect(

View File

@@ -126,9 +126,8 @@ test.describe('Assets sidebar - media type filter', { tag: '@cloud' }, () => {
tab.filterVideoCheckbox,
tab.filterAudioCheckbox,
tab.filter3DCheckbox
]) {
])
await expect(cb).toHaveAttribute('aria-checked', 'false')
}
})
test('Selecting only "Image" hides non-image assets', async ({

View File

@@ -11,9 +11,9 @@ test.describe('Sidebar splitter width independence', () => {
async function dismissToasts(comfyPage: ComfyPage) {
const buttons = await comfyPage.page.locator('.p-toast-close-button').all()
for (const btn of buttons) {
for (const btn of buttons)
await btn.click({ timeout: 2000 }).catch(() => {})
}
// Brief wait for animations
await comfyPage.nextFrame()
}

View File

@@ -40,9 +40,8 @@ test.describe('Subgraph CRUD', { tag: ['@slow', '@subgraph'] }, () => {
const result = await comfyPage.page.evaluate(() => {
const graph = window.app!.graph!
const subgraphNode = graph.nodes.find((n) => n.isSubgraphNode())
if (!subgraphNode || !subgraphNode.isSubgraphNode()) {
if (!subgraphNode || !subgraphNode.isSubgraphNode())
return { error: 'No subgraph node found' }
}
graph.unpackSubgraph(subgraphNode)

View File

@@ -12,9 +12,8 @@ async function openSubgraphById(comfyPage: ComfyPage, nodeId: string) {
const node = window.app!.rootGraph.nodes.find(
(candidate) => String(candidate.id) === targetNodeId
)
if (!node || !('subgraph' in node) || !node.subgraph) {
if (!node || !('subgraph' in node) || !node.subgraph)
throw new Error(`Subgraph node ${targetNodeId} not found`)
}
window.app!.canvas.openSubgraph(node.subgraph, node)
}, nodeId)

View File

@@ -42,9 +42,8 @@ async function getPrimitiveFanoutSnapshot(
return comfyPage.page.evaluate((id) => {
const graph = window.app!.canvas.graph!
const hostNode = graph.getNodeById(Number(id))
if (!hostNode?.isSubgraphNode?.()) {
if (!hostNode?.isSubgraphNode?.())
throw new Error(`Host node ${id} is not a SubgraphNode`)
}
const [primitiveNode] = hostNode.subgraph.findNodesByType(
'PrimitiveNode',
@@ -335,9 +334,8 @@ test.describe('Subgraph Serialization', { tag: ['@subgraph'] }, () => {
const widgets = await getPromotedWidgets(comfyPage, '11')
expect(widgets.length).toBeGreaterThan(0)
for (const [interiorNodeId] of widgets) {
for (const [interiorNodeId] of widgets)
expect(Number(interiorNodeId)).toBeGreaterThan(0)
}
await expectPromotedWidgetsToResolveToInteriorNodes(
comfyPage,
@@ -609,9 +607,9 @@ test.describe('Subgraph Serialization', { tag: ['@subgraph'] }, () => {
g: typeof graph
): string | null => {
if (isSentinelNodeId(id)) return null
if (typeof id !== 'number' || !g._nodes_by_id[id]) {
if (typeof id !== 'number' || !g._nodes_by_id[id])
return `${label}: ${kind} ${id} invalid or not found`
}
return null
}

View File

@@ -267,9 +267,8 @@ test.describe('Subgraph Slots', { tag: ['@slow', '@subgraph'] }, () => {
app.canvas.linkConnector
)
if (app.canvas.pointer.onDoubleClick) {
if (app.canvas.pointer.onDoubleClick)
app.canvas.pointer.onDoubleClick(leftClickEvent)
}
}
})

View File

@@ -8,9 +8,9 @@ import { TestIds } from '@e2e/fixtures/selectors'
async function ensurePropertiesPanel(comfyPage: ComfyPage) {
const panel = comfyPage.menu.propertiesPanel.root
if (!(await panel.isVisible())) {
if (!(await panel.isVisible()))
await comfyPage.actionbar.propertiesButton.click()
}
await expect(panel).toBeVisible()
return panel
}
@@ -64,9 +64,8 @@ test.describe(
await expect(toggleButtons.first()).toBeVisible()
const count = await toggleButtons.count()
for (let i = 0; i < count; i++) {
for (let i = 0; i < count; i++)
await expect(toggleButtons.nth(i)).toBeDisabled()
}
})
test('linked promoted widgets show link icon instead of eye icon', async ({

View File

@@ -59,25 +59,21 @@ async function getNodeGroupCenteringErrors(
const app = window.app!
const node = app.graph.nodes[0] as GraphNode | undefined
if (!node) {
throw new Error('Expected a node in the loaded workflow')
}
if (!node) throw new Error('Expected a node in the loaded workflow')
const nodeElement = document.querySelector<HTMLElement>(
`[data-node-id="${node.id}"]`
)
if (!nodeElement) {
if (!nodeElement)
throw new Error(`Vue node element not found for node ${node.id}`)
}
const groups = app.graph.groups as GraphGroup[]
const innerGroup = groups.find((group) => group.title === 'Inner Group')
const outerGroup = groups.find((group) => group.title === 'Outer Group')
if (!innerGroup || !outerGroup) {
if (!innerGroup || !outerGroup)
throw new Error('Expected both Inner Group and Outer Group in graph')
}
const nodeRect = nodeElement.getBoundingClientRect()

View File

@@ -492,9 +492,7 @@ test.describe(
await comfyMouse.drop()
dropped = true
} finally {
if (!dropped) {
await comfyMouse.drop().catch(() => {})
}
if (!dropped) await comfyMouse.drop().catch(() => {})
}
await expect
@@ -1155,9 +1153,8 @@ test.describe('Vue Node Widget Link Position', { tag: '@vue-nodes' }, () => {
schedulerIndex: findIndex('scheduler')
}
})
if (!ksampler) {
throw new Error('KSampler should be present in fixture')
}
if (!ksampler) throw new Error('KSampler should be present in fixture')
expect(
ksampler.denoiseIndex,
'denoise input slot not found'

View File

@@ -80,17 +80,13 @@ test.describe(
await expect.poll(node.pollWidth).toBeGreaterThan(box.width)
await expect.poll(node.pollHeight).toBeGreaterThan(box.height)
if (hasWestEdge(corner)) {
if (hasWestEdge(corner))
await expect.poll(node.pollLeftEdge).toBeLessThan(box.x)
} else {
await expect.poll(node.pollLeftEdge).toBeCloseTo(box.x, 0)
}
else await expect.poll(node.pollLeftEdge).toBeCloseTo(box.x, 0)
if (hasNorthEdge(corner)) {
if (hasNorthEdge(corner))
await expect.poll(node.pollTopEdge).toBeLessThan(box.y)
} else {
await expect.poll(node.pollTopEdge).toBeCloseTo(box.y, 0)
}
else await expect.poll(node.pollTopEdge).toBeCloseTo(box.y, 0)
})
})
})

View File

@@ -142,9 +142,9 @@ test.describe('Vue Node Error', { tag: '@vue-nodes' }, () => {
const node = window.app!.graph.getNodeById(nodeId)
const index =
node?.inputs?.findIndex((input) => input.name === inputName) ?? -1
if (index < 0) {
if (index < 0)
throw new Error(`Input slot "${inputName}" not found`)
}
return index
},
{ nodeId: ksamplerId, inputName: KSAMPLER_MODEL_INPUT_NAME }
@@ -264,9 +264,8 @@ test.describe('Vue Node Error', { tag: '@vue-nodes' }, () => {
await test.step('drop an image onto the Load Image node', async () => {
const dropPosition =
await comfyPage.canvasOps.getNodeCenterByTitle('Load Image')
if (!dropPosition) {
if (!dropPosition)
throw new Error('Load Image node center must be available for drop')
}
await comfyPage.dragDrop.dragAndDropFile(LOAD_IMAGE_UPLOAD_FILE, {
dropPosition,

View File

@@ -49,9 +49,7 @@ test.describe('Vue Combo Widget', { tag: ['@vue-nodes', '@widget'] }, () => {
await expect.poll(() => viewport.boundingBox()).not.toBeNull()
const box = await viewport.boundingBox()
if (!box) {
throw new Error('Widget select viewport is not visible')
}
if (!box) throw new Error('Widget select viewport is not visible')
return box
}
@@ -268,9 +266,7 @@ test.describe('Vue Combo Widget', { tag: ['@vue-nodes', '@widget'] }, () => {
await comfyPage.vueNodes.waitForNodes()
const [ksamplerNode] = await comfyPage.nodeOps.getNodeRefsByType('KSampler')
if (!ksamplerNode) {
throw new Error('KSampler node not found after reload')
}
if (!ksamplerNode) throw new Error('KSampler node not found after reload')
const schedulerWidget = await ksamplerNode.getWidgetByName('scheduler')
await expect.poll(() => schedulerWidget.getValue()).toBe('karras')

View File

@@ -68,9 +68,7 @@ test.describe('Vue Float Widget', { tag: '@vue-nodes' }, () => {
await comfyPage.vueNodes.waitForNodes()
const [ksamplerNode] = await comfyPage.nodeOps.getNodeRefsByType('KSampler')
if (!ksamplerNode) {
throw new Error('KSampler node not found after reload')
}
if (!ksamplerNode) throw new Error('KSampler node not found after reload')
const cfgWidgetAfterReload = await ksamplerNode.getWidgetByName('cfg')
await expect.poll(() => cfgWidgetAfterReload.getValue()).toBe(7.5)

View File

@@ -340,9 +340,9 @@ test.describe('Image Crop', { tag: ['@widget', '@vue-nodes'] }, () => {
v.y <= valueBefore.y ||
v.width !== valueBefore.width ||
v.height !== valueBefore.height
) {
)
return null
}
return v
},
{
@@ -811,9 +811,9 @@ test.describe('Image Crop', { tag: ['@widget', '@vue-nodes'] }, () => {
.poll(
async () => {
const v = await getCropValue(comfyPage, 2)
if (!v || v.width <= before.width || v.height <= before.height) {
if (!v || v.width <= before.width || v.height <= before.height)
return null
}
const r = v.width / v.height
if (Math.abs(r - ratio) > 0.06) return null
return v

View File

@@ -16,12 +16,9 @@ const waitForWorkflowTabState = async (comfyPage: ComfyPage, minPaths = 2) => {
for (let i = 0; i < window.sessionStorage.length; i++) {
const key = window.sessionStorage.key(i)
if (key?.startsWith('Comfy.Workflow.ActivePath:')) {
hasActivePath = true
}
if (!key?.startsWith('Comfy.Workflow.OpenPaths:')) {
continue
}
if (key?.startsWith('Comfy.Workflow.ActivePath:')) hasActivePath = true
if (!key?.startsWith('Comfy.Workflow.OpenPaths:')) continue
const raw = window.sessionStorage.getItem(key)
if (!raw) continue

View File

@@ -30,9 +30,8 @@ test.describe('Workflow Tab Thumbnails', { tag: '@workflow' }, () => {
const popover = comfyPage.page.locator('.workflow-popover-fade')
await expect(popover).toHaveCount(1)
await expect(popover).toBeVisible()
if (name) {
await expect(popover).toContainText(name)
}
if (name) await expect(popover).toContainText(name)
return popover
}

View File

@@ -121,11 +121,10 @@ test.describe('WebSocket reconnect with stale job', { tag: '@ui' }, () => {
await triggerReconnect(comfyPage, ws, scenario, jobId)
if (scenario.expectsActiveAfter) {
if (scenario.expectsActiveAfter)
await expect(firstSkeleton).toBeVisible()
} else {
else
await expect(comfyPage.appMode.outputHistory.skeletons).toHaveCount(0)
}
})
}
@@ -200,11 +199,9 @@ test.describe('WebSocket reconnect with stale job', { tag: '@ui' }, () => {
await triggerReconnect(comfyPage, ws, scenario, jobId)
if (scenario.expectsActiveAfter) {
if (scenario.expectsActiveAfter)
await expect(ksamplerNode).toHaveClass(EXECUTING_CLASS)
} else {
await expect(ksamplerNode).not.toHaveClass(EXECUTING_CLASS)
}
else await expect(ksamplerNode).not.toHaveClass(EXECUTING_CLASS)
})
}
})

View File

@@ -76,9 +76,8 @@ test.describe('Zoom Controls', { tag: '@canvas' }, () => {
await comfyPage.nextFrame()
const zoomOut = comfyPage.page.getByTestId(TestIds.canvas.zoomOutAction)
for (let i = 0; i < 30; i++) {
await zoomOut.click()
}
for (let i = 0; i < 30; i++) await zoomOut.click()
await comfyPage.nextFrame()
await expect.poll(() => comfyPage.canvasOps.getScale()).toBeCloseTo(0.1, 1)

View File

@@ -27,9 +27,8 @@ const isLoading = computed<boolean>(() => workspaceStore.spinner)
watch(
isLoading,
(loading, prevLoading) => {
if (prevLoading && !loading) {
if (prevLoading && !loading)
document.getElementById('splash-loader')?.remove()
}
},
{ flush: 'post' }
)
@@ -61,9 +60,7 @@ function handleResourceError(url: string, tagName: string) {
onMounted(() => {
window['__COMFYUI_FRONTEND_VERSION__'] = config.app_version
if (isDesktop) {
document.addEventListener('contextmenu', showContextMenu)
}
if (isDesktop) document.addEventListener('contextmenu', showContextMenu)
// Handle preload errors that occur during dynamic imports (e.g., stale chunks after deployment)
// See: https://vite.dev/guide/build#load-error-handling
@@ -111,14 +108,13 @@ onMounted(() => {
'error',
(event) => {
const target = event.target
if (target instanceof HTMLScriptElement) {
if (target instanceof HTMLScriptElement)
handleResourceError(target.src, 'script')
} else if (
else if (
target instanceof HTMLLinkElement &&
target.rel === 'stylesheet'
) {
)
handleResourceError(target.href, 'link')
}
},
true
)

View File

@@ -24,9 +24,7 @@ export function assert(condition: unknown, message: string): asserts condition {
const formatted = `[Assertion failed]: ${message}`
console.error(formatted)
if (import.meta.env.DEV) {
throw new Error(formatted)
}
if (import.meta.env.DEV) throw new Error(formatted)
try {
reporter?.(formatted)

View File

@@ -47,9 +47,7 @@ export let runWhenGlobalIdle: (
// Fallback implementation for browsers without native support (e.g., Safari)
_runWhenIdle = (_targetWindow, runner, _timeout?) => {
setTimeout(() => {
if (disposed) {
return
}
if (disposed) return
// Simulate IdleDeadline - give 15ms window (one frame at ~64fps)
const end = Date.now() + 15
@@ -66,9 +64,8 @@ export let runWhenGlobalIdle: (
let disposed = false
return {
dispose() {
if (disposed) {
return
}
if (disposed) return
disposed = true
}
}
@@ -84,9 +81,8 @@ export let runWhenGlobalIdle: (
let disposed = false
return {
dispose() {
if (disposed) {
return
}
if (disposed) return
disposed = true
targetWindow.cancelIdleCallback(handle)
}

View File

@@ -33,9 +33,8 @@ function triggerLinkDownload(href: string, filename: string): void {
* @throws {Error} If the URL is invalid or empty
*/
export function downloadFile(url: string, filename?: string): void {
if (!url || typeof url !== 'string' || url.trim().length === 0) {
if (!url || typeof url !== 'string' || url.trim().length === 0)
throw new Error('Invalid URL provided for download')
}
const inferredFilename =
filename || extractFilenameFromUrl(url) || DEFAULT_DOWNLOAD_FILENAME
@@ -103,15 +102,11 @@ export function extractFilenameFromContentDisposition(
// Try simple quoted format: filename="..."
const quotedMatch = header.match(/filename="([^"]+)"/i)
if (quotedMatch?.[1]) {
return quotedMatch[1]
}
if (quotedMatch?.[1]) return quotedMatch[1]
// Try unquoted format: filename=...
const unquotedMatch = header.match(/filename=([^;\s]+)/i)
if (unquotedMatch?.[1]) {
return unquotedMatch[1]
}
if (unquotedMatch?.[1]) return unquotedMatch[1]
return null
}
@@ -122,9 +117,9 @@ export function extractFilenameFromContentDisposition(
*/
async function fetchAsBlob(url: string): Promise<Response> {
const response = await fetch(url)
if (!response.ok) {
if (!response.ok)
throw new Error(`Failed to fetch ${url}: ${response.status}`)
}
return response
}

View File

@@ -21,9 +21,8 @@ const formatNumber = ({
typeof merged.maximumFractionDigits === 'number' &&
typeof merged.minimumFractionDigits === 'number' &&
merged.maximumFractionDigits < merged.minimumFractionDigits
) {
)
merged.minimumFractionDigits = merged.maximumFractionDigits
}
return new Intl.NumberFormat(locale, merged).format(value)
}

View File

@@ -38,9 +38,9 @@ function hasWebViewBridge(): boolean {
win.webkit !== null &&
typeof (win.webkit as Record<string, unknown>).messageHandlers ===
'object'
) {
)
return true
}
if (win.ReactNativeWebView != null) return true
} catch {
// Access to bridge objects may throw in sandboxed contexts

View File

@@ -205,9 +205,9 @@ const sidebarTabKey = computed(() => {
const sidebarStateKey = computed(() => {
const base = sidebarTabKey.value
if (sidebarLocation.value === 'left' && !showOffsideSplitter.value) {
if (sidebarLocation.value === 'left' && !showOffsideSplitter.value)
return base
}
const suffix = showOffsideSplitter.value ? '-with-offside' : ''
return `${base}-${sidebarLocation.value}${suffix}`
})
@@ -241,9 +241,9 @@ function normalizeSavedSizes() {
!Array.isArray(parsed) ||
parsed.length === 0 ||
parsed.some((s) => typeof s !== 'number' || !Number.isFinite(s))
) {
)
return
}
const sum = parsed.reduce((a, b) => a + b, 0)
if (sum <= 0 || Math.abs(sum - 100) <= 0.5) return
localStorage.setItem(
@@ -265,17 +265,17 @@ const splitterRefreshKey = computed(() => {
const firstPanelStyle = computed(() => {
if (focusMode.value) return { display: 'none' }
if (sidebarLocation.value === 'left') {
if (sidebarLocation.value === 'left')
return { display: sidebarPanelVisible.value ? 'flex' : 'none' }
}
return undefined
})
const lastPanelStyle = computed(() => {
if (focusMode.value) return { display: 'none' }
if (sidebarLocation.value === 'right') {
if (sidebarLocation.value === 'right')
return { display: sidebarPanelVisible.value ? 'flex' : 'none' }
}
return undefined
})
</script>

View File

@@ -34,13 +34,9 @@ const exitFocusMode = () => {
}
watchEffect(() => {
if (settingStore.get('Comfy.UseNewMenu') !== 'Disabled') {
return
}
if (workspaceState.focusMode) {
app.ui.menuContainer.style.display = 'none'
} else {
app.ui.menuContainer.style.display = 'block'
}
if (settingStore.get('Comfy.UseNewMenu') !== 'Disabled') return
if (workspaceState.focusMode) app.ui.menuContainer.style.display = 'none'
else app.ui.menuContainer.style.display = 'block'
})
</script>

View File

@@ -167,9 +167,9 @@ function getLegacyCommandsContainer(container: Element): HTMLElement {
const legacyContainer = container.querySelector(
'[data-testid="legacy-topbar-container"]'
)
if (!(legacyContainer instanceof HTMLElement)) {
if (!(legacyContainer instanceof HTMLElement))
throw new Error('Expected legacy commands container to be present')
}
return legacyContainer
}

View File

@@ -244,9 +244,9 @@ function updateProgressTarget(target: HTMLElement | null) {
progressTarget.value = target
}
const inlineProgressSummaryTarget = computed(() => {
if (!shouldShowInlineProgressSummary.value || !isActionbarFloating.value) {
if (!shouldShowInlineProgressSummary.value || !isActionbarFloating.value)
return null
}
return progressTarget.value
})
const shouldHideInlineProgressSummary = computed(

View File

@@ -87,9 +87,7 @@ const incrementButtonClass = 'rounded-tr-none border-b border-border-subtle'
const decrementButtonClass = 'rounded-br-none'
watch(batchCount, (nextBatchCount) => {
if (!isEditing.value) {
batchCountInput.value = String(nextBatchCount)
}
if (!isEditing.value) batchCountInput.value = String(nextBatchCount)
})
const clampBatchCount = (nextBatchCount: number): number =>

View File

@@ -178,9 +178,7 @@ const setInitialPosition = () => {
const menuWidth = panel.offsetWidth
const menuHeight = panel.offsetHeight
if (menuWidth === 0 || menuHeight === 0) {
return
}
if (menuWidth === 0 || menuHeight === 0) return
// Check if stored position exists and is within bounds
if (storedPosition.value.x !== 0 || storedPosition.value.y !== 0) {
@@ -212,9 +210,7 @@ async function comfyRunButtonResolved() {
}
watch(visible, async (newVisible) => {
if (newVisible) {
await nextTick(setInitialPosition)
}
if (newVisible) await nextTick(setInitialPosition)
})
/**
@@ -314,15 +310,11 @@ const isMouseOverDropZone = ref(false)
// Mouse event handlers for self-contained drop zone
const onMouseEnterDropZone = () => {
if (isDragging.value) {
isMouseOverDropZone.value = true
}
if (isDragging.value) isMouseOverDropZone.value = true
}
const onMouseLeaveDropZone = () => {
if (isDragging.value) {
isMouseOverDropZone.value = false
}
if (isDragging.value) isMouseOverDropZone.value = false
}
const inlineProgressTarget = computed(() => {
@@ -330,9 +322,9 @@ const inlineProgressTarget = computed(() => {
!visible.value ||
!isQueuePanelV2Enabled.value ||
!isRunProgressBarEnabled.value
) {
)
return null
}
if (isDocked.value) return topMenuContainer ?? null
return panelElement.value
})
@@ -351,14 +343,11 @@ watch(
watch(isDragging, (dragging) => {
if (dragging) {
// Starting to drag - undock if docked
if (isDocked.value) {
isDocked.value = false
}
if (isDocked.value) isDocked.value = false
} else {
// Stopped dragging - dock if mouse is over drop zone
if (isMouseOverDropZone.value) {
isDocked.value = true
}
if (isMouseOverDropZone.value) isDocked.value = true
// Reset drop zone state
isMouseOverDropZone.value = false
}

View File

@@ -187,37 +187,28 @@ const queueMenuTriggerClass =
const queueMenuItemButtonClass = 'w-full justify-start font-normal'
const iconClass = computed(() => {
if (isStopInstantAction.value) {
return 'icon-[lucide--square]'
}
if (hasMissingNodes.value) {
return 'icon-[lucide--triangle-alert]'
}
if (workspaceStore.shiftDown) {
return 'icon-[lucide--list-start]'
}
if (queueMode.value === 'disabled') {
return 'icon-[lucide--play]'
}
if (isInstantMode(queueMode.value)) {
return 'icon-[lucide--fast-forward]'
}
if (queueMode.value === 'change') {
return 'icon-[lucide--step-forward]'
}
if (isStopInstantAction.value) return 'icon-[lucide--square]'
if (hasMissingNodes.value) return 'icon-[lucide--triangle-alert]'
if (workspaceStore.shiftDown) return 'icon-[lucide--list-start]'
if (queueMode.value === 'disabled') return 'icon-[lucide--play]'
if (isInstantMode(queueMode.value)) return 'icon-[lucide--fast-forward]'
if (queueMode.value === 'change') return 'icon-[lucide--step-forward]'
return 'icon-[lucide--play]'
})
const queueButtonTooltip = computed(() => {
if (isStopInstantAction.value) {
return t('menu.stopRunInstantTooltip')
}
if (hasMissingNodes.value) {
return t('menu.runWorkflowDisabled')
}
if (workspaceStore.shiftDown) {
return t('menu.runWorkflowFront')
}
if (isStopInstantAction.value) return t('menu.stopRunInstantTooltip')
if (hasMissingNodes.value) return t('menu.runWorkflowDisabled')
if (workspaceStore.shiftDown) return t('menu.runWorkflowFront')
return t('menu.runWorkflow')
})
@@ -233,9 +224,7 @@ const queuePrompt = async (e: Event) => {
? 'Comfy.QueuePromptFront'
: 'Comfy.QueuePrompt'
if (isInstantMode(queueMode.value)) {
queueMode.value = 'instant-running'
}
if (isInstantMode(queueMode.value)) queueMode.value = 'instant-running'
if (batchCount.value > 1) {
useTelemetry()?.trackUiButtonClicked({

View File

@@ -64,9 +64,8 @@ const { subcategories } = defineProps<{
const filteredSubcategories = computed(() => {
const result: Record<string, ComfyCommandImpl[]> = {}
for (const [subcategory, commands] of Object.entries(subcategories)) {
for (const [subcategory, commands] of Object.entries(subcategories))
result[subcategory] = commands.filter((cmd) => !!cmd.keybinding)
}
return result
})

View File

@@ -81,9 +81,7 @@ const handleCopy = async () => {
if (selectedText) {
await navigator.clipboard.writeText(selectedText)
if (shouldSelectAll) {
terminal.clearSelection()
}
if (shouldSelectAll) terminal.clearSelection()
}
}
@@ -92,9 +90,7 @@ const showContextMenu = (event: MouseEvent) => {
electronAPI()?.showContextMenu({ type: 'text' })
}
if (isDesktop) {
useEventListener(terminalEl, 'contextmenu', showContextMenu)
}
if (isDesktop) useEventListener(terminalEl, 'contextmenu', showContextMenu)
onMounted(() => {
selectionDisposable = terminal.onSelectionChange(() => {

View File

@@ -117,16 +117,14 @@ describe('WidgetBoundingBox', () => {
describe('Disabled state', () => {
it('disables all four inputs when disabled=true', () => {
renderBox({ x: 0, y: 0, width: 1, height: 1 }, true)
for (const input of screen.getAllByRole('spinbutton')) {
for (const input of screen.getAllByRole('spinbutton'))
expect(input).toBeDisabled()
}
})
it('leaves all four inputs enabled when disabled=false', () => {
renderBox({ x: 0, y: 0, width: 1, height: 1 }, false)
for (const input of screen.getAllByRole('spinbutton')) {
for (const input of screen.getAllByRole('spinbutton'))
expect(input).not.toBeDisabled()
}
})
})
})

View File

@@ -178,9 +178,7 @@ watch(breadcrumbElement, (el) => {
const totalWidth = itemsWidth + separatorsWidth + gapsWidth
const containerWidth = el.clientWidth
if (totalWidth <= containerWidth) {
collapseTabs.value = false
}
if (totalWidth <= containerWidth) collapseTabs.value = false
}
} else if (isOverflowing) {
collapseTabs.value = true
@@ -191,9 +189,7 @@ watch(breadcrumbElement, (el) => {
// If e.g. the workflow name changes, we need to check the overflow again
onUpdated(() => {
if (!overflowObserver?.disposed.value) {
overflowObserver?.checkOverflow()
}
if (!overflowObserver?.disposed.value) overflowObserver?.checkOverflow()
})
</script>

View File

@@ -138,9 +138,9 @@ const rename = async (
const isRoot = item.key === 'root'
const tooltipText = computed(() => {
if (hasMissingNodes.value && isRoot) {
if (hasMissingNodes.value && isRoot)
return t('breadcrumbsMenu.missingNodesWarning')
}
return item.label
})
@@ -158,9 +158,8 @@ const startRename = async () => {
if (itemInputRef.value?.$el) {
itemInputRef.value.$el.focus()
itemInputRef.value.$el.select()
if (wrapperRef.value) {
if (wrapperRef.value)
itemInputRef.value.$el.style.width = `${Math.max(200, wrapperRef.value.offsetWidth)}px`
}
}
})
}
@@ -168,16 +167,11 @@ const startRename = async () => {
const { menuItems } = useWorkflowActionsMenu(startRename, { isRoot })
const handleClick = (event: MouseEvent) => {
if (isEditing.value) {
return
}
if (isEditing.value) return
if (event.detail === 1) {
if (isActive) {
menu.value?.toggle(event)
} else {
item.command?.({ item: item, originalEvent: event })
}
if (isActive) menu.value?.toggle(event)
else item.command?.({ item: item, originalEvent: event })
} else if (isActive && event.detail === 2) {
menu.value?.hide()
event.stopPropagation()
@@ -187,9 +181,7 @@ const handleClick = (event: MouseEvent) => {
}
const inputBlur = async (doRename: boolean) => {
if (doRename) {
await rename(itemLabel.value, item.label as string)
}
if (doRename) await rename(itemLabel.value, item.label as string)
isEditing.value = false
}

View File

@@ -63,9 +63,9 @@ const mappedSelections = computed((): WidgetEntry[] => {
const { entityId, node, widget, config } = entry
if (node.mode !== LGraphEventMode.ALWAYS) return []
if (!nodeDataByNode.has(node)) {
if (!nodeDataByNode.has(node))
nodeDataByNode.set(node, nodeToNodeData(node))
}
const fullNodeData = nodeDataByNode.get(node)!
const matchingWidget = fullNodeData.widgets?.find((vueWidget) => {
@@ -133,9 +133,10 @@ function nodeToNodeData(node: LGraphNode) {
async function handleDragDrop() {
const onDragDrop = async (e: DragEvent) => {
for (const { nodeData } of mappedSelections.value)
for (const { nodeData } of mappedSelections.value) {
if (nodeData?.onDragOver?.(e) && (await nodeData.onDragDrop?.(e)))
return true
}
return false
}

View File

@@ -35,18 +35,20 @@ function onEditComplete(newName: string) {
const entries = computed(() => {
const items = []
if (canRename)
if (canRename) {
items.push({
label: t('g.rename'),
command: () => setTimeout(() => (isEditing.value = true)),
icon: 'icon-[lucide--pencil]'
})
if (remove)
}
if (remove) {
items.push({
label: t('g.delete'),
command: remove,
icon: 'icon-[lucide--trash-2]'
})
}
return items
})
</script>

View File

@@ -50,9 +50,9 @@ const settingStore = useSettingStore()
const dontShowAgain = ref(false)
function dismiss() {
if (dontShowAgain.value) {
if (dontShowAgain.value)
void settingStore.set('Comfy.AppBuilder.VueNodeSwitchDismissed', true)
}
appModeStore.showVueNodeSwitchPopup = false
}
</script>

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