mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-06-18 04:19:46 +00:00
Compare commits
1 Commits
main
...
coderabbit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9632e6dea0 |
55
.github/workflows/pr-cursor-review.yaml
vendored
55
.github/workflows/pr-cursor-review.yaml
vendored
@@ -1,55 +0,0 @@
|
||||
# Description: Team-gated multi-model Cursor review — a thin caller for the
|
||||
# reusable workflow in Comfy-Org/github-workflows, which is the single source of
|
||||
# truth for the panel, judge, prompts, and scripts. Triggered by the
|
||||
# 'cursor-review' label.
|
||||
#
|
||||
# Access control (team-only, two layers):
|
||||
# 1. Only users with triage permission or higher can apply a label in a public
|
||||
# repo, so the public cannot trigger this.
|
||||
# 2. The reusable workflow's secret-bearing jobs do not run on fork PRs (forks
|
||||
# get no secrets), so CURSOR_API_KEY is reachable only on internal branches.
|
||||
name: 'PR: Cursor Review'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, unlabeled]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
# Re-labeling cancels an in-flight run for the same PR + label.
|
||||
group: cursor-review-pr-${{ github.event.pull_request.number }}-${{ github.event.label.name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
cursor-review:
|
||||
if: github.event.action == 'labeled' && github.event.label.name == 'cursor-review'
|
||||
# SHA-pinned per zizmor `unpinned-uses: hash-pin`. Bump this SHA to pick up
|
||||
# upstream changes; keep `workflows_ref` matching so prompts/scripts load
|
||||
# from the same commit as the workflow definition.
|
||||
uses: Comfy-Org/github-workflows/.github/workflows/cursor-review.yml@047ca48febe3a6647608ed2e0c4331b491cb9d6a # github-workflows#9
|
||||
with:
|
||||
# Overriding diff_excludes replaces the reusable default wholesale, so
|
||||
# this restates the generated/vendored defaults and adds this repo's heavy
|
||||
# paths (Playwright snapshots, generated manager types).
|
||||
diff_excludes: >-
|
||||
:!**/package-lock.json
|
||||
:!**/yarn.lock
|
||||
:!**/pnpm-lock.yaml
|
||||
:!**/node_modules/**
|
||||
:!**/.claude/**
|
||||
:!**/dist/**
|
||||
:!**/vendor/**
|
||||
:!**/*.generated.*
|
||||
:!**/*.min.js
|
||||
:!**/*.min.css
|
||||
:!**/*-snapshots/**
|
||||
:!src/workbench/extensions/manager/types/generatedManagerTypes.ts
|
||||
# Load the prompts/scripts from the same ref as `uses:`.
|
||||
workflows_ref: 047ca48febe3a6647608ed2e0c4331b491cb9d6a
|
||||
secrets:
|
||||
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
|
||||
# Optional — enables start/complete Slack DMs to the triggerer.
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
@@ -1385,9 +1385,9 @@ const translations = {
|
||||
'zh-CN': '随时加购积分'
|
||||
},
|
||||
'pricing.included.feature5.description': {
|
||||
en: 'Purchase additional credits at any time. Top-up credits are valid for 1 year from the date of purchase and do not roll over with your monthly plan.',
|
||||
en: 'Purchase additional credits at any time. Unused top-ups roll over to the next month automatically for up to 1 year.',
|
||||
'zh-CN':
|
||||
'可随时购买额外积分。充值积分自购买之日起 1 年内有效,且不会随月度计划结转。'
|
||||
'可随时购买额外积分。未使用的充值积分自动结转至下月,最长保留 1 年。'
|
||||
},
|
||||
'pricing.included.feature6.title': {
|
||||
en: 'Pre-installed models',
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
function flagAttributeFor(testId: string) {
|
||||
const encoded = Array.from(testId, (ch) =>
|
||||
ch.charCodeAt(0).toString(16)
|
||||
).join('')
|
||||
return `data-flashed-${encoded}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags the first time an element matching `[data-testid="<testId>"]` is
|
||||
* present and rendered, sampled every frame via `requestAnimationFrame` from
|
||||
* page load. Catches a dialog that mounts and unmounts within a few frames,
|
||||
* which `toBeHidden()` (final state only) cannot.
|
||||
*
|
||||
* Must be called before navigation (e.g. before `comfyPage.setup()`).
|
||||
*/
|
||||
export async function trackElementFlash(
|
||||
page: Page,
|
||||
testId: string
|
||||
): Promise<{ hasFlashed: () => Promise<boolean> }> {
|
||||
const flagAttribute = flagAttributeFor(testId)
|
||||
|
||||
await page.addInitScript(
|
||||
({ id, attribute }: { id: string; attribute: string }) => {
|
||||
const sample = () => {
|
||||
const el = document.querySelector(`[data-testid="${CSS.escape(id)}"]`)
|
||||
if (el instanceof HTMLElement) {
|
||||
const rect = el.getBoundingClientRect()
|
||||
if (rect.width > 0 && rect.height > 0) {
|
||||
document.documentElement.setAttribute(attribute, 'true')
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(sample)
|
||||
}
|
||||
requestAnimationFrame(sample)
|
||||
},
|
||||
{ id: testId, attribute: flagAttribute }
|
||||
)
|
||||
|
||||
return {
|
||||
hasFlashed: async () =>
|
||||
(await page.locator('html').getAttribute(flagAttribute)) === 'true'
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,6 @@ import { createMixedMediaJobs } from '@e2e/fixtures/helpers/AssetsHelper'
|
||||
// fixtures — Playwright runs auto fixtures before the `comfyPage` fixture's
|
||||
// internal `setup()`, so the page first-loads with mocks already in place.
|
||||
// See cloud-asset-default.spec.ts for the same pattern.
|
||||
//
|
||||
// Use `waitForAssets()` not `waitForAssets(MIXED_JOBS.length)`: VirtualGrid can
|
||||
// virtualize the 3D card out of the initial render (#11635). Filtering reads the
|
||||
// full store, so the per-filter count assertions still cover the behavior.
|
||||
|
||||
const MIXED_JOBS = createMixedMediaJobs(['images', 'video', 'audio', '3D'])
|
||||
|
||||
@@ -117,7 +113,7 @@ test.describe('Assets sidebar - media type filter', { tag: '@cloud' }, () => {
|
||||
}) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
await tab.waitForAssets(MIXED_JOBS.length)
|
||||
|
||||
await tab.openFilterMenu()
|
||||
|
||||
@@ -140,7 +136,7 @@ test.describe('Assets sidebar - media type filter', { tag: '@cloud' }, () => {
|
||||
}) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
await tab.waitForAssets(MIXED_JOBS.length)
|
||||
|
||||
await tab.openFilterMenu()
|
||||
await tab.toggleMediaTypeFilter('image')
|
||||
@@ -157,7 +153,7 @@ test.describe('Assets sidebar - media type filter', { tag: '@cloud' }, () => {
|
||||
}) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
await tab.waitForAssets(MIXED_JOBS.length)
|
||||
|
||||
await tab.openFilterMenu()
|
||||
await tab.toggleMediaTypeFilter('video')
|
||||
@@ -171,7 +167,7 @@ test.describe('Assets sidebar - media type filter', { tag: '@cloud' }, () => {
|
||||
}) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
await tab.waitForAssets(MIXED_JOBS.length)
|
||||
|
||||
await tab.openFilterMenu()
|
||||
await tab.toggleMediaTypeFilter('audio')
|
||||
@@ -183,7 +179,7 @@ test.describe('Assets sidebar - media type filter', { tag: '@cloud' }, () => {
|
||||
test('Selecting only "3D" hides non-3D assets', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
await tab.waitForAssets(MIXED_JOBS.length)
|
||||
|
||||
await tab.openFilterMenu()
|
||||
await tab.toggleMediaTypeFilter('3d')
|
||||
@@ -197,7 +193,7 @@ test.describe('Assets sidebar - media type filter', { tag: '@cloud' }, () => {
|
||||
}) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
await tab.waitForAssets(MIXED_JOBS.length)
|
||||
|
||||
await tab.openFilterMenu()
|
||||
await tab.toggleMediaTypeFilter('image')
|
||||
@@ -215,7 +211,7 @@ test.describe('Assets sidebar - media type filter', { tag: '@cloud' }, () => {
|
||||
}) => {
|
||||
const tab = comfyPage.menu.assetsTab
|
||||
await tab.open()
|
||||
await tab.waitForAssets()
|
||||
await tab.waitForAssets(MIXED_JOBS.length)
|
||||
|
||||
await tab.openFilterMenu()
|
||||
await tab.toggleMediaTypeFilter('image')
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { WorkflowTemplates } from '@/platform/workflow/templates/types/temp
|
||||
import { getWav } from '@e2e/fixtures/components/AudioPreview'
|
||||
import { comfyPageFixture as test } from '@e2e/fixtures/ComfyPage'
|
||||
import { TestIds } from '@e2e/fixtures/selectors'
|
||||
import { trackElementFlash } from '@e2e/fixtures/utils/flashDetector'
|
||||
|
||||
async function checkTemplateFileExists(
|
||||
page: Page,
|
||||
@@ -506,32 +505,3 @@ test.describe('Templates', { tag: ['@slow', '@workflow'] }, () => {
|
||||
expect(popup.url()).toEqual(tutorialUrl)
|
||||
})
|
||||
})
|
||||
|
||||
test.describe(
|
||||
'Templates deeplink (new user)',
|
||||
{ tag: ['@slow', '@workflow'] },
|
||||
() => {
|
||||
test('templates dialog never flashes when first-time user opens a template link', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const templatesFlash = await trackElementFlash(
|
||||
comfyPage.page,
|
||||
TestIds.templates.content
|
||||
)
|
||||
|
||||
await comfyPage.settings.setSetting('Comfy.TutorialCompleted', false)
|
||||
|
||||
await comfyPage.setup({
|
||||
clearStorage: true,
|
||||
url: '/?template=default'
|
||||
})
|
||||
|
||||
await expect
|
||||
.poll(() => comfyPage.nodeOps.getGraphNodesCount())
|
||||
.toBeGreaterThan(0)
|
||||
|
||||
expect(await templatesFlash.hasFlashed()).toBe(false)
|
||||
await expect(comfyPage.templates.content).toBeHidden()
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
11
codecov.yml
11
codecov.yml
@@ -4,14 +4,3 @@ comment:
|
||||
require_changes: false
|
||||
require_base: false
|
||||
require_head: true
|
||||
|
||||
# Carry forward the last known coverage for a flag when its upload is missing or
|
||||
# late. The `e2e` flag is uploaded by a separate workflow_run job that can fail
|
||||
# or arrive after Codecov has already computed the patch status; without this,
|
||||
# E2E-only code paths show up as patch misses and the patch status fails. See
|
||||
# https://docs.codecov.com/docs/carryforward-flags
|
||||
flags:
|
||||
unit:
|
||||
carryforward: true
|
||||
e2e:
|
||||
carryforward: true
|
||||
|
||||
@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { nextTick, ref } from 'vue'
|
||||
import type { IFuseOptions } from 'fuse.js'
|
||||
|
||||
import type { Distribution } from '@/platform/distribution/types'
|
||||
import type { TemplateInfo } from '@/platform/workflow/templates/types/template'
|
||||
import { TemplateIncludeOnDistributionEnum } from '@/platform/workflow/templates/types/template'
|
||||
import { useTemplateFiltering } from '@/composables/useTemplateFiltering'
|
||||
@@ -436,7 +437,7 @@ describe('useTemplateFiltering', () => {
|
||||
})
|
||||
|
||||
describe('Distribution filtering', () => {
|
||||
const setDistribution = (distribution: 'desktop' | 'localhost' | 'cloud') =>
|
||||
const setDistribution = (distribution: Distribution) =>
|
||||
vi.stubGlobal('__DISTRIBUTION__', distribution)
|
||||
|
||||
const cloudTemplate: TemplateInfo = {
|
||||
@@ -457,6 +458,14 @@ describe('useTemplateFiltering', () => {
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Desktop]
|
||||
}
|
||||
|
||||
const localTemplate: TemplateInfo = {
|
||||
name: 'local-only',
|
||||
description: 'Local template',
|
||||
mediaType: 'image',
|
||||
mediaSubtype: 'png',
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Local]
|
||||
}
|
||||
|
||||
const universalTemplate: TemplateInfo = {
|
||||
name: 'universal',
|
||||
description: 'Universal template',
|
||||
@@ -540,14 +549,6 @@ describe('useTemplateFiltering', () => {
|
||||
|
||||
it('shows local templates on localhost distribution', () => {
|
||||
setDistribution('localhost')
|
||||
const localTemplate: TemplateInfo = {
|
||||
name: 'local-only',
|
||||
description: 'Local template',
|
||||
mediaType: 'image',
|
||||
mediaSubtype: 'png',
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Local]
|
||||
}
|
||||
|
||||
const templates = ref([localTemplate, cloudTemplate, desktopTemplate])
|
||||
|
||||
const { filteredTemplates, filteredCount, totalCount } =
|
||||
@@ -558,6 +559,219 @@ describe('useTemplateFiltering', () => {
|
||||
expect(filteredTemplates.value[0].name).toBe('local-only')
|
||||
})
|
||||
|
||||
it('shows local templates on desktop2 distribution', () => {
|
||||
setDistribution('desktop2')
|
||||
const templates = ref([localTemplate, desktopTemplate, cloudTemplate])
|
||||
|
||||
const { filteredTemplates, filteredCount, totalCount } =
|
||||
useTemplateFiltering(templates)
|
||||
|
||||
expect(filteredCount.value).toBe(1)
|
||||
expect(totalCount.value).toBe(1)
|
||||
expect(filteredTemplates.value[0].name).toBe('local-only')
|
||||
})
|
||||
|
||||
it('excludes cloud-only templates on desktop2 distribution', () => {
|
||||
setDistribution('desktop2')
|
||||
const templates = ref([cloudTemplate, universalTemplate])
|
||||
|
||||
const { filteredTemplates, filteredCount, totalCount } =
|
||||
useTemplateFiltering(templates)
|
||||
|
||||
expect(filteredCount.value).toBe(1)
|
||||
expect(totalCount.value).toBe(1)
|
||||
expect(filteredTemplates.value[0].name).toBe('universal')
|
||||
})
|
||||
|
||||
it('excludes desktop-only templates on desktop2 distribution', () => {
|
||||
setDistribution('desktop2')
|
||||
const templates = ref([desktopTemplate, universalTemplate])
|
||||
|
||||
const { filteredTemplates, filteredCount } = useTemplateFiltering(templates)
|
||||
|
||||
expect(filteredCount.value).toBe(1)
|
||||
expect(filteredTemplates.value[0].name).toBe('universal')
|
||||
})
|
||||
|
||||
it('shows universal templates (no distribution constraint) on desktop2', () => {
|
||||
setDistribution('desktop2')
|
||||
const templates = ref([universalTemplate])
|
||||
|
||||
const { filteredCount, totalCount } = useTemplateFiltering(templates)
|
||||
|
||||
expect(filteredCount.value).toBe(1)
|
||||
expect(totalCount.value).toBe(1)
|
||||
})
|
||||
|
||||
it('desktop2 and localhost produce identical template filtering results', () => {
|
||||
const templates = ref([
|
||||
localTemplate,
|
||||
cloudTemplate,
|
||||
desktopTemplate,
|
||||
universalTemplate
|
||||
])
|
||||
|
||||
setDistribution('desktop2')
|
||||
const { filteredTemplates: desktop2Filtered } =
|
||||
useTemplateFiltering(templates)
|
||||
|
||||
setDistribution('localhost')
|
||||
const { filteredTemplates: localhostFiltered } =
|
||||
useTemplateFiltering(templates)
|
||||
|
||||
expect(desktop2Filtered.value.map((t) => t.name)).toEqual(
|
||||
localhostFiltered.value.map((t) => t.name)
|
||||
)
|
||||
})
|
||||
|
||||
it('desktop2 does not include Mac-specific templates', () => {
|
||||
setDistribution('desktop2')
|
||||
const macTemplate: TemplateInfo = {
|
||||
name: 'mac-template',
|
||||
description: 'Mac only',
|
||||
mediaType: 'image',
|
||||
mediaSubtype: 'png',
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Mac]
|
||||
}
|
||||
|
||||
const templates = ref([macTemplate, localTemplate])
|
||||
|
||||
const { filteredTemplates, filteredCount } = useTemplateFiltering(templates)
|
||||
|
||||
expect(filteredCount.value).toBe(1)
|
||||
expect(filteredTemplates.value[0].name).toBe('local-only')
|
||||
})
|
||||
|
||||
it('desktop2 does not include Windows-specific templates', () => {
|
||||
setDistribution('desktop2')
|
||||
const windowsTemplate: TemplateInfo = {
|
||||
name: 'windows-template',
|
||||
description: 'Windows only',
|
||||
mediaType: 'image',
|
||||
mediaSubtype: 'png',
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Windows]
|
||||
}
|
||||
|
||||
const templates = ref([windowsTemplate, localTemplate])
|
||||
|
||||
const { filteredTemplates, filteredCount } = useTemplateFiltering(templates)
|
||||
|
||||
expect(filteredCount.value).toBe(1)
|
||||
expect(filteredTemplates.value[0].name).toBe('local-only')
|
||||
})
|
||||
|
||||
it('desktop2 distribution composes with model filter', () => {
|
||||
setDistribution('desktop2')
|
||||
const localFluxTemplate: TemplateInfo = {
|
||||
name: 'local-flux',
|
||||
description: 'Local Flux template',
|
||||
mediaType: 'image',
|
||||
mediaSubtype: 'png',
|
||||
models: ['Flux'],
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Local]
|
||||
}
|
||||
const localSDTemplate: TemplateInfo = {
|
||||
name: 'local-sd',
|
||||
description: 'Local SD template',
|
||||
mediaType: 'image',
|
||||
mediaSubtype: 'png',
|
||||
models: ['SD 1.5'],
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Local]
|
||||
}
|
||||
const cloudFluxTemplate: TemplateInfo = {
|
||||
name: 'cloud-flux',
|
||||
description: 'Cloud Flux template',
|
||||
mediaType: 'image',
|
||||
mediaSubtype: 'png',
|
||||
models: ['Flux'],
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Cloud]
|
||||
}
|
||||
|
||||
const templates = ref([localFluxTemplate, localSDTemplate, cloudFluxTemplate])
|
||||
|
||||
const { selectedModels, filteredTemplates, filteredCount, totalCount } =
|
||||
useTemplateFiltering(templates)
|
||||
|
||||
expect(totalCount.value).toBe(2)
|
||||
|
||||
selectedModels.value = ['Flux']
|
||||
|
||||
expect(filteredCount.value).toBe(1)
|
||||
expect(filteredTemplates.value[0].name).toBe('local-flux')
|
||||
})
|
||||
|
||||
it('desktop2 distribution composes with use case filter', () => {
|
||||
setDistribution('desktop2')
|
||||
const localVideoTemplate: TemplateInfo = {
|
||||
name: 'local-video',
|
||||
description: 'Local video template',
|
||||
mediaType: 'video',
|
||||
mediaSubtype: 'mp4',
|
||||
tags: ['Video'],
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Local]
|
||||
}
|
||||
const localImageTemplate: TemplateInfo = {
|
||||
name: 'local-image',
|
||||
description: 'Local image template',
|
||||
mediaType: 'image',
|
||||
mediaSubtype: 'png',
|
||||
tags: ['Image Gen'],
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Local]
|
||||
}
|
||||
const cloudVideoTemplate: TemplateInfo = {
|
||||
name: 'cloud-video',
|
||||
description: 'Cloud video template',
|
||||
mediaType: 'video',
|
||||
mediaSubtype: 'mp4',
|
||||
tags: ['Video'],
|
||||
includeOnDistributions: [TemplateIncludeOnDistributionEnum.Cloud]
|
||||
}
|
||||
|
||||
const templates = ref([
|
||||
localVideoTemplate,
|
||||
localImageTemplate,
|
||||
cloudVideoTemplate
|
||||
])
|
||||
|
||||
const { selectedUseCases, filteredTemplates, filteredCount } =
|
||||
useTemplateFiltering(templates)
|
||||
|
||||
selectedUseCases.value = ['Video']
|
||||
|
||||
expect(filteredCount.value).toBe(1)
|
||||
expect(filteredTemplates.value[0].name).toBe('local-video')
|
||||
})
|
||||
|
||||
it('desktop2 shows template with Local in multi-distribution list', () => {
|
||||
setDistribution('desktop2')
|
||||
const localAndCloudTemplate: TemplateInfo = {
|
||||
name: 'local-and-cloud',
|
||||
description: 'Available on Local and Cloud',
|
||||
mediaType: 'image',
|
||||
mediaSubtype: 'png',
|
||||
includeOnDistributions: [
|
||||
TemplateIncludeOnDistributionEnum.Local,
|
||||
TemplateIncludeOnDistributionEnum.Cloud
|
||||
]
|
||||
}
|
||||
|
||||
const templates = ref([localAndCloudTemplate, desktopTemplate])
|
||||
|
||||
const { filteredTemplates, filteredCount } = useTemplateFiltering(templates)
|
||||
|
||||
expect(filteredCount.value).toBe(1)
|
||||
expect(filteredTemplates.value[0].name).toBe('local-and-cloud')
|
||||
})
|
||||
|
||||
it('desktop2 excludes template with only Cloud+Desktop multi-distribution', () => {
|
||||
setDistribution('desktop2')
|
||||
const templates = ref([multiDistTemplate])
|
||||
|
||||
const { filteredCount } = useTemplateFiltering(templates)
|
||||
|
||||
expect(filteredCount.value).toBe(0)
|
||||
})
|
||||
|
||||
it('includes templates with multiple distributions when any match', () => {
|
||||
setDistribution('cloud')
|
||||
const templates = ref([multiDistTemplate])
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
} from '@/extensions/core/load3d/interfaces'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
||||
|
||||
type Load3dSerializedBase = {
|
||||
export type Load3dSerializedBase = {
|
||||
camera_info: CameraState | null
|
||||
model_3d_info: Model3DInfo
|
||||
}
|
||||
|
||||
@@ -591,31 +591,5 @@ describe('useWorkflowPersistenceV2', () => {
|
||||
'Comfy.BrowseTemplates'
|
||||
)
|
||||
})
|
||||
|
||||
it('does not open templates browser when template param is in URL', async () => {
|
||||
routeMocks.query = { template: 'default-template-id' }
|
||||
|
||||
const { initializeWorkflow } = mountWorkflowPersistence()
|
||||
await initializeWorkflow()
|
||||
|
||||
expect(loadBlankWorkflowMock).toHaveBeenCalled()
|
||||
expect(commandStoreMocks.execute).not.toHaveBeenCalledWith(
|
||||
'Comfy.BrowseTemplates'
|
||||
)
|
||||
})
|
||||
|
||||
it('does not open templates browser when template intent is preserved across /user-select redirect', async () => {
|
||||
preservedQueryMocks.payloads.template = {
|
||||
template: 'default-template-id'
|
||||
}
|
||||
|
||||
const { initializeWorkflow } = mountWorkflowPersistence()
|
||||
await initializeWorkflow()
|
||||
|
||||
expect(loadBlankWorkflowMock).toHaveBeenCalled()
|
||||
expect(commandStoreMocks.execute).not.toHaveBeenCalledWith(
|
||||
'Comfy.BrowseTemplates'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -161,24 +161,18 @@ export function useWorkflowPersistenceV2() {
|
||||
})
|
||||
}
|
||||
|
||||
const hasPreservedIntent = (namespace: string, key: string) => {
|
||||
if (typeof route.query[key] === 'string') return true
|
||||
hydratePreservedQuery(namespace)
|
||||
const merged = mergePreservedQueryIntoQuery(namespace, route.query)
|
||||
return typeof merged?.[key] === 'string'
|
||||
const hasSharedWorkflowIntent = () => {
|
||||
if (typeof route.query.share === 'string') return true
|
||||
hydratePreservedQuery(SHARE_NAMESPACE)
|
||||
const merged = mergePreservedQueryIntoQuery(SHARE_NAMESPACE, route.query)
|
||||
return typeof merged?.share === 'string'
|
||||
}
|
||||
|
||||
const hasSharedWorkflowIntent = () =>
|
||||
hasPreservedIntent(SHARE_NAMESPACE, 'share')
|
||||
|
||||
const hasTemplateUrlIntent = () =>
|
||||
hasPreservedIntent(TEMPLATE_NAMESPACE, 'template')
|
||||
|
||||
const loadDefaultWorkflow = async () => {
|
||||
if (!settingStore.get('Comfy.TutorialCompleted')) {
|
||||
await settingStore.set('Comfy.TutorialCompleted', true)
|
||||
await useWorkflowService().loadBlankWorkflow()
|
||||
if (!hasSharedWorkflowIntent() && !hasTemplateUrlIntent()) {
|
||||
if (!hasSharedWorkflowIntent()) {
|
||||
await useCommandStore().execute('Comfy.BrowseTemplates')
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user