Compare commits

...

31 Commits

Author SHA1 Message Date
christian-byrne
11c46ea62d Refactor Comfy version retrieval and update changelog version description 2025-01-18 13:04:22 -07:00
christian-byrne
86789ceb9d Show changelog on new version 2025-01-18 12:25:04 -07:00
Chenlei Hu
1a4e77a3ab [Desktop] Ctrl+w to close workflow tab (#2282) 2025-01-17 20:02:54 -05:00
Chenlei Hu
de570712df 1.7.14 (#2281) 2025-01-17 18:04:43 -05:00
Chenlei Hu
44612e8f97 [Desktop] Add privacy policy link to install view about dialog (#2280)
Co-authored-by: github-actions <github-actions@github.com>
2025-01-17 18:04:08 -05:00
bymyself
3df911c1bf Add consent prompt view (#2268)
Co-authored-by: github-actions <github-actions@github.com>
2025-01-17 17:39:50 -05:00
Chenlei Hu
af26b9ad6d [Desktop] Report completed generation status (#2279) 2025-01-17 17:36:46 -05:00
Chenlei Hu
d503873980 Move queueStore update from QueueSidebarTab to GraphView (#2278) 2025-01-17 17:14:22 -05:00
bymyself
842a9f74fc [BrowserTest] Fix flaky gallery test (#2150) 2025-01-17 17:11:49 -05:00
Chenlei Hu
29551a36b3 Add .cursorrules (#2277) 2025-01-17 16:35:25 -05:00
filtered
d6e5c8950c [Desktop] Loading screen (#2274)
Co-authored-by: huchenlei <huchenlei@proton.me>
2025-01-17 11:12:03 -05:00
Chenlei Hu
ad1c1ce9c2 [chore] Update electron-types to 0.4.9 (#2276) 2025-01-17 10:59:41 -05:00
Benjamin Lu
cb9d2c6bae Caching brush settings in mask editor (#2271)
Co-authored-by: Benjamin Lu <templu1107@proton.me>
2025-01-17 10:38:23 -05:00
Chenlei Hu
7fd41eeaba 1.7.13 (#2270) 2025-01-16 11:51:58 -05:00
filtered
79fee6ac72 Fix collapsed node textarea causes UI inconsistency (#2267) 2025-01-16 11:50:53 -05:00
Benjamin Lu
edd58cd153 Hotfix scoped --sidebar-width uasges in maskeditor.ts (#2269)
Co-authored-by: Benjamin Lu <templu1107@proton.me>
2025-01-16 11:36:11 -05:00
Chenlei Hu
e153508955 1.7.12 (#2265) 2025-01-15 20:19:24 -05:00
Chenlei Hu
237fca0bf1 [CodeHealth] Use scoped CSS for SideToolbar (#2264) 2025-01-15 20:16:39 -05:00
Chenlei Hu
65542b885a [Style] Fix root CSS selector (#2263) 2025-01-15 19:44:46 -05:00
Chenlei Hu
f739f704af [CodeHealth] Use scoped CSS in views (#2262) 2025-01-15 19:35:01 -05:00
Chenlei Hu
37abdbe35d [Desktop] Add install screen stepper change metrics (#2261) 2025-01-15 19:27:05 -05:00
Chenlei Hu
ff445f5c95 Apply min col on logs terminal for colab (#2260) 2025-01-15 17:16:33 -05:00
Chenlei Hu
84b652a281 [CodeHealth] Convert useAutoSize to kwargs (#2259) 2025-01-15 17:06:45 -05:00
Chenlei Hu
184291d21b [Settings] Enable Comfy.Window.UnloadConfirmation by default (#2258) 2025-01-15 16:37:11 -05:00
Chenlei Hu
d7fb25a36a Don't prompt unsaved when there is no unsaved workflow on window close (#2257) 2025-01-15 16:34:05 -05:00
Comfy Org PR Bot
c039a60fcc Update locales for node definitions (#2256)
Co-authored-by: huchenlei <20929282+huchenlei@users.noreply.github.com>
Co-authored-by: huchenlei <huchenlei@proton.me>
2025-01-15 16:23:45 -05:00
Chenlei Hu
3b6108c26e Add work dir to i18n-node-defs.yaml (#2255) 2025-01-15 16:19:13 -05:00
Chenlei Hu
49bb247526 1.7.11 (#2250) 2025-01-14 23:34:58 -05:00
bymyself
dd005f5fa5 Allow parent component to pass tags to issue report panel (#2247) 2025-01-14 23:34:11 -05:00
Chenlei Hu
bf90b458d3 [Desktop] Add clarification of migration from existing install (#2249)
Co-authored-by: github-actions <github-actions@github.com>
2025-01-14 23:33:53 -05:00
bymyself
7e78c5b1dc Fix type error in BaseViewTemplate (#2245) 2025-01-14 20:19:35 -05:00
50 changed files with 1030 additions and 181 deletions

43
.cusorrules Normal file
View File

@@ -0,0 +1,43 @@
// Vue 3 Composition API .cursorrules
// Vue 3 Composition API best practices
const vue3CompositionApiBestPractices = [
"Use setup() function for component logic",
"Utilize ref and reactive for reactive state",
"Implement computed properties with computed()",
"Use watch and watchEffect for side effects",
"Implement lifecycle hooks with onMounted, onUpdated, etc.",
"Utilize provide/inject for dependency injection",
]
// Folder structure
const folderStructure = `
src/
components/
constants/
hooks/
views/
stores/
services/
App.vue
main.ts
`;
// Tailwind CSS best practices
const tailwindCssBestPractices = [
"Use Tailwind CSS for styling",
"Implement responsive design with Tailwind CSS",
]
// Additional instructions
const additionalInstructions = `
1. Leverage VueUse functions for performance-enhancing styles
2. Use lodash for utility functions
3. Use TypeScript for type safety
4. Implement proper props and emits definitions
5. Utilize Vue 3's Teleport component when needed
6. Use Suspense for async components
7. Implement proper error handling
8. Follow Vue 3 style guide and naming conventions
9. Use Vite for fast development and building
`;

View File

@@ -42,6 +42,7 @@ jobs:
Automated PR to update locales for node definitions
This PR was created automatically by the frontend update workflow.
branch: update-locales-node-defs-{{ github.event.inputs.trigger_type }}-{{ github.run_id }}
branch: update-locales-node-defs-${{ github.event.inputs.trigger_type }}-${{ github.run_id }}
base: main
labels: dependencies
labels: dependencies
path: ComfyUI_frontend

View File

@@ -0,0 +1,37 @@
{
"last_node_id": 1,
"last_link_id": 0,
"nodes": [
{
"id": 1,
"type": "CLIPTextEncode",
"pos": [20, 50],
"size": [400, 200],
"flags": { "collapsed": true },
"order": 0,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": null,
"localized_name": "clip"
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": null,
"localized_name": "CONDITIONING"
}
],
"properties": {},
"widgets_values": ["Should not be displayed."]
}
],
"links": [],
"groups": [],
"config": {},
"version": 0.4
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,27 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './fixtures/ComfyPage'
test.describe('DOM Widget', () => {
test('Collapsed multiline textarea is not visible', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('collapsed_multiline')
expect(comfyPage.page.locator('.comfy-multiline-input')).not.toBeVisible()
})
test('Multiline textarea correctly collapses', async ({ comfyPage }) => {
const multilineTextAreas = comfyPage.page.locator('.comfy-multiline-input')
const firstMultiline = multilineTextAreas.first()
const lastMultiline = multilineTextAreas.last()
await expect(firstMultiline).toBeVisible()
await expect(lastMultiline).toBeVisible()
const nodes = await comfyPage.getNodeRefsByType('CLIPTextEncode')
for (const node of nodes) {
await node.click('collapse')
}
await expect(firstMultiline).not.toBeVisible()
await expect(lastMultiline).not.toBeVisible()
})
})

View File

@@ -194,6 +194,10 @@ export class QueueSidebarTab extends SidebarTab {
return this.root.locator('.no-results-placeholder')
}
get galleryImage() {
return this.page.locator('.galleria-image')
}
private getToggleExpandButton(isExpanded: boolean) {
const iconSelector = isExpanded ? '.pi-image' : '.pi-images'
return this.root.locator(`.toggle-expanded-button ${iconSelector}`)
@@ -256,14 +260,24 @@ export class QueueSidebarTab extends SidebarTab {
async openTaskPreview(taskIndex: number) {
const previewButton = this.getTaskPreviewButton(taskIndex)
await previewButton.hover()
await previewButton.click()
return this.getGalleryImage(taskIndex).waitFor({ state: 'visible' })
return this.galleryImage.waitFor({ state: 'visible' })
}
getGalleryImage(galleryItemIndex: number) {
// Aria labels of Galleria items are 1-based indices
const galleryLabel = `${galleryItemIndex + 1}`
return this.page.getByLabel(galleryLabel).locator('.galleria-image')
getGalleryImage(imageFilename: string) {
return this.galleryImage.and(this.page.getByAltText(imageFilename))
}
getTaskImage(imageFilename: string) {
return this.tasks.getByAltText(imageFilename)
}
/** Trigger the queue store and tasks to update */
async triggerTasksUpdate() {
await this.page.evaluate(() => {
window['app']['api'].dispatchCustomEvent('status', {
exec_info: { queue_remaining: 0 }
})
})
}
}

View File

@@ -132,11 +132,12 @@ export default class TaskHistory {
private addTask(task: HistoryTaskItem) {
setPromptId(task)
setQueueIndex(task)
this.tasks.push(task)
this.tasks.unshift(task) // Tasks are added to the front of the queue
}
clearTasks() {
clearTasks(): this {
this.tasks = []
return this
}
withTask(
@@ -155,7 +156,7 @@ export default class TaskHistory {
/** Repeats the last task in the task history a specified number of times. */
repeat(n: number): this {
for (let i = 0; i < n; i++)
this.addTask(structuredClone(this.tasks.at(-1)) as HistoryTaskItem)
this.addTask(structuredClone(this.tasks.at(0)) as HistoryTaskItem)
return this
}
}

View File

@@ -1,9 +1,6 @@
import { expect, mergeTests } from '@playwright/test'
import { expect } from '@playwright/test'
import { comfyPageFixture } from './fixtures/ComfyPage'
import { webSocketFixture } from './fixtures/ws'
const test = mergeTests(comfyPageFixture, webSocketFixture)
import { comfyPageFixture as test } from './fixtures/ComfyPage'
test.describe('Menu', () => {
test.beforeEach(async ({ comfyPage }) => {
@@ -948,66 +945,61 @@ test.describe.skip('Queue sidebar', () => {
})
test.describe('Gallery', () => {
const firstImage = 'example.webp'
const secondImage = 'image32x32.webp'
test.beforeEach(async ({ comfyPage }) => {
await comfyPage
.setupHistory()
.withTask(['example.webp'])
.repeat(1)
.withTask([secondImage])
.withTask([firstImage])
.setupRoutes()
await comfyPage.menu.queueTab.open()
await comfyPage.menu.queueTab.waitForTasks()
await comfyPage.menu.queueTab.openTaskPreview(0)
})
test('displays gallery image after opening task preview', async ({
comfyPage
}) => {
await comfyPage.menu.queueTab.openTaskPreview(0)
expect(comfyPage.menu.queueTab.getGalleryImage(0)).toBeVisible()
await comfyPage.nextFrame()
expect(comfyPage.menu.queueTab.getGalleryImage(firstImage)).toBeVisible()
})
test('should maintain active gallery item when new tasks are added', async ({
comfyPage,
ws
test('maintains active gallery item when new tasks are added', async ({
comfyPage
}) => {
const initialIndex = 0
await comfyPage.menu.queueTab.openTaskPreview(initialIndex)
// Add a new task while the gallery is still open
comfyPage.setupHistory().withTask(['example.webp'])
await ws.trigger({
type: 'status',
data: {
status: { exec_info: { queue_remaining: 0 } }
}
})
await comfyPage.menu.queueTab.waitForTasks()
// The index of all tasks increments when a new task is prepended
const expectIndex = initialIndex + 1
expect(comfyPage.menu.queueTab.getGalleryImage(expectIndex)).toBeVisible()
const newImage = 'image64x64.webp'
comfyPage.setupHistory().withTask([newImage])
await comfyPage.menu.queueTab.triggerTasksUpdate()
await comfyPage.page.waitForTimeout(500)
const newTask = comfyPage.menu.queueTab.tasks.getByAltText(newImage)
await newTask.waitFor({ state: 'visible' })
// The active gallery item should still be the initial image
expect(comfyPage.menu.queueTab.getGalleryImage(firstImage)).toBeVisible()
})
test.describe('Gallery navigation', () => {
const paths: {
description: string
path: ('Right' | 'Left')[]
expectIndex: number
end: string
}[] = [
{ description: 'Right', path: ['Right'], expectIndex: 1 },
{ description: 'Left', path: ['Right', 'Left'], expectIndex: 0 },
{ description: 'Right wrap', path: ['Right', 'Right'], expectIndex: 0 },
{ description: 'Left wrap', path: ['Left'], expectIndex: 1 }
{ description: 'Right', path: ['Right'], end: secondImage },
{ description: 'Left', path: ['Right', 'Left'], end: firstImage },
{ description: 'Left wrap', path: ['Left'], end: secondImage },
{ description: 'Right wrap', path: ['Right', 'Right'], end: firstImage }
]
paths.forEach(({ description, path, expectIndex }) => {
paths.forEach(({ description, path, end }) => {
test(`can navigate gallery ${description}`, async ({ comfyPage }) => {
await comfyPage.menu.queueTab.openTaskPreview(0)
for (const direction of path)
await comfyPage.page.keyboard.press(`Arrow${direction}`)
expect(
comfyPage.menu.queueTab.getGalleryImage(expectIndex)
).toBeVisible()
await comfyPage.page.keyboard.press(`Arrow${direction}`, {
delay: 256
})
await comfyPage.nextFrame()
expect(comfyPage.menu.queueTab.getGalleryImage(end)).toBeVisible()
})
})
})

13
package-lock.json generated
View File

@@ -1,16 +1,16 @@
{
"name": "@comfyorg/comfyui-frontend",
"version": "1.7.10",
"version": "1.7.14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@comfyorg/comfyui-frontend",
"version": "1.7.10",
"version": "1.7.14",
"license": "GPL-3.0-only",
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
"@comfyorg/comfyui-electron-types": "^0.4.7",
"@comfyorg/comfyui-electron-types": "^0.4.10",
"@comfyorg/litegraph": "^0.8.60",
"@primevue/themes": "^4.0.5",
"@sentry/vue": "^8.48.0",
@@ -1936,10 +1936,9 @@
"dev": true
},
"node_modules/@comfyorg/comfyui-electron-types": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/@comfyorg/comfyui-electron-types/-/comfyui-electron-types-0.4.7.tgz",
"integrity": "sha512-APC3C4VZOo9W6h0xiAGxnsU9iNp3T8rN9w/5KmOCI0GUoKtKg5U2OaicTmnMwcDSQe5Jxflmej53GyJ1nH9oRw==",
"license": "GPL-3.0-only"
"version": "0.4.10",
"resolved": "https://registry.npmjs.org/@comfyorg/comfyui-electron-types/-/comfyui-electron-types-0.4.10.tgz",
"integrity": "sha512-UWBgyuWeV7vussYZVUYhCe0jj+XbIq2nglrCUy6IgFgXp9pbE8Ktg5D36WxE0RWj6SvVXErlCL9wWnMktaRbCA=="
},
"node_modules/@comfyorg/litegraph": {
"version": "0.8.60",

View File

@@ -1,7 +1,7 @@
{
"name": "@comfyorg/comfyui-frontend",
"private": true,
"version": "1.7.10",
"version": "1.7.14",
"type": "module",
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
"homepage": "https://comfy.org",
@@ -83,7 +83,7 @@
},
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
"@comfyorg/comfyui-electron-types": "^0.4.7",
"@comfyorg/comfyui-electron-types": "^0.4.10",
"@comfyorg/litegraph": "^0.8.60",
"@primevue/themes": "^4.0.5",
"@sentry/vue": "^8.48.0",

View File

@@ -0,0 +1 @@
{"last_node_id":16,"last_link_id":10,"nodes":[{"id":16,"type":"MarkdownNote","pos":[630,60],"size":[930,945],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[],"title":"Changelog","properties":{},"widgets_values":["# v0.3.11 (Pre-release)\n\n## What's Changed\n\n* Nvidia Cosmos 7B and 14B: text to video and image to video diffusion model support.\n <iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/watch?v=-OCwHBur0FM\" frameborder=\"0\" allowfullscreen></iframe>\n\n\n* New sampler: res_multistep\n* ckpt/pt/etc.. files are now always loaded safely on pytorch 2.4 and above.\n* Fix some cases of ancestral samplers not being deterministic.\n* Support ascend npu by @ji-huazhong [#5436](https://github.com/comfyanonymous/ComfyUI/pull/5436)\n* Add option to log non-error output to stdout by @webfiltered [#6243](https://github.com/comfyanonymous/ComfyUI/pull/6243)\n* serve workflow templates from custom_nodes by @bezo97 [#6193](https://github.com/comfyanonymous/ComfyUI/pull/6193)\n* Remove duplicate calls to INPUT_TYPES by @catboxanon [#6249](https://github.com/comfyanonymous/ComfyUI/pull/6249)\n* Fix Hook Keyframe 'guarantee_steps' behavior and add 'sigmas' by @Kosinkadink [#6273](https://github.com/comfyanonymous/ComfyUI/pull/6273)\n* Add kl_optimal scheduler by @blepping [#6206](https://github.com/comfyanonymous/ComfyUI/pull/6206)\n* (fix): \"verbose\" argument by @bigcat88 [#6289](https://github.com/comfyanonymous/ComfyUI/pull/6289)\n* Fix custom node type-hinting examples by @webfiltered [#6281](https://github.com/comfyanonymous/ComfyUI/pull/6281)\n* Add missing model_options param to finalize_default_conds call by @Kosinkadink [#6296](https://github.com/comfyanonymous/ComfyUI/pull/6296)\n* Fix unknown scheduler error handling in calculate_sigmas function by @blepping [#6280](https://github.com/comfyanonymous/ComfyUI/pull/6280)\n* Fix temporal tiling for Tiled VAE decoder, remove redundant tiles. by @kvochko [#6306](https://github.com/comfyanonymous/ComfyUI/pull/6306)\n* Update web content to release v1.6.14 by @huchenlei [#6312](https://github.com/comfyanonymous/ComfyUI/pull/6312)\n* add fov and mask for load 3d node by @jtydhr88 [#6308](https://github.com/comfyanonymous/ComfyUI/pull/6308)\n* Update web content to release v1.6.15 by @huchenlei [#6324](https://github.com/comfyanonymous/ComfyUI/pull/6324)\n* Update web content to release v1.6.16 by @huchenlei [#6335](https://github.com/comfyanonymous/ComfyUI/pull/6335)\n* Update web content to release v1.6.17 by @huchenlei [#6337](https://github.com/comfyanonymous/ComfyUI/pull/6337)\n* Add update-frontend github action by @huchenlei [#6336](https://github.com/comfyanonymous/ComfyUI/pull/6336)\n* Update CODEOWNERS by @yoland68 [#6338](https://github.com/comfyanonymous/ComfyUI/pull/6338)\n* In inner_sample, change \"sigmas\" to \"sample_sigmas\" by @Kosinkadink [#6360](https://github.com/comfyanonymous/ComfyUI/pull/6360)\n* Frontend Update: v1.6.18 by @huchenlei [#6368](https://github.com/comfyanonymous/ComfyUI/pull/6368)\n* Document get_attr and get_model_object by @huchenlei [#6357](https://github.com/comfyanonymous/ComfyUI/pull/6357)\n* fixed: robust loading \\`comfy.settings.json\\` by @ltdrdata [#6383](https://github.com/comfyanonymous/ComfyUI/pull/6383)\n* Add pyproject.toml by @huchenlei [#6386](https://github.com/comfyanonymous/ComfyUI/pull/6386)\n* Hooks Part 2 - TransformerOptionsHook and AdditionalModelsHook by @Kosinkadink [#6377](https://github.com/comfyanonymous/ComfyUI/pull/6377)\n* Merge ruff.toml into pyproject.toml by @huchenlei [#6431](https://github.com/comfyanonymous/ComfyUI/pull/6431)\n* (fix): load_extra_path_config: relative path not converted to a full path by @bigcat88 [#6395](https://github.com/comfyanonymous/ComfyUI/pull/6395)\n* Rewrite res_multistep sampler and implement res_multistep_cfg_pp sampler by @pamparamm [#6462](https://github.com/comfyanonymous/ComfyUI/pull/6462)\n* Add SetFirstSigma node by @catboxanon [#6459](https://github.com/comfyanonymous/ComfyUI/pull/6459)\n\n**Full Changelog**: [\\`v0.3.10...v0.3.11\\`](https://github.com/comfyanonymous/ComfyUI/compare/v0.3.10...v0.3.11)\n\n----\n\n> To disable showing changelog on new releases, go the the Changelog section in settings\n"],"color":"#222","bgcolor":"#000"}],"links":[],"groups":[],"config":{},"extra":{"ds":{"scale":1.503752370924104,"offset":[-489.9627968865069,158.59081129748483]}},"version":0.4}

View File

@@ -20,11 +20,16 @@ const terminalCreated = (
let offData: IDisposable
let offOutput: () => void
useAutoSize(root, true, true, () => {
// If we aren't visible, don't resize
if (!terminal.element?.offsetParent) return
useAutoSize({
root,
autoRows: true,
autoCols: true,
onResize: () => {
// If we aren't visible, don't resize
if (!terminal.element?.offsetParent) return
terminalApi.resize(terminal.cols, terminal.rows)
terminalApi.resize(terminal.cols, terminal.rows)
}
})
onMounted(async () => {

View File

@@ -29,7 +29,12 @@ const terminalCreated = (
{ terminal, useAutoSize }: ReturnType<typeof useTerminal>,
root: Ref<HTMLElement>
) => {
useAutoSize(root, true, false)
// `autoCols` is false because we don't want the progress bar in the terminal
// to render incorrectly as the progress bar is rendered based on the
// server's terminal size.
// Apply a min cols of 80 for colab environments
// See https://github.com/comfyanonymous/ComfyUI/issues/6396
useAutoSize({ root, autoRows: true, autoCols: false, minCols: 80 })
const update = (entries: Array<LogEntry>, size?: TerminalSize) => {
if (size) {

View File

@@ -13,11 +13,16 @@
import { onBeforeUnmount, onMounted } from 'vue'
import { useSettingStore } from '@/stores/settingStore'
import { useWorkflowStore } from '@/stores/workflowStore'
const settingStore = useSettingStore()
const workflowStore = useWorkflowStore()
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
if (settingStore.get('Comfy.Window.UnloadConfirmation')) {
if (
settingStore.get('Comfy.Window.UnloadConfirmation') &&
workflowStore.modifiedWorkflows.length > 0
) {
event.preventDefault()
return true
}

View File

@@ -30,6 +30,7 @@
v-if="sendReportOpen"
error-type="graphExecutionError"
:extra-fields="[stackTraceField]"
:tags="{ exceptionMessage: props.error.exception_message }"
/>
<div class="action-container">
<FindIssueButton

View File

@@ -75,9 +75,12 @@ const props = defineProps<{
errorType: string
defaultFields?: DefaultField[]
extraFields?: ReportField[]
tags?: Record<string, string>
}>()
const { defaultFields = ['Workflow', 'Logs', 'SystemStats', 'Settings'] } =
props
const {
defaultFields = ['Workflow', 'Logs', 'SystemStats', 'Settings'],
tags = {}
} = props
const { t } = useI18n()
const toast = useToast()
@@ -168,7 +171,8 @@ const createCaptureContext = async (): Promise<CaptureContext> => {
user: getUserInfo(),
level: 'error',
tags: {
errorType: props.errorType
errorType: props.errorType,
...tags
},
extra: {
...createFeedback(),

View File

@@ -43,6 +43,7 @@ import {
} from '@comfyorg/litegraph'
import { computed, onMounted, ref, watch, watchEffect } from 'vue'
import changelog from '@/assets/changelog.json'
import LiteGraphCanvasSplitterOverlay from '@/components/LiteGraphCanvasSplitterOverlay.vue'
import BottomPanel from '@/components/bottomPanel/BottomPanel.vue'
import GraphCanvasMenu from '@/components/graph/GraphCanvasMenu.vue'
@@ -56,6 +57,7 @@ import { CORE_SETTINGS } from '@/constants/coreSettings'
import { usePragmaticDroppable } from '@/hooks/dndHooks'
import { api } from '@/scripts/api'
import { app as comfyApp } from '@/scripts/app'
import { app } from '@/scripts/app'
import { ChangeTracker } from '@/scripts/changeTracker'
import { getStorageValue, setStorageValue } from '@/scripts/utils'
import { IS_CONTROL_WIDGET, updateControlWidgetLabel } from '@/scripts/widgets'
@@ -75,6 +77,7 @@ import { useWorkflowStore } from '@/stores/workflowStore'
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
import { useWorkspaceStore } from '@/stores/workspaceStore'
import type { RenderedTreeExplorerNode } from '@/types/treeExplorerTypes'
import { getComfyVersion, isVersionLessThan } from '@/utils/envUtil'
const emit = defineEmits(['ready'])
const canvasRef = ref<HTMLCanvasElement | null>(null)
@@ -278,6 +281,35 @@ const persistCurrentWorkflow = () => {
}
}
const stopWatchChangeLog = watch(
() => workflowStore.activeWorkflow,
async () => {
if (!comfyAppReady.value) return
if (!changelog || settingStore.get('Comfy.ShowChangeLog') === false) {
stopWatchChangeLog()
return
}
const workflow = workflowStore.activeWorkflow
const activeState = workflow?.activeState
const comfyVersion = getComfyVersion() // TODO: initialize/fetch somewhere else
if (!workflow || !activeState || !comfyVersion) return
// Just checking if workflow temporary is not enough bc of Duplicate feature
const isBlank = !workflow.isPersisted && activeState.nodes?.length === 0
if (!isBlank) return
const lastShown = settingStore.get('Comfy.LastChangelogVersion')
const isSeen = lastShown && !isVersionLessThan(lastShown, comfyVersion)
if (!isSeen) {
app.loadGraphData(changelog)
settingStore.set('Comfy.LastChangelogVersion', comfyVersion)
}
stopWatchChangeLog()
}
)
watchEffect(() => {
if (workflowStore.activeWorkflow) {
const workflow = workflowStore.activeWorkflow

View File

@@ -72,14 +72,6 @@
'install.settings.dataCollectionDialog.collect.userJourneyEvents'
)
}}
<span
class="pi pi-info-circle text-neutral-400"
v-tooltip="
$t(
'install.settings.dataCollectionDialog.collect.userJourneyTooltip'
)
"
/>
</li>
</ul>
@@ -116,6 +108,16 @@
}}
</li>
</ul>
<div class="mt-4">
<a
href="https://comfy.org/privacy"
target="_blank"
class="text-blue-400 hover:text-blue-300 underline"
>
{{ $t('install.settings.dataCollectionDialog.viewFullPolicy') }}
</a>
</div>
</div>
</Dialog>
</div>

View File

@@ -159,8 +159,8 @@ const pickGpu = (value: typeof selected.value) => {
}
</script>
<style lang="postcss">
:root {
<style scoped>
.p-tag {
--p-tag-gap: 0.5rem;
}

View File

@@ -1,6 +1,6 @@
<template>
<teleport :to="teleportTarget">
<nav :class="'side-tool-bar-container' + (isSmall ? ' small-sidebar' : '')">
<nav class="side-tool-bar-container" :class="{ 'small-sidebar': isSmall }">
<SidebarIcon
v-for="tab in tabs"
:key="tab.id"
@@ -69,17 +69,6 @@ const getTabTooltipSuffix = (tab: SidebarTabExtension) => {
}
</script>
<style>
:root {
--sidebar-width: 4rem;
--sidebar-icon-size: 1.5rem;
}
:root .small-sidebar {
--sidebar-width: 2.5rem;
--sidebar-icon-size: 1rem;
}
</style>
<style scoped>
.side-tool-bar-container {
display: flex;
@@ -94,6 +83,14 @@ const getTabTooltipSuffix = (tab: SidebarTabExtension) => {
background-color: var(--comfy-menu-secondary-bg);
color: var(--fg-color);
box-shadow: var(--bar-shadow);
--sidebar-width: 4rem;
--sidebar-icon-size: 1.5rem;
}
.side-tool-bar-container.small-sidebar {
--sidebar-width: 2.5rem;
--sidebar-icon-size: 1rem;
}
.side-tool-bar-end {

View File

@@ -99,7 +99,7 @@ import type { MenuItem } from 'primevue/menuitem'
import ProgressSpinner from 'primevue/progressspinner'
import { useConfirm } from 'primevue/useconfirm'
import { useToast } from 'primevue/usetoast'
import { computed, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'
import { computed, ref, shallowRef, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
@@ -194,10 +194,6 @@ const confirmRemoveAll = (event: Event) => {
})
}
const onStatus = async () => {
await queueStore.update()
}
const menu = ref(null)
const menuTargetTask = ref<TaskItemImpl | null>(null)
const menuTargetNode = ref<ComfyNode | null>(null)
@@ -267,13 +263,4 @@ watch(allTasks, () => {
const newIndex = galleryActiveIndex.value + lengthChange
galleryActiveIndex.value = Math.max(0, newIndex)
})
onMounted(() => {
api.addEventListener('status', onStatus)
queueStore.update()
})
onUnmounted(() => {
api.removeEventListener('status', onStatus)
})
</script>

View File

@@ -230,7 +230,8 @@ export const CORE_SETTINGS: SettingParams[] = [
id: 'Comfy.Window.UnloadConfirmation',
name: 'Show confirmation when closing window',
type: 'boolean',
defaultValue: false
defaultValue: true,
versionModified: '1.7.12'
},
{
id: 'Comfy.TreeExplorer.ItemPadding',
@@ -707,5 +708,20 @@ export const CORE_SETTINGS: SettingParams[] = [
defaultValue: 'after',
options: ['before', 'after'],
versionModified: '1.6.10'
},
{
id: 'Comfy.ShowChangeLog',
category: ['Comfy', 'Changelog'],
name: 'Show changelog on new release',
type: 'boolean',
defaultValue: true,
versionAdded: '1.7.15'
},
{
id: 'Comfy.LastChangelogVersion',
name: 'Last shown changelog version',
type: 'hidden',
defaultValue: '0.0.0',
versionAdded: '1.7.15'
}
]

View File

@@ -181,6 +181,16 @@ import { electronAPI as getElectronAPI, isElectron } from '@/utils/envUtil'
}
],
keybindings: [
{
commandId: 'Workspace.CloseWorkflow',
combo: {
key: 'w',
ctrl: true
}
}
],
aboutPageBadges: [
{
label: 'ComfyUI_desktop v' + desktopAppVersion,

View File

@@ -1,7 +1,10 @@
import { debounce } from 'lodash'
import { api } from '../../scripts/api'
import { app } from '../../scripts/app'
import { ComfyApp } from '../../scripts/app'
import { $el, ComfyDialog } from '../../scripts/ui'
import { getStorageValue, setStorageValue } from '../../scripts/utils'
import { ClipspaceDialog } from './clipspace'
import { MaskEditorDialogOld } from './maskEditorOld'
@@ -262,15 +265,15 @@ var styles = `
}
#maskEditor_toolPanel {
height: 100%;
width: var(--sidebar-width);
width: 4rem;
z-index: 8888;
background: var(--comfy-menu-bg);
display: flex;
flex-direction: column;
}
.maskEditor_toolPanelContainer {
width: var(--sidebar-width);
height: var(--sidebar-width);
width: 4rem;
height: 4rem;
display: flex;
justify-content: center;
align-items: center;
@@ -331,7 +334,7 @@ var styles = `
margin-bottom: 5px;
}
#maskEditor_pointerZone {
width: calc(100% - var(--sidebar-width) - 220px);
width: calc(100% - 4rem - 220px);
height: 100%;
}
#maskEditor_uiContainer {
@@ -703,8 +706,8 @@ var styles = `
}
.maskEditor_toolPanelZoomIndicator {
width: var(--sidebar-width);
height: var(--sidebar-width);
width: 4rem;
height: 4rem;
display: flex;
flex-direction: column;
justify-content: center;
@@ -776,10 +779,37 @@ interface Offset {
}
export interface Brush {
type: BrushShape
size: number
opacity: number
hardness: number
type: BrushShape
smoothingPrecision: number
}
const saveBrushToCache = debounce(function (key: string, brush: Brush): void {
try {
const brushString = JSON.stringify(brush)
setStorageValue(key, brushString)
} catch (error) {
console.error('Failed to save brush to cache:', error)
}
}, 300)
function loadBrushFromCache(key: string): Brush | null {
try {
const brushString = getStorageValue(key)
if (brushString) {
const brush = JSON.parse(brushString) as Brush
console.log('Loaded brush from cache:', brush)
return brush
} else {
console.log('No brush found in cache.')
return null
}
} catch (error) {
console.error('Failed to load brush from cache:', error)
return null
}
}
type Callback = (data?: any) => void
@@ -1952,12 +1982,19 @@ class BrushTool {
'Comfy.MaskEditor.BrushAdjustmentSpeed'
)
this.brushSettings = {
size: 10,
opacity: 100,
hardness: 1,
type: BrushShape.Arc
const cachedBrushSettings = loadBrushFromCache('maskeditor_brush_settings')
if (cachedBrushSettings) {
this.brushSettings = cachedBrushSettings
} else {
this.brushSettings = {
type: BrushShape.Arc,
size: 10,
opacity: 0.7,
hardness: 1,
smoothingPrecision: 10
}
}
this.maskBlendMode = MaskBlendMode.Black
}
@@ -2016,6 +2053,10 @@ class BrushTool {
'brushType',
async () => this.brushSettings.type
)
this.messageBroker.createPullTopic(
'brushSmoothingPrecision',
async () => this.brushSettings.smoothingPrecision
)
this.messageBroker.createPullTopic(
'maskBlendMode',
async () => this.maskBlendMode
@@ -2143,7 +2184,7 @@ class BrushTool {
}
const distanceBetweenPoints =
(this.brushSettings.size / this.smoothingPrecision) * 6
(this.brushSettings.size / this.brushSettings.smoothingPrecision) * 6
const stepNr = Math.ceil(totalLength / distanceBetweenPoints)
let interpolatedPoints = points
@@ -2190,7 +2231,7 @@ class BrushTool {
const brush_size = await this.messageBroker.pull('brushSize')
const distance = Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2)
const steps = Math.ceil(
distance / ((brush_size / this.smoothingPrecision) * 4)
distance / ((brush_size / this.brushSettings.smoothingPrecision) * 4)
) // Adjust for smoother lines
const interpolatedOpacity =
1 / (1 + Math.exp(-6 * (this.brushSettings.opacity - 0.5))) -
@@ -2545,23 +2586,27 @@ class BrushTool {
private setBrushSize(size: number) {
this.brushSettings.size = size
saveBrushToCache('maskeditor_brush_settings', this.brushSettings)
}
private setBrushOpacity(opacity: number) {
this.brushSettings.opacity = opacity
saveBrushToCache('maskeditor_brush_settings', this.brushSettings)
}
private setBrushHardness(hardness: number) {
this.brushSettings.hardness = hardness
saveBrushToCache('maskeditor_brush_settings', this.brushSettings)
}
private setBrushType(type: BrushShape) {
this.brushSettings.type = type
saveBrushToCache('maskeditor_brush_settings', this.brushSettings)
}
private setBrushSmoothingPrecision(precision: number) {
//console.log('precision', precision)
this.smoothingPrecision = precision
this.brushSettings.smoothingPrecision = precision
saveBrushToCache('maskeditor_brush_settings', this.brushSettings)
}
}
@@ -2825,7 +2870,6 @@ class UIManager {
const circle_shape = document.createElement('div')
circle_shape.id = 'maskEditor_sidePanelBrushShapeCircle'
circle_shape.classList.add(shapeColor)
circle_shape.style.background = 'var(--p-button-text-primary-color)'
circle_shape.addEventListener('click', () => {
this.messageBroker.publish('setBrushShape', BrushShape.Arc)
this.setBrushBorderRadius()
@@ -2836,7 +2880,6 @@ class UIManager {
const square_shape = document.createElement('div')
square_shape.id = 'maskEditor_sidePanelBrushShapeSquare'
square_shape.classList.add(shapeColor)
square_shape.style.background = ''
square_shape.addEventListener('click', () => {
this.messageBroker.publish('setBrushShape', BrushShape.Rect)
this.setBrushBorderRadius()
@@ -2844,6 +2887,16 @@ class UIManager {
circle_shape.style.background = ''
})
if (
(await this.messageBroker.pull('brushSettings')).type === BrushShape.Arc
) {
circle_shape.style.background = 'var(--p-button-text-primary-color)'
square_shape.style.background = ''
} else {
circle_shape.style.background = ''
square_shape.style.background = 'var(--p-button-text-primary-color)'
}
brush_shape_container.appendChild(circle_shape)
brush_shape_container.appendChild(square_shape)
@@ -2855,7 +2908,7 @@ class UIManager {
1,
100,
1,
10,
(await this.messageBroker.pull('brushSettings')).size,
(event, value) => {
this.messageBroker.publish('setBrushSize', parseInt(value))
this.updateBrushPreview()
@@ -2868,7 +2921,7 @@ class UIManager {
0,
1,
0.01,
0.7,
(await this.messageBroker.pull('brushSettings')).opacity,
(event, value) => {
this.messageBroker.publish('setBrushOpacity', parseFloat(value))
this.updateBrushPreview()
@@ -2881,7 +2934,7 @@ class UIManager {
0,
1,
0.01,
1,
(await this.messageBroker.pull('brushSettings')).hardness,
(event, value) => {
this.messageBroker.publish('setBrushHardness', parseFloat(value))
this.updateBrushPreview()
@@ -2894,7 +2947,7 @@ class UIManager {
1,
100,
1,
10,
(await this.messageBroker.pull('brushSettings')).smoothingPrecision,
(event, value) => {
this.messageBroker.publish(
'setBrushSmoothingPrecision',
@@ -2903,7 +2956,31 @@ class UIManager {
}
)
const resetBrushSettingsButton = document.createElement('button')
resetBrushSettingsButton.id = 'resetBrushSettingsButton'
resetBrushSettingsButton.innerText = 'Reset to Default'
resetBrushSettingsButton.addEventListener('click', () => {
this.messageBroker.publish('setBrushShape', BrushShape.Arc)
this.messageBroker.publish('setBrushSize', 10)
this.messageBroker.publish('setBrushOpacity', 0.7)
this.messageBroker.publish('setBrushHardness', 1)
this.messageBroker.publish('setBrushSmoothingPrecision', 10)
circle_shape.style.background = 'var(--p-button-text-primary-color)'
square_shape.style.background = ''
thicknesSliderObj.slider.value = '10'
opacitySliderObj.slider.value = '0.7'
hardnessSliderObj.slider.value = '1'
brushSmoothingPrecisionSliderObj.slider.value = '10'
this.setBrushBorderRadius()
this.updateBrushPreview()
})
brush_settings_container.appendChild(brush_settings_title)
brush_settings_container.appendChild(resetBrushSettingsButton)
brush_settings_container.appendChild(brush_shape_outer_container)
brush_settings_container.appendChild(thicknesSliderObj.container)
brush_settings_container.appendChild(opacitySliderObj.container)

View File

@@ -36,12 +36,21 @@ export function useTerminal(element: Ref<HTMLElement>) {
return {
terminal,
useAutoSize(
root: Ref<HTMLElement>,
autoRows: boolean = true,
autoCols: boolean = true,
useAutoSize({
root,
autoRows = true,
autoCols = true,
minCols = Number.NEGATIVE_INFINITY,
minRows = Number.NEGATIVE_INFINITY,
onResize
}: {
root: Ref<HTMLElement>
autoRows?: boolean
autoCols?: boolean
minCols?: number
minRows?: number
onResize?: () => void
) {
}) {
const ensureValidRows = (rows: number | undefined) => {
if (rows == null || isNaN(rows)) {
return root.value?.clientHeight / 20
@@ -61,8 +70,14 @@ export function useTerminal(element: Ref<HTMLElement>) {
const dims = fitAddon.proposeDimensions()
// Sometimes propose returns NaN, so we may need to estimate.
terminal.resize(
autoCols ? ensureValidCols(dims?.cols) : terminal.cols,
autoRows ? ensureValidRows(dims?.rows) : terminal.rows
Math.max(
autoCols ? ensureValidCols(dims?.cols) : terminal.cols,
minCols
),
Math.max(
autoRows ? ensureValidRows(dims?.rows) : terminal.rows,
minRows
)
)
onResize?.()
}

View File

@@ -70,7 +70,9 @@
"keybinding": "Keybinding",
"upload": "Upload",
"export": "Export",
"workflow": "Workflow"
"workflow": "Workflow",
"success": "Success",
"ok": "OK"
},
"issueReport": {
"submitErrorReport": "Submit Error Report (Optional)",
@@ -151,7 +153,7 @@
"appDataLocationTooltip": "ComfyUI's app data directory. Stores:\n- Logs\n- Server configs",
"appPathLocationTooltip": "ComfyUI's app asset directory. Stores the ComfyUI code and assets",
"migrateFromExistingInstallation": "Migrate from Existing Installation",
"migrationSourcePathDescription": "If you have an existing ComfyUI installation, we can copy/link your existing user files and models to the new installation.",
"migrationSourcePathDescription": "If you have an existing ComfyUI installation, we can copy/link your existing user files and models to the new installation. Your existing ComfyUI installation will not be affected.",
"selectItemsToMigrate": "Select Items to Migrate",
"migrationOptional": "Migration is optional. If you don't have an existing installation, you can skip this step.",
"desktopAppSettings": "Desktop App Settings",
@@ -182,6 +184,8 @@
"settings": {
"autoUpdate": "Automatic Updates",
"allowMetrics": "Usage Metrics",
"errorUpdatingConsent": "Error Updating Consent",
"errorUpdatingConsentDetail": "Failed to update metrics consent settings",
"autoUpdateDescription": "Automatically download updates when they become available. You will be notified before updates are installed.",
"allowMetricsDescription": "Help improve ComfyUI by sending anonymous usage metrics. No personal information or workflow content will be collected.",
"learnMoreAboutData": "Learn more about data collection",
@@ -192,19 +196,25 @@
"collect": {
"errorReports": "Error message and stack trace",
"systemInfo": "Hardware, OS type, and app version",
"userJourneyEvents": "User journey events",
"userJourneyTooltip": "User journey events are used to track the user's journey through the app installation process. The event collection ends on the first successful ComfyUI workflow run."
"userJourneyEvents": "User journey events"
},
"doNotCollect": {
"personalInformation": "Personal information",
"fileSystemInformation": "File system information",
"workflowContents": "Workflow contents",
"customNodeConfigurations": "Custom node configurations"
}
},
"viewFullPolicy": "View full policy"
}
},
"customNodes": "Custom Nodes",
"customNodesDescription": "Reinstall custom nodes from existing ComfyUI installations."
"customNodesDescription": "Reinstall custom nodes from existing ComfyUI installations.",
"helpImprove": "Please help improve ComfyUI",
"moreInfo": "For more info, please read our",
"privacyPolicy": "privacy policy",
"metricsEnabled": "Metrics Enabled",
"metricsDisabled": "Metrics Disabled",
"updateConsent": "You previously opted in to reporting crashes. We are now tracking event-based metrics to help identify bugs and improve the app. No personal identifiable information is collected."
},
"serverStart": {
"reinstall": "Reinstall",
@@ -600,6 +610,7 @@
"combine": "combine",
"cond single": "cond single",
"controlnet": "controlnet",
"inpaint": "inpaint",
"scheduling": "scheduling",
"create": "create",
"mask": "mask",
@@ -619,7 +630,6 @@
"batch": "batch",
"video_models": "video_models",
"upscaling": "upscaling",
"inpaint": "inpaint",
"instructpix2pix": "instructpix2pix",
"compositing": "compositing",
"samplers": "samplers",

View File

@@ -177,7 +177,7 @@
},
"CLIPLoader": {
"display_name": "Load CLIP",
"description": "[Recipes]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5",
"description": "[Recipes]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5\ncosmos: old t5 xxl",
"inputs": {
"clip_name": {
"name": "clip_name"
@@ -862,6 +862,32 @@
}
}
},
"CosmosImageToVideoLatent": {
"display_name": "CosmosImageToVideoLatent",
"inputs": {
"vae": {
"name": "vae"
},
"width": {
"name": "width"
},
"height": {
"name": "height"
},
"length": {
"name": "length"
},
"batch_size": {
"name": "batch_size"
},
"start_image": {
"name": "start_image"
},
"end_image": {
"name": "end_image"
}
}
},
"CreateHookKeyframe": {
"display_name": "Create Hook Keyframe",
"inputs": {
@@ -1230,6 +1256,23 @@
}
}
},
"EmptyCosmosLatentVideo": {
"display_name": "EmptyCosmosLatentVideo",
"inputs": {
"width": {
"name": "width"
},
"height": {
"name": "height"
},
"length": {
"name": "length"
},
"batch_size": {
"name": "batch_size"
}
}
},
"EmptyHunyuanLatentVideo": {
"display_name": "EmptyHunyuanLatentVideo",
"inputs": {
@@ -4745,6 +4788,17 @@
}
}
},
"SetFirstSigma": {
"display_name": "SetFirstSigma",
"inputs": {
"sigmas": {
"name": "sigmas"
},
"sigma": {
"name": "sigma"
}
}
},
"SetHookKeyframes": {
"display_name": "Set Hook Keyframes",
"inputs": {

View File

@@ -107,6 +107,7 @@
"noTasksFound": "Aucune tâche trouvée",
"noTasksFoundMessage": "Il n'y a pas de tâches dans la file d'attente.",
"noWorkflowsFound": "Aucun flux de travail trouvé.",
"ok": "OK",
"openNewIssue": "Ouvrir un nouveau problème",
"overwrite": "Écraser",
"reconnected": "Reconnecté",
@@ -129,6 +130,7 @@
"searchWorkflows": "Rechercher des flux de travail",
"settings": "Paramètres",
"showReport": "Afficher le rapport",
"success": "Succès",
"systemInfo": "Informations système",
"terminal": "Terminal",
"upload": "Téléverser",
@@ -186,6 +188,7 @@
"selectGpu": "Sélectionnez le GPU",
"selectGpuDescription": "Sélectionnez le type de GPU que vous avez"
},
"helpImprove": "Veuillez aider à améliorer ComfyUI",
"installLocation": "Emplacement d'installation",
"installLocationDescription": "Sélectionnez le répertoire pour les données utilisateur de ComfyUI. Un environnement python sera installé à l'emplacement sélectionné. Veuillez vous assurer que le disque sélectionné a suffisamment d'espace (~15GB) restant.",
"installLocationTooltip": "Répertoire des données utilisateur de ComfyUI. Stocke :\n- Environnement Python\n- Modèles\n- Nœuds personnalisés\n",
@@ -197,13 +200,17 @@
"title": "Configuration manuelle",
"virtualEnvironmentPath": "Chemin de l'environnement virtuel"
},
"metricsDisabled": "Métriques désactivées",
"metricsEnabled": "Métriques activées",
"migrateFromExistingInstallation": "Migrer à partir d'une installation existante",
"migration": "Migration",
"migrationOptional": "La migration est facultative. Si vous n'avez pas d'installation existante, vous pouvez sauter cette étape.",
"migrationSourcePathDescription": "Si vous avez une installation existante de ComfyUI, nous pouvons copier/lier vos fichiers utilisateur et modèles existants à la nouvelle installation.",
"migrationSourcePathDescription": "Si vous avez une installation existante de ComfyUI, nous pouvons copier/lier vos fichiers utilisateur et modèles existants à la nouvelle installation. Votre installation existante de ComfyUI ne sera pas affectée.",
"moreInfo": "Pour plus d'informations, veuillez lire notre",
"parentMissing": "Le chemin n'existe pas - créez d'abord le répertoire contenant",
"pathExists": "Le répertoire existe déjà - veuillez vous assurer que vous avez sauvegardé toutes les données",
"pathValidationFailed": "Échec de la validation du chemin",
"privacyPolicy": "politique de confidentialité",
"selectItemsToMigrate": "Sélectionnez les éléments à migrer",
"settings": {
"allowMetrics": "Métriques d'utilisation",
@@ -214,8 +221,7 @@
"collect": {
"errorReports": "Message d'erreur et trace de la pile",
"systemInfo": "Matériel, type de système d'exploitation et version de l'application",
"userJourneyEvents": "Événements du parcours utilisateur",
"userJourneyTooltip": "Les événements du parcours utilisateur sont utilisés pour suivre le parcours de l'utilisateur lors du processus d'installation de l'application. La collecte d'événements se termine lors de la première exécution réussie du flux de travail ComfyUI."
"userJourneyEvents": "Événements du parcours utilisateur"
},
"doNotCollect": {
"customNodeConfigurations": "Configurations de nœud personnalisées",
@@ -224,13 +230,17 @@
"workflowContents": "Contenus du flux de travail"
},
"title": "À propos de la collecte de données",
"viewFullPolicy": "Voir la politique complète",
"whatWeCollect": "Ce que nous collectons :",
"whatWeDoNotCollect": "Ce que nous ne collectons pas :"
},
"errorUpdatingConsent": "Erreur de mise à jour du consentement",
"errorUpdatingConsentDetail": "Échec de la mise à jour des paramètres de consentement aux métriques",
"learnMoreAboutData": "En savoir plus sur la collecte de données"
},
"systemLocations": "Emplacements système",
"unhandledError": "Erreur inconnue"
"unhandledError": "Erreur inconnue",
"updateConsent": "Vous avez précédemment accepté de signaler les plantages. Nous suivons maintenant des métriques basées sur les événements pour aider à identifier les bugs et améliorer l'application. Aucune information personnelle identifiable n'est collectée."
},
"issueReport": {
"contactFollowUp": "Contactez-moi pour un suivi",

View File

@@ -113,7 +113,7 @@
}
},
"CLIPLoader": {
"description": "[Recettes]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5",
"description": "[Recettes]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5\ncosmos: old t5 xxl",
"display_name": "Charger CLIP",
"inputs": {
"clip_name": {
@@ -862,6 +862,32 @@
}
}
},
"CosmosImageToVideoLatent": {
"display_name": "CosmosImageVersVidéoLatent",
"inputs": {
"batch_size": {
"name": "taille_du_lot"
},
"end_image": {
"name": "image_de_fin"
},
"height": {
"name": "hauteur"
},
"length": {
"name": "longueur"
},
"start_image": {
"name": "image_de_départ"
},
"vae": {
"name": "vae"
},
"width": {
"name": "largeur"
}
}
},
"CreateHookKeyframe": {
"display_name": "Créer une image clé de crochet",
"inputs": {
@@ -1230,6 +1256,23 @@
}
}
},
"EmptyCosmosLatentVideo": {
"display_name": "VidéoLatenteCosmosVide",
"inputs": {
"batch_size": {
"name": "taille_du_lot"
},
"height": {
"name": "hauteur"
},
"length": {
"name": "longueur"
},
"width": {
"name": "largeur"
}
}
},
"EmptyHunyuanLatentVideo": {
"display_name": "EmptyHunyuanLatentVideo",
"inputs": {
@@ -4825,6 +4868,17 @@
}
}
},
"SetFirstSigma": {
"display_name": "DéfinirPremierSigma",
"inputs": {
"sigma": {
"name": "sigma"
},
"sigmas": {
"name": "sigmas"
}
}
},
"SetHookKeyframes": {
"display_name": "Définir les Images Clés de Crochet",
"inputs": {

View File

@@ -107,6 +107,7 @@
"noTasksFound": "タスクが見つかりません",
"noTasksFoundMessage": "キューにタスクがありません。",
"noWorkflowsFound": "ワークフローが見つかりません。",
"ok": "OK",
"openNewIssue": "新しい問題を開く",
"overwrite": "上書き",
"reconnected": "再接続されました",
@@ -129,6 +130,7 @@
"searchWorkflows": "ワークフローを検索",
"settings": "設定",
"showReport": "レポートを表示",
"success": "成功",
"systemInfo": "システム情報",
"terminal": "ターミナル",
"upload": "アップロード",
@@ -186,6 +188,7 @@
"selectGpu": "GPUを選択",
"selectGpuDescription": "所有しているGPUのタイプを選択してください"
},
"helpImprove": "ComfyUIの改善にご協力ください",
"installLocation": "インストール先",
"installLocationDescription": "ComfyUIのユーザーデータを保存するディレクトリを選択してください。Python環境が選択した場所にインストールされます。選択したディスクに約15GBの空き容量が必要です。",
"installLocationTooltip": "ComfyUIのユーザーデータディレクトリ。保存内容:\n- Python環境\n- モデル\n- カスタムノード\n",
@@ -197,13 +200,17 @@
"title": "マニュアル設定",
"virtualEnvironmentPath": "仮想環境のパス"
},
"metricsDisabled": "メトリクス無効",
"metricsEnabled": "メトリクス有効",
"migrateFromExistingInstallation": "既存のインストールから移行",
"migration": "移行",
"migrationOptional": "移行は任意です。既存のインストールがない場合、このステップをスキップできます。",
"migrationSourcePathDescription": "既存のComfyUIインストールがある場合、既存のユーザーファイルモデルを新しいインストールにコピー/リンクできます。",
"migrationSourcePathDescription": "既存のComfyUIインストールがある場合、既存のユーザーファイルモデルを新しいインストールにコピー/リンクすることができます。既存のComfyUIインストールは影響を受けません。",
"moreInfo": "詳細は、私たちの",
"parentMissing": "パスが存在しません - 最初に含まれるディレクトリを作成してください",
"pathExists": "ディレクトリはすでに存在します - すべてのデータをバックアップしたことを確認してください",
"pathValidationFailed": "パスの検証に失敗しました",
"privacyPolicy": "プライバシーポリシー",
"selectItemsToMigrate": "移行する項目を選択",
"settings": {
"allowMetrics": "使用状況のメトリクス",
@@ -214,8 +221,7 @@
"collect": {
"errorReports": "エラーメッセージとスタックトレース",
"systemInfo": "ハードウェア、OSタイプ、アプリバージョン",
"userJourneyEvents": "ユーザージャーニーイベント",
"userJourneyTooltip": "ユーザージャーニーイベントは、アプリのインストールプロセスを通じてユーザーの旅を追跡するために使用されます。イベントの収集は、最初の成功したComfyUIワークフローの実行で終了します。"
"userJourneyEvents": "ユーザージャーニーイベント"
},
"doNotCollect": {
"customNodeConfigurations": "カスタムノードの設定",
@@ -224,13 +230,17 @@
"workflowContents": "ワークフローの内容"
},
"title": "データ収集について",
"viewFullPolicy": "完全なポリシーを見る",
"whatWeCollect": "収集内容:",
"whatWeDoNotCollect": "収集しない内容:"
},
"errorUpdatingConsent": "同意の更新エラー",
"errorUpdatingConsentDetail": "メトリクスの同意設定の更新に失敗しました",
"learnMoreAboutData": "データ収集の詳細を見る"
},
"systemLocations": "システムの場所",
"unhandledError": "未知のエラー"
"unhandledError": "未知のエラー",
"updateConsent": "以前はクラッシュの報告に同意していました。現在、バグの特定とアプリの改善を助けるためにイベントベースのメトリクスを追跡しています。個人を特定できる情報は収集されません。"
},
"issueReport": {
"contactFollowUp": "フォローアップのために私に連絡する",

View File

@@ -113,7 +113,7 @@
}
},
"CLIPLoader": {
"description": "[レシピ]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5",
"description": "[レシピ]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5\ncosmos: old t5 xxl",
"display_name": "CLIPを読み込む",
"inputs": {
"clip_name": {
@@ -862,6 +862,32 @@
}
}
},
"CosmosImageToVideoLatent": {
"display_name": "CosmosImageToVideoLatent",
"inputs": {
"batch_size": {
"name": "バッチサイズ"
},
"end_image": {
"name": "終了画像"
},
"height": {
"name": "高さ"
},
"length": {
"name": "長さ"
},
"start_image": {
"name": "開始画像"
},
"vae": {
"name": "vae"
},
"width": {
"name": "幅"
}
}
},
"CreateHookKeyframe": {
"display_name": "フックキーフレームを作成",
"inputs": {
@@ -1230,6 +1256,23 @@
}
}
},
"EmptyCosmosLatentVideo": {
"display_name": "EmptyCosmosLatentVideo",
"inputs": {
"batch_size": {
"name": "バッチサイズ"
},
"height": {
"name": "高さ"
},
"length": {
"name": "長さ"
},
"width": {
"name": "幅"
}
}
},
"EmptyHunyuanLatentVideo": {
"display_name": "EmptyHunyuanLatentVideo",
"inputs": {
@@ -4825,6 +4868,17 @@
}
}
},
"SetFirstSigma": {
"display_name": "SetFirstSigma",
"inputs": {
"sigma": {
"name": "シグマ"
},
"sigmas": {
"name": "シグマ"
}
}
},
"SetHookKeyframes": {
"display_name": "フックキーフレームを設定",
"inputs": {

View File

@@ -107,6 +107,7 @@
"noTasksFound": "작업을 찾을 수 없습니다.",
"noTasksFoundMessage": "대기열에 작업이 없습니다.",
"noWorkflowsFound": "워크플로를 찾을 수 없습니다.",
"ok": "확인",
"openNewIssue": "새 문제 열기",
"overwrite": "덮어쓰기",
"reconnected": "재연결됨",
@@ -129,6 +130,7 @@
"searchWorkflows": "워크플로 검색",
"settings": "설정",
"showReport": "보고서 보기",
"success": "성공",
"systemInfo": "시스템 정보",
"terminal": "터미널",
"upload": "업로드",
@@ -186,6 +188,7 @@
"selectGpu": "GPU 선택",
"selectGpuDescription": "소유한 GPU 유형을 선택하세요"
},
"helpImprove": "ComfyUI 개선에 도움을 주세요",
"installLocation": "설치 위치",
"installLocationDescription": "ComfyUI의 사용자 데이터 디렉토리를 선택하십시오. 선택한 위치에 Python 환경이 설치됩니다. 선택한 디스크에 충분한 공간(~15GB)이 남아 있는지 확인하십시오.",
"installLocationTooltip": "ComfyUI의 사용자 데이터 디렉토리. 저장소:\n- Python 환경\n- 모델\n- 사용자 정의 노드\n",
@@ -197,13 +200,17 @@
"title": "수동 구성",
"virtualEnvironmentPath": "가상 환경 경로"
},
"metricsDisabled": "메트릭스 비활성화",
"metricsEnabled": "메트릭스 활성화",
"migrateFromExistingInstallation": "기존 설치에서 마이그레이션",
"migration": "마이그레이션",
"migrationOptional": "마이그레이션은 선택 사항입니다. 기존에 설치된 것이 없다면, 이 단계를 건너뛸 수 있습니다.",
"migrationSourcePathDescription": "기존에 설치된 ComfyUI가 있는 경우, 기존 사용자 파일과 모델을 새 설치 복사하거나 링크할 수 있습니다",
"migrationSourcePathDescription": "기존 ComfyUI 설치가 있으면, 기존 사용자 파일과 모델을 새 설치 복사/링크할 수 있습니다. 기존의 ComfyUI 설치는 영향을 받지 않습니다.",
"moreInfo": "자세한 정보는 우리의",
"parentMissing": "경로가 존재하지 않습니다 - 먼저 포함하는 디렉토리를 생성하세요",
"pathExists": "디렉토리가 이미 존재합니다 - 모든 데이터를 백업했는지 확인해 주세요",
"pathValidationFailed": "경로 유효성 검사 실패",
"privacyPolicy": "개인정보 보호정책",
"selectItemsToMigrate": "마이그레이션 항목 선택",
"settings": {
"allowMetrics": "사용 통계",
@@ -214,8 +221,7 @@
"collect": {
"errorReports": "오류 메시지 및 스택 추적",
"systemInfo": "하드웨어, OS 유형, 앱 버전",
"userJourneyEvents": "사용자 여정 이벤트",
"userJourneyTooltip": "사용자 여정 이벤트는 앱 설치 과정을 통한 사용자의 여정을 추적하는 데 사용됩니다. 이벤트 수집은 첫 번째 성공적인 ComfyUI 워크플로우 실행에서 종료됩니다."
"userJourneyEvents": "사용자 여정 이벤트"
},
"doNotCollect": {
"customNodeConfigurations": "사용자 정의 노드 구성",
@@ -224,13 +230,17 @@
"workflowContents": "워크플로우 내용"
},
"title": "데이터 수집 안내",
"viewFullPolicy": "전체 정책 보기",
"whatWeCollect": "수집하는 정보:",
"whatWeDoNotCollect": "수집하지 않는 정보:"
},
"errorUpdatingConsent": "동의 업데이트 오류",
"errorUpdatingConsentDetail": "메트릭스 동의 설정 업데이트에 실패했습니다",
"learnMoreAboutData": "데이터 수집에 대해 더 알아보기"
},
"systemLocations": "시스템 위치",
"unhandledError": "알 수 없는 오류"
"unhandledError": "알 수 없는 오류",
"updateConsent": "당신은 이전에 충돌 보고에 동의했습니다. 이제 버그를 식별하고 앱을 개선하기 위해 이벤트 기반 메트릭스를 추적하고 있습니다. 개인 식별 정보는 수집하지 않습니다."
},
"issueReport": {
"contactFollowUp": "추적 조사를 위해 연락해 주세요",

View File

@@ -113,7 +113,7 @@
}
},
"CLIPLoader": {
"description": "[조합법]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5",
"description": "[조합법]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5\ncosmos: old t5 xxl",
"display_name": "CLIP 로드",
"inputs": {
"clip_name": {
@@ -862,6 +862,32 @@
}
}
},
"CosmosImageToVideoLatent": {
"display_name": "CosmosImageToVideoLatent",
"inputs": {
"batch_size": {
"name": "배치 크기"
},
"end_image": {
"name": "끝 이미지"
},
"height": {
"name": "높이"
},
"length": {
"name": "길이"
},
"start_image": {
"name": "시작 이미지"
},
"vae": {
"name": "vae"
},
"width": {
"name": "너비"
}
}
},
"CreateHookKeyframe": {
"display_name": "후크 키프레임 생성",
"inputs": {
@@ -1230,6 +1256,23 @@
}
}
},
"EmptyCosmosLatentVideo": {
"display_name": "EmptyCosmosLatentVideo",
"inputs": {
"batch_size": {
"name": "배치 크기"
},
"height": {
"name": "높이"
},
"length": {
"name": "길이"
},
"width": {
"name": "너비"
}
}
},
"EmptyHunyuanLatentVideo": {
"display_name": "빈 잠재 비디오 (Hunyuan)",
"inputs": {
@@ -4825,6 +4868,17 @@
}
}
},
"SetFirstSigma": {
"display_name": "SetFirstSigma",
"inputs": {
"sigma": {
"name": "시그마"
},
"sigmas": {
"name": "시그마들"
}
}
},
"SetHookKeyframes": {
"display_name": "후크 키프레임 설정",
"inputs": {

View File

@@ -107,6 +107,7 @@
"noTasksFound": "Задачи не найдены",
"noTasksFoundMessage": "В очереди нет задач.",
"noWorkflowsFound": "Рабочие процессы не найдены.",
"ok": "ОК",
"openNewIssue": "Открыть новую проблему",
"overwrite": "Перезаписать",
"reconnected": "Переподключено",
@@ -129,6 +130,7 @@
"searchWorkflows": "Поиск рабочих процессов",
"settings": "Настройки",
"showReport": "Показать отчет",
"success": "Успех",
"systemInfo": "Информация о системе",
"terminal": "Терминал",
"upload": "Загрузить",
@@ -186,6 +188,7 @@
"selectGpu": "Выберите GPU",
"selectGpuDescription": "Выберите тип GPU, который у вас есть"
},
"helpImprove": "Пожалуйста, помогите улучшить ComfyUI",
"installLocation": "Место установки",
"installLocationDescription": "Выберите директорию для пользовательских данных ComfyUI. В выбранном месте будет установлена среда Python. Пожалуйста, убедитесь, что на выбранном диске достаточно места (~15 ГБ).",
"installLocationTooltip": "Директория пользовательских данных ComfyUI. Хранит:\n- Среда Python\n- Модели\n- Пользовательские узлы\n",
@@ -197,13 +200,17 @@
"title": "Ручная Конфигурация",
"virtualEnvironmentPath": "Путь виртуального окружения"
},
"metricsDisabled": "Метрики отключены",
"metricsEnabled": "Метрики включены",
"migrateFromExistingInstallation": "Миграция из существующей установки",
"migration": "Миграция",
"migrationOptional": "Миграция является необязательной. Если у вас нет существующей установки, вы можете пропустить этот шаг.",
"migrationSourcePathDescription": "Если у вас есть существующая установка ComfyUI, мы можем скопировать/связать ваши существующие пользовательские файлы и модели в новую установку.",
"migrationSourcePathDescription": "Если у вас уже есть установка ComfyUI, мы можем скопировать/связать ваши существующие пользовательские файлы и модели с новой установкой. Ваша существующая установка ComfyUI не будет затронута.",
"moreInfo": "Для получения дополнительной информации, пожалуйста, прочтите нашу",
"parentMissing": "Путь не существует - сначала создайте родительский каталог",
"pathExists": "Директория уже существует - пожалуйста, убедитесь, что вы сделали резервное копирование всех данных",
"pathValidationFailed": "Не удалось проверить путь",
"privacyPolicy": "политику конфиденциальности",
"selectItemsToMigrate": "Выберите элементы для миграции",
"settings": {
"allowMetrics": "Метрики использования",
@@ -214,8 +221,7 @@
"collect": {
"errorReports": "Сообщение об ошибке и трассировка стека",
"systemInfo": "Аппаратное обеспечение, тип ОС и версия приложения",
"userJourneyEvents": "События пользовательского пути",
"userJourneyTooltip": "События пользовательского пути используются для отслеживания пути пользователя в процессе установки приложения. Сбор событий заканчивается после первого успешного запуска рабочего процесса ComfyUI."
"userJourneyEvents": "События пользовательского пути"
},
"doNotCollect": {
"customNodeConfigurations": "Пользовательские конфигурации узлов",
@@ -224,13 +230,17 @@
"workflowContents": "Содержание рабочего процесса"
},
"title": "О сборе данных",
"viewFullPolicy": "Просмотреть политику полностью",
"whatWeCollect": "Что мы собираем:",
"whatWeDoNotCollect": "Что мы не собираем:"
},
"errorUpdatingConsent": "Ошибка обновления согласия",
"errorUpdatingConsentDetail": "Не удалось обновить настройки согласия на метрики",
"learnMoreAboutData": "Узнать больше о сборе данных"
},
"systemLocations": "Системные места",
"unhandledError": "Неизвестная ошибка"
"unhandledError": "Неизвестная ошибка",
"updateConsent": "Вы ранее согласились на отчетность об ошибках. Теперь мы отслеживаем событийные метрики, чтобы помочь выявить ошибки и улучшить приложение. Личная идентифицируемая информация не собирается."
},
"issueReport": {
"contactFollowUp": "Свяжитесь со мной для уточнения",

View File

@@ -113,7 +113,7 @@
}
},
"CLIPLoader": {
"description": "[Рецепты]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5",
"description": "[Рецепты]\n\nstable_diffusion: clip-l\nstable_cascade: clip-g\nsd3: t5 / clip-g / clip-l\nstable_audio: t5\nmochi: t5\ncosmos: old t5 xxl",
"display_name": "Загрузить CLIP",
"inputs": {
"clip_name": {
@@ -862,6 +862,32 @@
}
}
},
"CosmosImageToVideoLatent": {
"display_name": "CosmosImageToVideoLatent",
"inputs": {
"batch_size": {
"name": "размер_пакета"
},
"end_image": {
"name": онечное_изображение"
},
"height": {
"name": "высота"
},
"length": {
"name": "длина"
},
"start_image": {
"name": ачальное_изображение"
},
"vae": {
"name": "vae"
},
"width": {
"name": "ширина"
}
}
},
"CreateHookKeyframe": {
"display_name": "Создать ключевой кадр хука",
"inputs": {
@@ -1230,6 +1256,23 @@
}
}
},
"EmptyCosmosLatentVideo": {
"display_name": "EmptyCosmosLatentVideo",
"inputs": {
"batch_size": {
"name": "размер_пакета"
},
"height": {
"name": "высота"
},
"length": {
"name": "длина"
},
"width": {
"name": "ширина"
}
}
},
"EmptyHunyuanLatentVideo": {
"display_name": устойHunyuanLatentVideo",
"inputs": {
@@ -4825,6 +4868,17 @@
}
}
},
"SetFirstSigma": {
"display_name": "SetFirstSigma",
"inputs": {
"sigma": {
"name": "сигма"
},
"sigmas": {
"name": "сигмы"
}
}
},
"SetHookKeyframes": {
"display_name": "Установить ключевые кадры хука",
"inputs": {

View File

@@ -107,6 +107,7 @@
"noTasksFound": "未找到任务",
"noTasksFoundMessage": "队列中没有任务。",
"noWorkflowsFound": "未找到工作流。",
"ok": "确定",
"openNewIssue": "打开新问题",
"overwrite": "覆盖",
"reconnected": "已重新连接",
@@ -129,6 +130,7 @@
"searchWorkflows": "搜索工作流",
"settings": "设置",
"showReport": "显示报告",
"success": "成功",
"systemInfo": "系统信息",
"terminal": "终端",
"upload": "上传",
@@ -186,6 +188,7 @@
"selectGpu": "选择 GPU",
"selectGpuDescription": "选择你拥有的 GPU 类型"
},
"helpImprove": "请帮助我们改进ComfyUI",
"installLocation": "安装位置",
"installLocationDescription": "选择 ComfyUI 用户数据的存放目录。将安装一个 Python 环境到所选位置。请确保所选磁盘有足够的空间(约 15GB。",
"installLocationTooltip": "ComfyUI 的用户数据目录。存储:\n- Python 环境\n- 模型\n- 自定义节点\n",
@@ -197,13 +200,17 @@
"title": "手动配置",
"virtualEnvironmentPath": "虚拟环境路径"
},
"metricsDisabled": "禁用度量",
"metricsEnabled": "启用度量",
"migrateFromExistingInstallation": "从现有安装迁移",
"migration": "迁移",
"migrationOptional": "迁移是可选的。如果您之前没有安装过 ComfyUI可以跳过此步骤。",
"migrationSourcePathDescription": "如果您有可用的 ComfyUI我们可以将您的现有用户文件和模型复制/链接到新安装。",
"migrationSourcePathDescription": "如果您已有现有的ComfyUI安装,我们可以复制/链接您现有用户文件和模型到新安装。您现有的ComfyUI安装将不会受到影响。",
"moreInfo": "有关更多信息,请阅读我们的",
"parentMissing": "路径不存在 - 请先创建包含该路径的目录",
"pathExists": "目录已存在 - 请确保您已备份全部数据",
"pathValidationFailed": "路径验证失败",
"privacyPolicy": "隐私政策",
"selectItemsToMigrate": "选择要迁移的项目",
"settings": {
"allowMetrics": "使用情况指标",
@@ -214,8 +221,7 @@
"collect": {
"errorReports": "错误报告和堆栈跟踪",
"systemInfo": "硬件,操作系统类型和应用版本",
"userJourneyEvents": "用户旅程事件",
"userJourneyTooltip": "用户旅程事件用于跟踪用户通过应用安装过程的旅程。事件收集在第一次成功运行ComfyUI工作流后结束。"
"userJourneyEvents": "用户旅程事件"
},
"doNotCollect": {
"customNodeConfigurations": "自定义节点配置",
@@ -224,13 +230,17 @@
"workflowContents": "工作流内容"
},
"title": "关于数据收集",
"viewFullPolicy": "查看完整政策",
"whatWeCollect": "我们收集的内容:",
"whatWeDoNotCollect": "我们不收集的内容:"
},
"errorUpdatingConsent": "更新同意错误",
"errorUpdatingConsentDetail": "无法更新度量同意设置",
"learnMoreAboutData": "了解更多关于数据收集的信息"
},
"systemLocations": "系统位置",
"unhandledError": "未知错误"
"unhandledError": "未知错误",
"updateConsent": "您之前选择了报告崩溃。我们现在正在跟踪基于事件的度量,以帮助识别错误并改进应用程序。我们不收集任何个人可识别信息。"
},
"issueReport": {
"contactFollowUp": "跟进联系我",

View File

@@ -113,7 +113,7 @@
}
},
"CLIPLoader": {
"description": "[配方]\n\nStable Diffusionclip-l\nStable Cascadeclip-g\nSD3t5 / clip-g / clip-l\nStable Audiot5\nMochit5",
"description": "[配方]\n\nStable Diffusionclip-l\nStable Cascadeclip-g\nSD3t5 / clip-g / clip-l\nStable Audiot5\nMochit5\ncosmosold t5 xxl",
"display_name": "加载CLIP",
"inputs": {
"clip_name": {
@@ -862,6 +862,32 @@
}
}
},
"CosmosImageToVideoLatent": {
"display_name": "Cosmos图像到视频潜在",
"inputs": {
"batch_size": {
"name": "批量大小"
},
"end_image": {
"name": "结束图像"
},
"height": {
"name": "高度"
},
"length": {
"name": "长度"
},
"start_image": {
"name": "开始图像"
},
"vae": {
"name": "vae"
},
"width": {
"name": "宽度"
}
}
},
"CreateHookKeyframe": {
"display_name": "创建约束关键帧",
"inputs": {
@@ -1230,6 +1256,23 @@
}
}
},
"EmptyCosmosLatentVideo": {
"display_name": "空的Cosmos潜在视频",
"inputs": {
"batch_size": {
"name": "批量大小"
},
"height": {
"name": "高度"
},
"length": {
"name": "长度"
},
"width": {
"name": "宽度"
}
}
},
"EmptyHunyuanLatentVideo": {
"display_name": "空Latent视频混元",
"inputs": {
@@ -4825,6 +4868,17 @@
}
}
},
"SetFirstSigma": {
"display_name": "设置第一个Sigma",
"inputs": {
"sigma": {
"name": "sigma"
},
"sigmas": {
"name": "sigmas"
}
}
},
"SetHookKeyframes": {
"display_name": "设置约束关键帧",
"inputs": {

View File

@@ -92,6 +92,18 @@ const router = createRouter({
name: 'ManualConfigurationView',
component: () => import('@/views/ManualConfigurationView.vue'),
beforeEnter: guardElectronAccess
},
{
path: '/metrics-consent',
name: 'MetricsConsentView',
component: () => import('@/views/MetricsConsentView.vue'),
beforeEnter: guardElectronAccess
},
{
path: 'desktop-start',
name: 'DesktopStartView',
component: () => import('@/views/DesktopStartView.vue'),
beforeEnter: guardElectronAccess
}
]
}

View File

@@ -417,6 +417,12 @@ LGraphNode.prototype.addDOMWidget = function <
element.dataset.collapsed = this.flags?.collapsed ? 'true' : 'false'
}
const { onConfigure } = this
this.onConfigure = function () {
onConfigure?.apply(this, arguments)
element.dataset.collapsed = this.flags?.collapsed ? 'true' : 'false'
}
const onRemoved = this.onRemoved
this.onRemoved = function () {
element.remove()

View File

@@ -3,6 +3,8 @@ import {
ElectronContextMenuOptions
} from '@comfyorg/comfyui-electron-types'
import { useSystemStatsStore } from '@/stores/systemStatsStore'
export function isElectron() {
return 'electronAPI' in window && window.electronAPI !== undefined
}
@@ -14,3 +16,34 @@ export function electronAPI() {
export function showNativeMenu(options?: ElectronContextMenuOptions) {
electronAPI()?.showContextMenu(options)
}
const normalizeVersion = (version: string) => {
return version
.split('.')
.map(Number)
.filter((n): n is number => !Number.isNaN(n))
}
export function isVersionLessThan(versionA: string, versionB: string) {
versionA ??= '0.0.0'
versionB ??= '0.0.0'
const normalizedA = normalizeVersion(versionA)
const normalizedB = normalizeVersion(versionB)
for (let i = 0; i < Math.max(normalizedA.length, normalizedB.length); i++) {
const a = normalizedA[i] ?? 0
const b = normalizedB[i] ?? 0
if (a < b) return true
if (a > b) return false
}
return false
}
export function getComfyVersion() {
if (isElectron()) return electronAPI().getComfyUIVersion()
const store = useSystemStatsStore()
store.fetchSystemStats()
return store.systemStats?.system?.comfyui_version ?? ''
}

View File

@@ -0,0 +1,13 @@
<template>
<BaseViewTemplate dark>
<div class="max-w-screen-sm w-screen p-8">
<ProgressBar mode="indeterminate" />
</div>
</BaseViewTemplate>
</template>
<script setup lang="ts">
import ProgressBar from 'primevue/progressbar'
import BaseViewTemplate from './templates/BaseViewTemplate.vue'
</script>

View File

@@ -55,6 +55,7 @@ const toast = useToast()
const settingStore = useSettingStore()
const executionStore = useExecutionStore()
const colorPaletteStore = useColorPaletteStore()
const queueStore = useQueueStore()
watch(
() => colorPaletteStore.completedActivePalette,
@@ -76,6 +77,29 @@ watch(
{ immediate: true }
)
if (isElectron()) {
watch(
() => queueStore.tasks,
(newTasks, oldTasks) => {
// Report tasks that previously running but are now completed (i.e. in history)
const oldRunningTaskIds = new Set(
oldTasks.filter((task) => task.isRunning).map((task) => task.promptId)
)
newTasks
.filter(
(task) => oldRunningTaskIds.has(task.promptId) && task.isHistory
)
.forEach((task) => {
electronAPI().Events.incrementUserProperty(
`execution:${task.displayStatus.toLowerCase()}`,
1
)
})
},
{ deep: true }
)
}
watchEffect(() => {
const fontSize = settingStore.get('Comfy.TextareaWidget.FontSize')
document.documentElement.style.setProperty(
@@ -110,9 +134,7 @@ watchEffect(() => {
})
watchEffect(() => {
useQueueStore().maxHistoryItems = settingStore.get(
'Comfy.Queue.MaxHistoryItems'
)
queueStore.maxHistoryItems = settingStore.get('Comfy.Queue.MaxHistoryItems')
})
const init = () => {
@@ -126,8 +148,9 @@ const init = () => {
}
const queuePendingTaskCountStore = useQueuePendingTaskCountStore()
const onStatus = (e: CustomEvent<StatusWsMessageStatus>) => {
const onStatus = async (e: CustomEvent<StatusWsMessageStatus>) => {
queuePendingTaskCountStore.update(e)
await queueStore.update()
}
const reconnectingMessage: ToastMessageOptions = {

View File

@@ -6,7 +6,7 @@
<Stepper
class="h-full p-8 2xl:p-16"
value="0"
@update:value="setHighestStep"
@update:value="handleStepChange"
>
<StepList class="select-none">
<Step value="0">
@@ -137,6 +137,14 @@ const allowMetrics = ref(true)
/** Forces each install step to be visited at least once. */
const highestStep = ref(0)
const handleStepChange = (value: string | number) => {
setHighestStep(value)
electronAPI().Events.trackEvent('install_stepper_change', {
step: value
})
}
const setHighestStep = (value: string | number) => {
const int = typeof value === 'number' ? value : parseInt(value, 10)
if (!isNaN(int) && int > highestStep.value) highestStep.value = int
@@ -167,12 +175,18 @@ onMounted(async () => {
if (!electron) return
const detectedGpu = await electron.Config.getDetectedGpu()
if (detectedGpu === 'mps' || detectedGpu === 'nvidia')
if (detectedGpu === 'mps' || detectedGpu === 'nvidia') {
device.value = detectedGpu
}
electronAPI().Events.trackEvent('install_stepper_change', {
step: '0',
gpu: detectedGpu
})
})
</script>
<style lang="postcss" scoped>
<style scoped>
:deep(.p-steppanel) {
@apply bg-transparent;
}

View File

@@ -75,8 +75,8 @@ onMounted(async () => {
})
</script>
<style>
:root {
<style scoped>
.p-tag {
--p-tag-gap: 0.5rem;
}

View File

@@ -0,0 +1,83 @@
<template>
<BaseViewTemplate dark>
<div class="h-full p-8 2xl:p-16 flex flex-col items-center justify-center">
<div
class="bg-neutral-800 rounded-lg shadow-lg p-6 w-full max-w-[600px] flex flex-col gap-6"
>
<h2 class="text-3xl font-semibold text-neutral-100">
{{ $t('install.helpImprove') }}
</h2>
<p class="text-neutral-400">
{{ $t('install.updateConsent') }}
</p>
<p class="text-neutral-400">
{{ $t('install.moreInfo') }}
<a
href="https://comfy.org/privacy"
target="_blank"
class="text-blue-400 hover:text-blue-300 underline"
>
{{ $t('install.privacyPolicy') }} </a
>.
</p>
<div class="flex items-center gap-4">
<ToggleSwitch
v-model="allowMetrics"
aria-describedby="metricsDescription"
/>
<span id="metricsDescription" class="text-neutral-100">
{{
allowMetrics
? $t('install.metricsEnabled')
: $t('install.metricsDisabled')
}}
</span>
</div>
<div class="flex pt-6 justify-end">
<Button
:label="$t('g.ok')"
icon="pi pi-check"
:loading="isUpdating"
iconPos="right"
@click="updateConsent"
/>
</div>
</div>
</div>
</BaseViewTemplate>
</template>
<script setup lang="ts">
import Button from 'primevue/button'
import ToggleSwitch from 'primevue/toggleswitch'
import { useToast } from 'primevue/usetoast'
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { electronAPI } from '@/utils/envUtil'
const toast = useToast()
const { t } = useI18n()
const allowMetrics = ref(true)
const router = useRouter()
const isUpdating = ref(false)
const updateConsent = async () => {
isUpdating.value = true
try {
await electronAPI().setMetricsConsent(allowMetrics.value)
} catch (error) {
toast.add({
severity: 'error',
summary: t('install.errorUpdatingConsent'),
detail: t('install.errorUpdatingConsentDetail'),
life: 3000
})
} finally {
isUpdating.value = false
}
router.push('/')
}
</script>

View File

@@ -78,7 +78,7 @@ const continueToInstall = () => {
}
</script>
<style>
<style scoped>
.sad-container {
@apply grid items-center justify-evenly;
grid-template-columns: 25rem 1fr;

View File

@@ -78,7 +78,7 @@ const terminalCreated = (
) => {
xterm = terminal
useAutoSize(root, true, true)
useAutoSize({ root, autoRows: true, autoCols: true })
electron.onLogMessage((message: string) => {
terminal.write(message)
})

View File

@@ -28,7 +28,7 @@ import { electronAPI, isElectron } from '@/utils/envUtil'
const props = withDefaults(
defineProps<{
dark: boolean
dark?: boolean
}>(),
{
dark: false

View File

@@ -57,8 +57,18 @@ const mockElectronAPI: Plugin = {
changeTheme: () => {},
Config: {
setWindowStyle: () => {},
getWindowStyle: () => Promise.resolve('default')
}
getWindowStyle: () => Promise.resolve('default'),
getDetectedGpu: () => Promise.resolve('nvidia')
},
Events: {
trackEvent: (event_name, event_data) => {
console.log('trackEvent', event_name, event_data)
},
incrementUserProperty: (property, value) => {
console.log('incrementUserProperty', property, value)
}
},
setMetricsConsent: (consent) => {}
};`
}
]