Merge branch 'main' into copilot/fix-4468

This commit is contained in:
snomiao
2025-08-03 19:46:19 +08:00
committed by GitHub
18 changed files with 609 additions and 211 deletions

View File

@@ -1,99 +1,106 @@
name: Bug Report name: Bug Report
description: 'Something is not behaving as expected.' description: 'Report something that is not working correctly'
title: '[Bug]: ' title: '[Bug]: '
labels: ['Potential Bug'] labels: ['Potential Bug']
type: Bug type: Bug
body: body:
- type: markdown
attributes:
value: |
Before submitting a **Bug Report**, please ensure the following:
- **1:** You are running the latest version of ComfyUI.
- **2:** You have looked at the existing bug reports and made sure this isn't already reported.
- type: checkboxes - type: checkboxes
id: custom-nodes-test
attributes: attributes:
label: Custom Node Testing label: Prerequisites
description: Please confirm you have tried to reproduce the issue with all custom nodes disabled.
options: options:
- label: I have tried disabling custom nodes and the issue persists (see [how to disable custom nodes](https://docs.comfy.org/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled) if you need help) - label: I am running the latest version of ComfyUI
required: true
- label: I have searched existing issues to make sure this isn't a duplicate
required: true
- label: I have tested with all custom nodes disabled ([see how](https://docs.comfy.org/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled))
required: true required: true
- type: textarea - type: textarea
id: description
attributes: attributes:
label: Frontend Version label: What happened?
description: | description: A clear and concise description of the bug. Include screenshots or videos if helpful.
What is the frontend version you are using? You can check this in the settings dialog. placeholder: |
Example: "When I connect a VAE Decode node to a KSampler, the connection line appears but the workflow fails to execute with an error message..."
<details>
<summary>Click to show where to find the version</summary>
Open the setting by clicking the cog icon in the bottom-left of the screen, then click `About`.
![Frontend version](https://github.com/user-attachments/assets/561fb7c3-3012-457c-a494-9bdc1ff035c0)
</details>
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: 'What you expected to happen.'
validations:
required: true
- type: textarea
attributes:
label: Actual Behavior
description: 'What actually happened. Please include a screenshot / video clip of the issue if possible.'
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: reproduce
attributes: attributes:
label: Steps to Reproduce label: Steps to Reproduce
description: "Describe how to reproduce the issue. Please be sure to attach a workflow JSON or PNG, ideally one that doesn't require custom nodes to test. If the bug open happens when certain custom nodes are used, most likely that custom node is what has the bug rather than ComfyUI, in which case it should be reported to the node's author." description: How can we reproduce this issue? Please attach your workflow (JSON or PNG).
validations: placeholder: |
required: true 1. Add a KSampler node
- type: textarea 2. Connect it to...
attributes: 3. Click Queue Prompt
label: Debug Logs 4. See error
description: 'Please copy the output from your terminal logs here.' value: |
render: powershell 1.
validations: 2.
required: true 3.
- type: textarea
attributes:
label: Browser Logs
description: 'Please copy the output from your browser logs here. You can access this by pressing F12 to toggle the developer tools, then navigating to the Console tab.'
validations:
required: true
- type: textarea
attributes:
label: Setting JSON
description: 'Please upload the setting file here. The setting file is located at `user/default/comfy.settings.json`'
validations: validations:
required: true required: true
- type: dropdown - type: dropdown
id: browsers id: severity
attributes: attributes:
label: What browsers do you use to access the UI ? label: How is this affecting you?
multiple: true
options: options:
- Mozilla Firefox - Crashes ComfyUI completely
- Google Chrome - Workflow won't execute
- Brave - Feature doesn't work as expected
- Apple Safari - Visual/UI issue only
- Microsoft Edge - Minor inconvenience
- Android
- iOS
- Other
- type: textarea
attributes:
label: Other Information
description: 'Any other context, details, or screenshots that might help solve the issue.'
placeholder: 'Add any other relevant information here...'
validations: validations:
required: false required: true
- type: input
id: version
attributes:
label: ComfyUI Frontend Version
description: Found in Settings > About (e.g., "1.3.45")
placeholder: "1.3.45"
validations:
required: true
- type: dropdown
id: browser
attributes:
label: Browser
description: Which browser are you using?
options:
- Chrome/Chromium
- Firefox
- Safari
- Edge
- Other
validations:
required: true
- type: markdown
attributes:
value: |
## Additional Information (Optional)
*The following fields help us debug complex issues but are not required for most bug reports.*
- type: textarea
id: console-errors
attributes:
label: Console Errors
description: If you see red error messages in the browser console (F12), paste them here
render: javascript
- type: textarea
id: logs
attributes:
label: Logs
description: If relevant, paste any terminal/server logs here
render: shell
- type: textarea
id: additional
attributes:
label: Additional Context
description: Any other information that might help (OS, GPU, specific nodes involved, etc.)

View File

@@ -1,6 +1,6 @@
name: Feature Request name: Feature Request
description: Suggest an idea for this project description: Report a problem or limitation you're experiencing
title: '[Feature Request]: ' title: '[Feature]: '
labels: ['enhancement'] labels: ['enhancement']
type: Feature type: Feature
@@ -8,34 +8,74 @@ body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Is there an existing issue for this? label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the feature you want, and that it's not implemented in a recent build/commit. description: Please search to see if an issue already exists for the problem you're experiencing, and that it's not addressed in a recent build/commit.
options: options:
- label: I have searched the existing issues and checked the recent builds/commits - label: I have searched the existing issues and checked the recent builds/commits
required: true required: true
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
*Please fill this form with as much information as possible, provide screenshots and/or illustrations of the feature if possible* *Please focus on describing the problem you're experiencing rather than proposing specific solutions. This helps us design the best possible solution for you and other users.*
- type: textarea - type: textarea
id: feature id: problem
attributes: attributes:
label: What would your feature do ? label: What problem are you experiencing?
description: Tell us about your feature in a very clear and simple way, and what problem it would solve description: Describe the issue or limitation you're facing in your workflow
placeholder: |
Example: "I frequently lose work when switching between different projects because there's no way to save my current workspace state"
NOT: "Add a save button that exports the workspace"
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: workflow id: context
attributes: attributes:
label: Proposed workflow label: When does this problem occur?
description: Please provide us with step by step information on how you'd like the feature to be accessed and used description: Describe the specific situations or workflows where you encounter this issue
value: | placeholder: |
1. Go to .... - When working with large node graphs...
2. Press .... - During batch processing workflows...
3. ... - While collaborating with team members...
validations:
required: true
- type: dropdown
id: frequency
attributes:
label: How often do you encounter this problem?
options:
- Multiple times per day
- Daily
- Several times per week
- Weekly
- Occasionally
- Rarely
validations:
required: true
- type: dropdown
id: impact
attributes:
label: How much does this problem affect your workflow?
description: Help us understand the severity of this issue for you
options:
- Blocks me from completing tasks
- Significantly slows down my work
- Causes moderate inconvenience
- Minor annoyance
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: misc id: workaround
attributes: attributes:
label: Additional information label: Current workarounds
description: Add any other context or screenshots about the feature request here. description: How do you currently deal with this problem, if at all?
placeholder: |
Example: "I manually export and reimport nodes between projects, which takes 10-15 minutes each time"
- type: textarea
id: ideas
attributes:
label: Ideas for solutions (Optional)
description: If you have thoughts on potential solutions, feel free to share them here. However, we'll explore all possible options to find the best approach.
- type: textarea
id: additional
attributes:
label: Additional context
description: Add any other context, screenshots, or examples that help illustrate the problem.

2
.gitignore vendored
View File

@@ -11,6 +11,8 @@ node_modules
dist dist
dist-ssr dist-ssr
*.local *.local
# Claude configuration
.claude/*.local.json
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*

13
.husky/pre-commit Normal file → Executable file
View File

@@ -1,9 +1,4 @@
if [[ "$OS" == "Windows_NT" ]]; then #!/usr/bin/env bash
npx.cmd lint-staged
# Check for unused i18n keys in staged files npx lint-staged
npx.cmd tsx scripts/check-unused-i18n-keys.ts npx tsx scripts/check-unused-i18n-keys.ts
else
npx lint-staged
# Check for unused i18n keys in staged files
npx tsx scripts/check-unused-i18n-keys.ts
fi

View File

@@ -3,6 +3,10 @@
"playwright": { "playwright": {
"command": "npx", "command": "npx",
"args": ["-y", "@executeautomation/playwright-mcp-server"] "args": ["-y", "@executeautomation/playwright-mcp-server"]
},
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp"]
} }
} }
} }

108
CLAUDE.md
View File

@@ -1,58 +1,50 @@
- use `npm run` to see what commands are available # ComfyUI Frontend Project Guidelines
- For component communication, prefer Vue's event-based pattern (emit/@event-name) for state changes and notifications; use defineExpose with refs only for imperative operations that need direct control (like form.validate(), modal.open(), or editor.focus()); events promote loose coupling and are better for reusable components, while exposed methods are acceptable for tightly-coupled component pairs or when wrapping third-party libraries that require imperative APIs
- After making code changes, follow this general process: (1) Create unit tests, component tests, browser tests (if appropriate for each), (2) run unit tests, component tests, and browser tests until passing, (3) run typecheck, lint, format (with prettier) -- you can use `npm run` command to see the scripts available, (4) check if any READMEs (including nested) or documentation needs to be updated, (5) Decide whether the changes are worth adding new content to the external documentation for (or would requires changes to the external documentation) at https://docs.comfy.org, then present your suggestion ## Quick Commands
- When referencing PrimeVue, you can get all the docs here: https://primevue.org. Do this instead of making up or inferring names of Components
- When trying to set tailwind classes for dark theme, use "dark-theme:" prefix rather than "dark:" - `npm run`: See all available commands
- Never add lines to PR descriptions or commit messages that say "Generated with Claude Code" - `npm run typecheck`: Type checking
- When making PR names and commit messages, if you are going to add a prefix like "docs:", "feat:", "bugfix:", use square brackets around the prefix term and do not use a colon (e.g., should be "[docs]" rather than "docs:"). - `npm run lint`: Linting
- When I reference GitHub Repos related to Comfy-Org, you should proactively fetch or read the associated information in the repo. To do so, you should exhaust all options: (1) Check if we have a local copy of the repo, (2) Use the GitHub API to fetch the information; you may want to do this IN ADDITION to the other options, especially for reading specific branches/PRs/comments/reviews/metadata, and (3) curl the GitHub website and parse the html or json responses - `npm run format`: Prettier formatting
- For information about ComfyUI, ComfyUI_frontend, or ComfyUI-Manager, you can web search or download these wikis: https://deepwiki.com/Comfy-Org/ComfyUI-Manager, https://deepwiki.com/Comfy-Org/ComfyUI_frontend/1-overview, https://deepwiki.com/comfyanonymous/ComfyUI/2-core-architecture
- If a question/project is related to Comfy-Org, Comfy, or ComfyUI ecosystem, you should proactively use the Comfy docs to answer the question. The docs may be referenced with URLs like https://docs.comfy.org ## Development Workflow
- When operating inside a repo, check for README files at key locations in the repo detailing info about the contents of that folder. E.g., top-level key folders like tests-ui, browser_tests, composables, extensions/core, stores, services often have their own README.md files. When writing code, make sure to frequently reference these README files to understand the overall architecture and design of the project. Pay close attention to the snippets to learn particular patterns that seem to be there for a reason, as you should emulate those.
- Prefer running single tests, and not the whole test suite, for performance 1. Make code changes
- If using a lesser known or complex CLI tool, run the --help to see the documentation before deciding what to run, even if just for double-checking or verifying things. 2. Run tests (see subdirectory CLAUDE.md files)
- IMPORTANT: the most important goal when writing code is to create clean, best-practices, sustainable, and scalable public APIs and interfaces. Our app is used by thousands of users and we have thousands of mods/extensions that are constantly changing and updating; and we are also always updating. That's why it is IMPORTANT that we design systems and write code that follows practices of domain-driven design, object-oriented design, and design patterns (such that you can assure stability while allowing for all components around you to change and evolve). We ABSOLUTELY prioritize clean APIs and public interfaces that clearly define and restrict how/what the mods/extensions can access. 3. Run typecheck, lint, format
- If any of these technologies are referenced, you can proactively read their docs at these locations: https://primevue.org/theming, https://primevue.org/forms/, https://www.electronjs.org/docs/latest/api/browser-window, https://vitest.dev/guide/browser/, https://atlassian.design/components/pragmatic-drag-and-drop/core-package/drop-targets/, https://playwright.dev/docs/api/class-test, https://playwright.dev/docs/api/class-electron, https://www.algolia.com/doc/api-reference/rest-api/, https://pyav.org/docs/develop/cookbook/basics.html 4. Check README updates
- IMPORTANT: Never add Co-Authored by Claude or any reference to Claude or Claude Code in commit messages, PR descriptions, titles, or any documentation whatsoever 5. Consider docs.comfy.org updates
- The npm script to type check is called "typecheck" NOT "type check"
- Use the Vue 3 Composition API instead of the Options API when writing Vue components. An exception is when overriding or extending a PrimeVue component for compatibility, you may use the Options API. ## Git Conventions
- when we are solving an issue we know the link/number for, we should add "Fixes #n" (where n is the issue number) to the PR description.
- Never write css if you can accomplish the same thing with tailwind utility classes - Use [prefix] format: [feat], [bugfix], [docs]
- Utilize ref and reactive for reactive state - Add "Fixes #n" to PR descriptions
- Implement computed properties with computed() - Never mention Claude/AI in commits
- Use watch and watchEffect for side effects
- Implement lifecycle hooks with onMounted, onUpdated, etc. ## External Resources
- Utilize provide/inject for dependency injection
- Use vue 3.5 style of default prop declaration. Do not define a `props` variable; instead, destructure props. Since vue 3.5, destructuring props does not strip them of reactivity. - PrimeVue docs: <https://primevue.org>
- Use Tailwind CSS for styling - ComfyUI docs: <https://docs.comfy.org>
- Leverage VueUse functions for performance-enhancing styles - Electron: <https://www.electronjs.org/docs/latest/>
- Use lodash for utility functions - Wiki: <https://deepwiki.com/Comfy-Org/ComfyUI_frontend/1-overview>
- Implement proper props and emits definitions
- Utilize Vue 3's Teleport component when needed ## Project Philosophy
- Use Suspense for async components
- Implement proper error handling - Clean, stable public APIs
- Follow Vue 3 style guide and naming conventions - Domain-driven design
- IMPORTANT: Use vue-i18n for ALL user-facing strings - no hard-coded text in services/utilities. Place new translation entries in src/locales/en/main.json - Thousands of users and extensions
- Avoid using `@ts-expect-error` to work around type issues. We needed to employ it to migrate to TypeScript, but it should not be viewed as an accepted practice or standard. - Prioritize clean interfaces that restrict extension access
- DO NOT use deprecated PrimeVue components. Use these replacements instead:
* `Dropdown` → Use `Select` (import from 'primevue/select') ## Repository Navigation
* `OverlayPanel` → Use `Popover` (import from 'primevue/popover')
* `Calendar` → Use `DatePicker` (import from 'primevue/datepicker') - Check README files in key folders (tests-ui, browser_tests, composables, etc.)
* `InputSwitch` → Use `ToggleSwitch` (import from 'primevue/toggleswitch') - Prefer running single tests for performance
* `Sidebar` → Use `Drawer` (import from 'primevue/drawer') - Use --help for unfamiliar CLI tools
* `Chips` → Use `AutoComplete` with multiple enabled and typeahead disabled
* `TabMenu` → Use `Tabs` without panels ## GitHub Integration
* `Steps` → Use `Stepper` without panels
* `InlineMessage` → Use `Message` component When referencing Comfy-Org repos:
* Use `api.apiURL()` for all backend API calls and routes
- Actual API endpoints like /prompt, /queue, /view, etc. 1. Check for local copy
- Image previews: `api.apiURL('/view?...')` 2. Use GitHub API for branches/PRs/metadata
- Any backend-generated content or dynamic routes 3. Curl GitHub website if needed
* Use `api.fileURL()` for static files served from the public folder:
- Templates: `api.fileURL('/templates/default.json')`
- Extensions: `api.fileURL(extensionPath)` for loading JS modules
- Any static assets that exist in the public directory
- When implementing code that outputs raw HTML (e.g., using v-html directive), always ensure dynamic content has been properly sanitized with DOMPurify or validated through trusted sources. Prefer Vue templates over v-html when possible.
- For any async operations (API calls, timers, etc), implement cleanup/cancellation in component unmount to prevent memory leaks
- Extract complex template conditionals into separate components or computed properties
- Error messages should be actionable and user-friendly (e.g., "Failed to load data. Please refresh the page." instead of "Unknown error")

17
browser_tests/CLAUDE.md Normal file
View File

@@ -0,0 +1,17 @@
# E2E Testing Guidelines
## Browser Tests
- Test user workflows
- Use Playwright fixtures
- Follow naming conventions
## Best Practices
- Check assets/ for test data
- Prefer specific selectors
- Test across viewports
## Testing Process
After code changes:
1. Create browser tests as appropriate
2. Run tests until passing
3. Then run typecheck, lint, format

View File

@@ -0,0 +1,182 @@
{
"id": "test-missing-nodes-in-subgraph",
"revision": 0,
"last_node_id": 2,
"last_link_id": 0,
"nodes": [
{
"id": 1,
"type": "KSampler",
"pos": [100, 100],
"size": [270, 262],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": []
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [0, "randomize", 20, 8, "euler", "simple", 1]
},
{
"id": 2,
"type": "subgraph-with-missing-node",
"pos": [400, 100],
"size": [144, 46],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "input1",
"type": "CONDITIONING",
"link": null
}
],
"outputs": [
{
"name": "output1",
"type": "LATENT",
"links": null
}
],
"properties": {},
"widgets_values": []
}
],
"links": [],
"groups": [],
"definitions": {
"subgraphs": [
{
"id": "subgraph-with-missing-node",
"version": 1,
"state": {
"lastGroupId": 0,
"lastNodeId": 2,
"lastLinkId": 2,
"lastRerouteId": 0
},
"revision": 0,
"config": {},
"name": "Subgraph with Missing Node",
"inputNode": {
"id": -10,
"bounding": [100, 200, 120, 60]
},
"outputNode": {
"id": -20,
"bounding": [500, 200, 120, 60]
},
"inputs": [
{
"id": "input1-id",
"name": "input1",
"type": "CONDITIONING",
"linkIds": [1],
"pos": {
"0": 150,
"1": 220
}
}
],
"outputs": [
{
"id": "output1-id",
"name": "output1",
"type": "LATENT",
"linkIds": [2],
"pos": {
"0": 520,
"1": 220
}
}
],
"widgets": [],
"nodes": [
{
"id": 1,
"type": "MISSING_NODE_TYPE_IN_SUBGRAPH",
"pos": [250, 180],
"size": [200, 100],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "input",
"type": "CONDITIONING",
"link": 1
}
],
"outputs": [
{
"name": "output",
"type": "LATENT",
"links": [2]
}
],
"properties": {
"Node name for S&R": "MISSING_NODE_TYPE_IN_SUBGRAPH"
},
"widgets_values": ["some", "widget", "values"]
}
],
"links": [
{
"id": 1,
"origin_id": -10,
"origin_slot": 0,
"target_id": 1,
"target_slot": 0,
"type": "CONDITIONING"
},
{
"id": 2,
"origin_id": 1,
"origin_slot": 0,
"target_id": -20,
"target_slot": 0,
"type": "LATENT"
}
]
}
]
},
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [0, 0]
}
},
"version": 0.4
}

View File

@@ -13,6 +13,21 @@ test.describe('Load workflow warning', () => {
const missingNodesWarning = comfyPage.page.locator('.comfy-missing-nodes') const missingNodesWarning = comfyPage.page.locator('.comfy-missing-nodes')
await expect(missingNodesWarning).toBeVisible() await expect(missingNodesWarning).toBeVisible()
}) })
test('Should display a warning when loading a workflow with missing nodes in subgraphs', async ({
comfyPage
}) => {
await comfyPage.loadWorkflow('missing_nodes_in_subgraph')
// Wait for the element with the .comfy-missing-nodes selector to be visible
const missingNodesWarning = comfyPage.page.locator('.comfy-missing-nodes')
await expect(missingNodesWarning).toBeVisible()
// Verify the missing node text includes subgraph context
const warningText = await missingNodesWarning.textContent()
expect(warningText).toContain('MISSING_NODE_TYPE_IN_SUBGRAPH')
expect(warningText).toContain('in subgraph')
})
}) })
test('Does not report warning on undo/redo', async ({ comfyPage }) => { test('Does not report warning on undo/redo', async ({ comfyPage }) => {
@@ -369,7 +384,7 @@ test.describe('Signin dialog', () => {
await textBox.press('Control+c') await textBox.press('Control+c')
await comfyPage.page.evaluate(() => { await comfyPage.page.evaluate(() => {
window['app'].extensionManager.dialog.showSignInDialog() void window['app'].extensionManager.dialog.showSignInDialog()
}) })
const input = comfyPage.page.locator('#comfy-org-sign-in-password') const input = comfyPage.page.locator('#comfy-org-sign-in-password')

12
package-lock.json generated
View File

@@ -1,18 +1,18 @@
{ {
"name": "@comfyorg/comfyui-frontend", "name": "@comfyorg/comfyui-frontend",
"version": "1.25.3", "version": "1.25.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@comfyorg/comfyui-frontend", "name": "@comfyorg/comfyui-frontend",
"version": "1.25.3", "version": "1.25.4",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"dependencies": { "dependencies": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
"@comfyorg/comfyui-electron-types": "^0.4.43", "@comfyorg/comfyui-electron-types": "^0.4.43",
"@comfyorg/litegraph": "^0.16.20", "@comfyorg/litegraph": "^0.17.0",
"@primevue/forms": "^4.2.5", "@primevue/forms": "^4.2.5",
"@primevue/themes": "^4.2.5", "@primevue/themes": "^4.2.5",
"@sentry/vue": "^8.48.0", "@sentry/vue": "^8.48.0",
@@ -1009,9 +1009,9 @@
"license": "GPL-3.0-only" "license": "GPL-3.0-only"
}, },
"node_modules/@comfyorg/litegraph": { "node_modules/@comfyorg/litegraph": {
"version": "0.16.20", "version": "0.17.0",
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.16.20.tgz", "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.17.0.tgz",
"integrity": "sha512-8iUBhKYkr9qV6vWxC3C9Wea9K7iHwyDHxxN6OrhE9sySYfUA14XuNpVMaC8eVUaIm5KBOSmr/Q1J2XVHsHEISg==", "integrity": "sha512-5Xu/G5NJFDucJontxXeXhzcaFuEXZznyg8u+joSjbe2G1991/tO7hYrPjXph2FLcovkSnEhG4ixdhyAwubGS3w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@cspotcode/source-map-support": { "node_modules/@cspotcode/source-map-support": {

View File

@@ -1,7 +1,7 @@
{ {
"name": "@comfyorg/comfyui-frontend", "name": "@comfyorg/comfyui-frontend",
"private": true, "private": true,
"version": "1.25.3", "version": "1.25.4",
"type": "module", "type": "module",
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend", "repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
"homepage": "https://comfy.org", "homepage": "https://comfy.org",
@@ -79,7 +79,7 @@
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
"@comfyorg/comfyui-electron-types": "^0.4.43", "@comfyorg/comfyui-electron-types": "^0.4.43",
"@comfyorg/litegraph": "^0.16.20", "@comfyorg/litegraph": "^0.17.0",
"@primevue/forms": "^4.2.5", "@primevue/forms": "^4.2.5",
"@primevue/themes": "^4.2.5", "@primevue/themes": "^4.2.5",
"@sentry/vue": "^8.48.0", "@sentry/vue": "^8.48.0",

44
src/CLAUDE.md Normal file
View File

@@ -0,0 +1,44 @@
# Source Code Guidelines
## Service Layer
### API Calls
- Use `api.apiURL()` for backend endpoints
- Use `api.fileURL()` for static files
- Examples:
- Backend: `api.apiURL('/prompt')`
- Static: `api.fileURL('/templates/default.json')`
### Error Handling
- User-friendly and actionable messages
- Proper error propagation
### Security
- Sanitize HTML with DOMPurify
- Validate trusted sources
- Never log secrets
## State Management (Stores)
### Store Design
- Follow domain-driven design
- Clear public interfaces
- Restrict extension access
### Best Practices
- Use TypeScript for type safety
- Implement proper error handling
- Clean up subscriptions
- Avoid @ts-expect-error
## General Guidelines
- Use lodash for utility functions
- Implement proper TypeScript types
- Follow Vue 3 composition API style guide
- Use vue-i18n for ALL user-facing strings in `src/locales/en/main.json`

45
src/components/CLAUDE.md Normal file
View File

@@ -0,0 +1,45 @@
# Component Guidelines
## Vue 3 Composition API
- Use setup() function
- Destructure props (Vue 3.5 style)
- Use ref/reactive for state
- Implement computed() for derived state
- Use provide/inject for dependency injection
## Component Communication
- Prefer `emit/@event-name` for state changes
- Use `defineExpose` only for imperative operations (`form.validate()`, `modal.open()`)
- Events promote loose coupling
## UI Framework
- Deprecated PrimeVue component replacements:
- Dropdown → Select
- OverlayPanel → Popover
- Calendar → DatePicker
- InputSwitch → ToggleSwitch
- Sidebar → Drawer
- Chips → AutoComplete with multiple enabled
- TabMenu → Tabs without panels
- Steps → Stepper without panels
- InlineMessage → Message
## Styling
- Use Tailwind CSS only (no custom CSS)
- Dark theme: use "dark-theme:" prefix
- For common operations, try to use existing VueUse composables that automatically handle effect scope
- Example: Use `useElementHover` instead of manually managing mouseover/mouseout event listeners
- Example: Use `useIntersectionObserver` for visibility detection instead of custom scroll handlers
## Best Practices
- Extract complex conditionals to computed
- Implement cleanup for async operations
- Use vue-i18n for ALL UI strings
- Use lifecycle hooks: onMounted, onUpdated
- Use Teleport/Suspense when needed
- Proper props and emits definitions

View File

@@ -8,7 +8,6 @@ import { api } from '@/scripts/api'
import { app } from '@/scripts/app' import { app } from '@/scripts/app'
import { useCanvasStore } from '@/stores/graphStore' import { useCanvasStore } from '@/stores/graphStore'
import { useSettingStore } from '@/stores/settingStore' import { useSettingStore } from '@/stores/settingStore'
import { useWorkflowStore } from '@/stores/workflowStore'
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore' import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
interface GraphCallbacks { interface GraphCallbacks {
@@ -21,7 +20,6 @@ export function useMinimap() {
const settingStore = useSettingStore() const settingStore = useSettingStore()
const canvasStore = useCanvasStore() const canvasStore = useCanvasStore()
const colorPaletteStore = useColorPaletteStore() const colorPaletteStore = useColorPaletteStore()
const workflowStore = useWorkflowStore()
const containerRef = ref<HTMLDivElement>() const containerRef = ref<HTMLDivElement>()
const canvasRef = ref<HTMLCanvasElement>() const canvasRef = ref<HTMLCanvasElement>()
@@ -110,29 +108,6 @@ export function useMinimap() {
const canvas = computed(() => canvasStore.canvas) const canvas = computed(() => canvasStore.canvas)
const graph = ref(app.canvas?.graph) const graph = ref(app.canvas?.graph)
// Update graph ref when subgraph context changes
watch(
() => workflowStore.activeSubgraph,
() => {
graph.value = app.canvas?.graph
// Force viewport update when switching subgraphs
if (initialized.value && visible.value) {
updateViewport()
}
}
)
// Update viewport when switching workflows
watch(
() => workflowStore.activeWorkflow,
() => {
// Force viewport update when switching workflows
if (initialized.value && visible.value) {
updateViewport()
}
}
)
const containerStyles = computed(() => ({ const containerStyles = computed(() => ({
width: `${width}px`, width: `${width}px`,
height: `${height}px`, height: `${height}px`,
@@ -377,6 +352,8 @@ export function useMinimap() {
needsBoundsUpdate.value = false needsBoundsUpdate.value = false
updateFlags.value.bounds = false updateFlags.value.bounds = false
needsFullRedraw.value = true needsFullRedraw.value = true
// When bounds change, we need to update the viewport position
updateFlags.value.viewport = true
} }
if ( if (
@@ -386,6 +363,11 @@ export function useMinimap() {
) { ) {
renderMinimap() renderMinimap()
} }
// Update viewport if needed (e.g., after bounds change)
if (updateFlags.value.viewport) {
updateViewport()
}
} }
const checkForChanges = useThrottleFn(() => { const checkForChanges = useThrottleFn(() => {
@@ -660,7 +642,7 @@ export function useMinimap() {
await init() await init()
} }
}, },
{ immediate: true } { immediate: true, flush: 'post' }
) )
watch(visible, async (isVisible) => { watch(visible, async (isVisible) => {
@@ -678,6 +660,8 @@ export function useMinimap() {
await nextTick() await nextTick()
await nextTick()
updateMinimap() updateMinimap()
updateViewport() updateViewport()
resumeChangeDetection() resumeChangeDetection()

View File

@@ -458,6 +458,24 @@ export type WorkflowJSON10 = z.infer<typeof zComfyWorkflow1>
export type ComfyWorkflowJSON = z.infer< export type ComfyWorkflowJSON = z.infer<
typeof zComfyWorkflow | typeof zComfyWorkflow1 typeof zComfyWorkflow | typeof zComfyWorkflow1
> >
export type SubgraphDefinition = z.infer<typeof zSubgraphDefinition>
/**
* Type guard to check if an object is a SubgraphDefinition.
* This helps TypeScript understand the type when z.lazy() breaks inference.
*/
export function isSubgraphDefinition(obj: any): obj is SubgraphDefinition {
return (
obj &&
typeof obj === 'object' &&
'id' in obj &&
'name' in obj &&
'nodes' in obj &&
Array.isArray(obj.nodes) &&
'inputNode' in obj &&
'outputNode' in obj
)
}
const zWorkflowVersion = z.object({ const zWorkflowVersion = z.object({
version: z.number() version: z.number()

View File

@@ -23,7 +23,8 @@ import {
ComfyApiWorkflow, ComfyApiWorkflow,
type ComfyWorkflowJSON, type ComfyWorkflowJSON,
type ModelFile, type ModelFile,
type NodeId type NodeId,
isSubgraphDefinition
} from '@/schemas/comfyWorkflowSchema' } from '@/schemas/comfyWorkflowSchema'
import { import {
type ComfyNodeDef as ComfyNodeDefV1, type ComfyNodeDef as ComfyNodeDefV1,
@@ -1092,23 +1093,51 @@ export class ComfyApp {
const embeddedModels: ModelFile[] = [] const embeddedModels: ModelFile[] = []
for (let n of graphData.nodes) { const collectMissingNodesAndModels = (
// Patch T2IAdapterLoader to ControlNetLoader since they are the same node now nodes: ComfyWorkflowJSON['nodes'],
if (n.type == 'T2IAdapterLoader') n.type = 'ControlNetLoader' path: string = ''
if (n.type == 'ConditioningAverage ') n.type = 'ConditioningAverage' //typo fix ) => {
if (n.type == 'SDV_img2vid_Conditioning') for (let n of nodes) {
n.type = 'SVD_img2vid_Conditioning' //typo fix // Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
if (n.type == 'T2IAdapterLoader') n.type = 'ControlNetLoader'
if (n.type == 'ConditioningAverage ') n.type = 'ConditioningAverage' //typo fix
if (n.type == 'SDV_img2vid_Conditioning')
n.type = 'SVD_img2vid_Conditioning' //typo fix
// Find missing node types // Find missing node types
if (!(n.type in LiteGraph.registered_node_types)) { if (!(n.type in LiteGraph.registered_node_types)) {
missingNodeTypes.push(n.type) // Include context about subgraph location if applicable
n.type = sanitizeNodeName(n.type) if (path) {
missingNodeTypes.push({
type: n.type,
hint: `in subgraph '${path}'`
})
} else {
missingNodeTypes.push(n.type)
}
n.type = sanitizeNodeName(n.type)
}
// Collect models metadata from node
const selectedModels = getSelectedModelsMetadata(n)
if (selectedModels?.length) {
embeddedModels.push(...selectedModels)
}
} }
}
// Collect models metadata from node // Process nodes at the top level
const selectedModels = getSelectedModelsMetadata(n) collectMissingNodesAndModels(graphData.nodes)
if (selectedModels?.length) {
embeddedModels.push(...selectedModels) // Process nodes in subgraphs
if (graphData.definitions?.subgraphs) {
for (const subgraph of graphData.definitions.subgraphs) {
if (isSubgraphDefinition(subgraph)) {
collectMissingNodesAndModels(
subgraph.nodes,
subgraph.name || subgraph.id
)
}
} }
} }

View File

@@ -33,6 +33,17 @@ export const useSubgraphNavigationStore = defineStore(
maxSize: 32 maxSize: 32
}) })
/**
* Get the ID of the root graph for the currently active workflow.
* @returns The ID of the root graph for the currently active workflow.
*/
const getCurrentRootGraphId = () => {
const canvas = canvasStore.getCanvas()
if (!canvas) return 'root'
return canvas.graph?.rootGraph?.id ?? 'root'
}
/** /**
* A stack representing subgraph navigation history from the root graph to * A stack representing subgraph navigation history from the root graph to
* the current opened subgraph. * the current opened subgraph.
@@ -117,13 +128,13 @@ export const useSubgraphNavigationStore = defineStore(
saveViewport(prevSubgraph.id) saveViewport(prevSubgraph.id)
} else if (!prevSubgraph && subgraph) { } else if (!prevSubgraph && subgraph) {
// Leaving root graph to enter a subgraph // Leaving root graph to enter a subgraph
saveViewport('root') saveViewport(getCurrentRootGraphId())
} }
const isInRootGraph = !subgraph const isInRootGraph = !subgraph
if (isInRootGraph) { if (isInRootGraph) {
idStack.value.length = 0 idStack.value.length = 0
restoreViewport('root') restoreViewport(getCurrentRootGraphId())
return return
} }

13
tests-ui/CLAUDE.md Normal file
View File

@@ -0,0 +1,13 @@
# Unit Testing Guidelines
## Testing Approach
- Write tests for new features
- Run single tests for performance
- Follow existing test patterns
## Test Structure
- Check @tests-ui/README.md for guidelines
- Use existing test utilities
- Mock external dependencies