From fceb0017ce105279d0ee57eac896823ae7f0d1b6 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Wed, 10 Dec 2025 18:35:30 -0800 Subject: [PATCH 01/72] refactor: update outdated tooltip on menu setting (#7330) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Followup from https://github.com/Comfy-Org/ComfyUI_frontend/pull/4312: remove tooltip with outdated info discussing the bottom menu. The bottom menu setting is removed and even if it was not removed, the logic that forced the menu to the top on mobile was removed, so this tooltip is outdated and gives wrong info. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7330-refactor-update-outdated-tooltip-on-menu-setting-2c66d73d36508128b356c6f985d5b12b) by [Unito](https://www.unito.io) --- src/locales/en/settings.json | 4 ++-- src/platform/settings/constants/coreSettings.ts | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index 08dd1aae99..31bda257ab 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -351,7 +351,7 @@ }, "Comfy_UseNewMenu": { "name": "Use new menu", - "tooltip": "Menu bar position. On mobile devices, the menu is always shown at the top.", + "tooltip": "Enable the redesigned top menu bar.", "options": { "Disabled": "Disabled", "Top": "Top" @@ -440,4 +440,4 @@ "pysssss_SnapToGrid": { "name": "Always snap to grid" } -} \ No newline at end of file +} diff --git a/src/platform/settings/constants/coreSettings.ts b/src/platform/settings/constants/coreSettings.ts index fb87ea1334..05d2a786d5 100644 --- a/src/platform/settings/constants/coreSettings.ts +++ b/src/platform/settings/constants/coreSettings.ts @@ -562,8 +562,7 @@ export const CORE_SETTINGS: SettingParams[] = [ name: 'Use new menu', type: 'combo', options: ['Disabled', 'Top'], - tooltip: - 'Menu bar position. On mobile devices, the menu is always shown at the top.', + tooltip: 'Enable the redesigned top menu bar.', migrateDeprecatedValue: (value: string) => { // Floating is now supported by dragging the docked actionbar off. if (value === 'Floating') { From 987dcb189dc9bae23ab02bdb6678ba3987c363e4 Mon Sep 17 00:00:00 2001 From: Alexander Brown Date: Wed, 10 Dec 2025 18:36:58 -0800 Subject: [PATCH 02/72] Lint: Start cleanup of the i18n imports (#7327) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Avoid direct access of i18n instance to favor useI18n ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7327-Lint-Start-cleanup-of-the-i18n-imports-2c56d73d3650811d9214c9a02863a5a3) by [Unito](https://www.unito.io) --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .oxlintrc.json | 71 +++++++++++++++++-- eslint.config.ts | 66 ++++------------- lint-staged.config.js | 17 ----- lint-staged.config.ts | 21 ++++++ src/i18n.ts | 4 +- .../SearchFilterDropdown.vue | 2 +- tsconfig.json | 1 + 7 files changed, 104 insertions(+), 78 deletions(-) delete mode 100644 lint-staged.config.js create mode 100644 lint-staged.config.ts diff --git a/.oxlintrc.json b/.oxlintrc.json index 4160601d9e..276ef5461d 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -2,16 +2,20 @@ "$schema": "./node_modules/oxlint/configuration_schema.json", "ignorePatterns": [ ".i18nrc.cjs", - "components.d.ts", - "lint-staged.config.js", - "vitest.setup.ts", + ".nx/*", "**/vite.config.*.timestamp*", "**/vitest.config.*.timestamp*", + "components.d.ts", + "coverage/*", + "dist/*", "packages/registry-types/src/comfyRegistryTypes.ts", + "playwright-report/*", "src/extensions/core/*", "src/scripts/*", "src/types/generatedManagerTypes.ts", - "src/types/vue-shim.d.ts" + "src/types/vue-shim.d.ts", + "test-results/*", + "vitest.setup.ts" ], "plugins": [ "eslint", @@ -24,9 +28,55 @@ ], "rules": { "no-async-promise-executor": "off", + "no-console": [ + "error", + { + "allow": [ + "warn", + "error" + ] + } + ], "no-control-regex": "off", "no-eval": "off", "no-redeclare": "error", + "no-restricted-imports": [ + "error", + { + "paths": [ + { + "name": "primevue/calendar", + "message": "Calendar is deprecated in PrimeVue 4+. Use DatePicker instead: import DatePicker from 'primevue/datepicker'" + }, + { + "name": "primevue/dropdown", + "message": "Dropdown is deprecated in PrimeVue 4+. Use Select instead: import Select from 'primevue/select'" + }, + { + "name": "primevue/inputswitch", + "message": "InputSwitch is deprecated in PrimeVue 4+. Use ToggleSwitch instead: import ToggleSwitch from 'primevue/toggleswitch'" + }, + { + "name": "primevue/overlaypanel", + "message": "OverlayPanel is deprecated in PrimeVue 4+. Use Popover instead: import Popover from 'primevue/popover'" + }, + { + "name": "primevue/sidebar", + "message": "Sidebar is deprecated in PrimeVue 4+. Use Drawer instead: import Drawer from 'primevue/drawer'" + }, + { + "name": "@/i18n--to-enable", + "importNames": [ + "st", + "t", + "te", + "d" + ], + "message": "Don't import `@/i18n` directly, prefer `useI18n()`" + } + ] + } + ], "no-self-assign": "allow", "no-unused-expressions": "off", "no-unused-private-class-members": "off", @@ -64,5 +114,16 @@ "typescript/no-floating-promises": "error", "vue/no-import-compiler-macros": "error", "vue/no-dupe-keys": "error" - } + }, + "overrides": [ + { + "files": [ + "**/*.{stories,test,spec}.ts", + "**/*.stories.vue" + ], + "rules": { + "no-console": "allow" + } + } + ] } \ No newline at end of file diff --git a/eslint.config.ts b/eslint.config.ts index 394df066d3..69efe4bef6 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -62,16 +62,20 @@ export default defineConfig([ { ignores: [ '.i18nrc.cjs', - 'components.d.ts', - 'lint-staged.config.js', - 'vitest.setup.ts', + '.nx/*', '**/vite.config.*.timestamp*', '**/vitest.config.*.timestamp*', + 'components.d.ts', + 'coverage/*', + 'dist/*', 'packages/registry-types/src/comfyRegistryTypes.ts', + 'playwright-report/*', 'src/extensions/core/*', 'src/scripts/*', 'src/types/generatedManagerTypes.ts', - 'src/types/vue-shim.d.ts' + 'src/types/vue-shim.d.ts', + 'test-results/*', + 'vitest.setup.ts' ] }, { @@ -103,24 +107,17 @@ export default defineConfig([ tseslintConfigs.recommended, // Difference in typecheck on CI vs Local - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore Bad types in the plugin pluginVue.configs['flat/recommended'], eslintPluginPrettierRecommended, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore Type incompatibility between import-x plugin and ESLint config types storybook.configs['flat/recommended'], - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore Type incompatibility between import-x plugin and ESLint config types + // @ts-expect-error Type incompatibility between import-x plugin and ESLint config types importX.flatConfigs.recommended, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore Type incompatibility between import-x plugin and ESLint config types + // @ts-expect-error Type incompatibility between import-x plugin and ESLint config types importX.flatConfigs.typescript, { plugins: { 'unused-imports': unusedImports, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore Type incompatibility in i18n plugin + // @ts-expect-error Type incompatibility in i18n plugin '@intlify/vue-i18n': pluginI18n }, rules: { @@ -138,7 +135,6 @@ export default defineConfig([ 'import-x/no-useless-path-segments': 'error', 'import-x/no-relative-packages': 'error', 'unused-imports/no-unused-imports': 'error', - 'no-console': ['error', { allow: ['warn', 'error'] }], 'vue/no-v-html': 'off', // Prohibit dark-theme: and dark: prefixes 'vue/no-restricted-class': ['error', '/^dark(-theme)?:/'], @@ -153,39 +149,7 @@ export default defineConfig([ 'vue/no-use-v-else-with-v-for': 'error', 'vue/one-component-per-file': 'error', 'vue/require-default-prop': 'off', // TODO: fix -- this one is very worthwhile - // Restrict deprecated PrimeVue components - 'no-restricted-imports': [ - 'error', - { - paths: [ - { - name: 'primevue/calendar', - message: - 'Calendar is deprecated in PrimeVue 4+. Use DatePicker instead: import DatePicker from "primevue/datepicker"' - }, - { - name: 'primevue/dropdown', - message: - 'Dropdown is deprecated in PrimeVue 4+. Use Select instead: import Select from "primevue/select"' - }, - { - name: 'primevue/inputswitch', - message: - 'InputSwitch is deprecated in PrimeVue 4+. Use ToggleSwitch instead: import ToggleSwitch from "primevue/toggleswitch"' - }, - { - name: 'primevue/overlaypanel', - message: - 'OverlayPanel is deprecated in PrimeVue 4+. Use Popover instead: import Popover from "primevue/popover"' - }, - { - name: 'primevue/sidebar', - message: - 'Sidebar is deprecated in PrimeVue 4+. Use Drawer instead: import Drawer from "primevue/drawer"' - } - ] - } - ], + // i18n rules '@intlify/vue-i18n/no-raw-text': [ 'error', @@ -273,12 +237,6 @@ export default defineConfig([ ] } }, - { - files: ['**/*.{test,spec,stories}.ts', '**/*.stories.vue'], - rules: { - 'no-console': 'off' - } - }, { files: ['scripts/**/*.js'], languageOptions: { diff --git a/lint-staged.config.js b/lint-staged.config.js deleted file mode 100644 index 5283d261ed..0000000000 --- a/lint-staged.config.js +++ /dev/null @@ -1,17 +0,0 @@ -export default { - './**/*.js': (stagedFiles) => formatAndEslint(stagedFiles), - - './**/*.{ts,tsx,vue,mts}': (stagedFiles) => [ - ...formatAndEslint(stagedFiles), - 'pnpm typecheck' - ] -} - -function formatAndEslint(fileNames) { - // Convert absolute paths to relative paths for better ESLint resolution - const relativePaths = fileNames.map((f) => f.replace(process.cwd() + '/', '')) - return [ - `pnpm exec eslint --cache --fix ${relativePaths.join(' ')}`, - `pnpm exec prettier --cache --write ${relativePaths.join(' ')}` - ] -} diff --git a/lint-staged.config.ts b/lint-staged.config.ts new file mode 100644 index 0000000000..89deaa4b0e --- /dev/null +++ b/lint-staged.config.ts @@ -0,0 +1,21 @@ +import path from 'node:path' + +export default { + './**/*.js': (stagedFiles: string[]) => formatAndEslint(stagedFiles), + + './**/*.{ts,tsx,vue,mts}': (stagedFiles: string[]) => [ + ...formatAndEslint(stagedFiles), + 'pnpm typecheck' + ] +} + +function formatAndEslint(fileNames: string[]) { + // Convert absolute paths to relative paths for better ESLint resolution + const relativePaths = fileNames.map((f) => path.relative(process.cwd(), f)) + const joinedPaths = relativePaths.map((p) => `"${p}"`).join(' ') + return [ + `pnpm exec prettier --cache --write ${joinedPaths}`, + `pnpm exec oxlint --fix ${joinedPaths}`, + `pnpm exec eslint --cache --fix --no-warn-ignored ${joinedPaths}` + ] +} diff --git a/src/i18n.ts b/src/i18n.ts index 3ef845ed16..4f09619f65 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -197,6 +197,7 @@ export const i18n = createI18n({ }) /** Convenience shorthand: i18n.global */ +/** @deprecated use useI18n */ export const { t, te, d } = i18n.global /** @@ -204,7 +205,8 @@ export const { t, te, d } = i18n.global * * @param key - The key to translate. * @param fallbackMessage - The fallback message to use if the key is not found. + * @deprecated Remove, use the defaultMsg overload */ export function st(key: string, fallbackMessage: string) { - return te(key) ? t(key) : fallbackMessage + return t(key, fallbackMessage) } diff --git a/src/workbench/extensions/manager/components/manager/registrySearchBar/SearchFilterDropdown.vue b/src/workbench/extensions/manager/components/manager/registrySearchBar/SearchFilterDropdown.vue index e076e0cafb..973820c728 100644 --- a/src/workbench/extensions/manager/components/manager/registrySearchBar/SearchFilterDropdown.vue +++ b/src/workbench/extensions/manager/components/manager/registrySearchBar/SearchFilterDropdown.vue @@ -19,7 +19,7 @@ \ No newline at end of file diff --git a/src/platform/cloud/subscription/components/StripePricingTable.vue b/src/platform/cloud/subscription/components/StripePricingTable.vue deleted file mode 100644 index 8efa7c21ed..0000000000 --- a/src/platform/cloud/subscription/components/StripePricingTable.vue +++ /dev/null @@ -1,117 +0,0 @@ - - - diff --git a/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue b/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue index ddb1680d8b..43e6a22952 100644 --- a/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue +++ b/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue @@ -1,6 +1,6 @@ \ No newline at end of file + } finally { + isLoading.value = false + loadingTier.value = null + } +}, reportError) + From 5cba1e3f88b4edb265e9976c78372192238ceb51 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Thu, 11 Dec 2025 02:01:54 -0800 Subject: [PATCH 14/72] fix: prevent duplicate backport workflow runs for same PR (#7335) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary When multiple labels are added to a PR in quick succession (e.g., `needs-backport` and `core/1.33`), each label triggers a separate workflow run. Both runs would proceed independently, causing duplicate failure comments or redundant work. This adds a concurrency group keyed by PR number with `cancel-in-progress: false`, ensuring runs for the same PR are serialized rather than racing. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7335-fix-prevent-duplicate-backport-workflow-runs-for-same-PR-2c66d73d36508140a603cd7110c42442) by [Unito](https://www.unito.io) --- .github/workflows/pr-backport.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/pr-backport.yaml b/.github/workflows/pr-backport.yaml index 69607d6fb1..13fab6cdb4 100644 --- a/.github/workflows/pr-backport.yaml +++ b/.github/workflows/pr-backport.yaml @@ -16,6 +16,10 @@ on: type: boolean default: false +concurrency: + group: backport-${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} + cancel-in-progress: false + jobs: backport: if: > From ea59fb5fc3df30036419a4cf2e0b6a57eaf00e81 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Thu, 11 Dec 2025 03:05:42 -0800 Subject: [PATCH 15/72] fix: hardcoded color tokens (not theme-aware) (#7366) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes instances of hardcoded color tokens (not semantic) which are not theme-aware and therefore are incorrect on e.g. light mode. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7366-fix-hardcoded-color-tokens-not-theme-aware-2c66d73d365081e294aaff366fc78a8f) by [Unito](https://www.unito.io) --- .../content/TopUpCreditsDialogContent.vue | 4 ++-- .../content/credit/CreditTopUpOption.vue | 4 ++-- .../subscription/components/PricingTable.vue | 20 +++++++++++-------- .../SubscriptionRequiredDialogContent.vue | 4 ++-- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/components/dialog/content/TopUpCreditsDialogContent.vue b/src/components/dialog/content/TopUpCreditsDialogContent.vue index 3ce3484f72..70798e6b32 100644 --- a/src/components/dialog/content/TopUpCreditsDialogContent.vue +++ b/src/components/dialog/content/TopUpCreditsDialogContent.vue @@ -3,7 +3,7 @@
-

+

{{ isInsufficientCredits ? $t('credits.topUp.addMoreCreditsToRun') @@ -62,7 +62,7 @@ severity="primary" :label="$t('credits.topUp.buy')" :class="['w-full', { 'opacity-30': !selectedCredits || loading }]" - :pt="{ label: { class: 'text-white' } }" + :pt="{ label: { class: 'text-primary-foreground' } }" @click="handleBuy" />

diff --git a/src/components/dialog/content/credit/CreditTopUpOption.vue b/src/components/dialog/content/credit/CreditTopUpOption.vue index a1bc147ef6..c67c273a27 100644 --- a/src/components/dialog/content/credit/CreditTopUpOption.vue +++ b/src/components/dialog/content/credit/CreditTopUpOption.vue @@ -8,10 +8,10 @@ ]" @click="$emit('select')" > - + {{ formattedCredits }} - + {{ description }}
diff --git a/src/platform/cloud/subscription/components/PricingTable.vue b/src/platform/cloud/subscription/components/PricingTable.vue index 60750ec159..e49f7c87d3 100644 --- a/src/platform/cloud/subscription/components/PricingTable.vue +++ b/src/platform/cloud/subscription/components/PricingTable.vue @@ -14,7 +14,7 @@
{{ t('subscription.mostPopular') }}
@@ -65,22 +65,25 @@ {{ t('subscription.gpuLabel') }} - +
{{ t('subscription.addCreditsLabel') }} - +
{{ t('subscription.customLoRAsLabel') }} - - + +
@@ -119,7 +122,8 @@ class="h-10 w-full" :pt="{ label: { - class: 'font-inter text-sm font-bold leading-normal text-white' + class: + 'font-inter text-sm font-bold leading-normal text-primary-foreground' } }" @click="() => handleSubscribe(tier.key)" @@ -149,10 +153,10 @@ {{ t('subscription.videoEstimateExplanation') }}

{{ t('subscription.videoEstimateTryTemplate') }} diff --git a/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue b/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue index 43e6a22952..095ebec4e6 100644 --- a/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue +++ b/src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue @@ -46,7 +46,7 @@ severity="secondary" icon="pi pi-comments" icon-pos="right" - class="h-6 p-1 text-sm text-text-secondary hover:text-white" + class="h-6 p-1 text-sm text-text-secondary hover:text-base-foreground" @click="handleContactUs" /> {{ $t('g.or') }} @@ -56,7 +56,7 @@ severity="secondary" icon="pi pi-external-link" icon-pos="right" - class="h-6 p-1 text-sm text-text-secondary hover:text-white" + class="h-6 p-1 text-sm text-text-secondary hover:text-base-foreground" @click="handleViewEnterprise" />
From 5fad29ed37906857ecd3fec072bc94119defc06d Mon Sep 17 00:00:00 2001 From: Luke Mino-Altherr Date: Thu, 11 Dec 2025 03:07:01 -0800 Subject: [PATCH 16/72] [docs] Improve import model copy and examples (#7339) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Updates user-facing copy in the import model feature for clarity and better examples. ## Changes - **Example Link**: Changed from direct download URL to model page URL (easier to find and copy) - **Success Message**: Removed emoji for more professional tone - **Support Documentation**: Updated Civitai link to include `/models` path 🤖 Generated with [Claude Code](https://claude.com/claude-code) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7339-docs-Improve-import-model-copy-and-examples-2c66d73d365081268cbfcae2910f3d7c) by [Unito](https://www.unito.io) Co-authored-by: Claude --- src/locales/en/main.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 08ffee3ee4..d348d6477e 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -2231,7 +2231,7 @@ "baseModels": "Base models", "browseAssets": "Browse Assets", "checkpoints": "Checkpoints", - "civitaiLinkExample": "Example: https://civitai.com/api/download/models/833921?type=Model&format=SafeTensor", + "civitaiLinkExample": "Example: https://civitai.com/models/10706/luisap-z-image-and-qwen-pixel-art-refiner?modelVersionId=2225295", "civitaiLinkLabel": "Civitai model download link", "civitaiLinkPlaceholder": "Paste link here", "confirmModelDetails": "Confirm Model Details", @@ -2257,7 +2257,7 @@ "modelNamePlaceholder": "Enter a name for this model", "modelTypeSelectorLabel": "What type of model is this?", "modelTypeSelectorPlaceholder": "Select model type", - "modelUploaded": "Model imported! 🎉", + "modelUploaded": "Model successfully imported.", "noAssetsFound": "No assets found", "noModelsInFolder": "No {type} available in this folder", "notSureLeaveAsIs": "Not sure? Just leave this as is", @@ -2287,7 +2287,7 @@ "uploadingModel": "Importing model...", "uploadModel": "Import", "uploadModelDescription1": "Paste a Civitai model download link to add it to your library.", - "uploadModelDescription2": "Only links from https://civitai.com are supported at the moment", + "uploadModelDescription2": "Only links from https://civitai.com/models are supported at the moment", "uploadModelDescription3": "Max file size: 1 GB", "uploadModelFailedToRetrieveMetadata": "Failed to retrieve metadata. Please check the link and try again.", "uploadModelFromCivitai": "Import a model from Civitai", From a7de97470b423e9bae3c3d33af1ab73045774be6 Mon Sep 17 00:00:00 2001 From: Comfy Org PR Bot Date: Thu, 11 Dec 2025 20:13:27 +0900 Subject: [PATCH 17/72] 1.35.2 (#7365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch version increment to 1.35.2 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7365-1-35-2-2c66d73d365081198874ca2695162232) by [Unito](https://www.unito.io) --------- Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com> Co-authored-by: github-actions --- package.json | 2 +- src/locales/en/main.json | 4 ++-- src/locales/en/nodeDefs.json | 28 ++++++++++++++++++++++++++++ src/locales/en/settings.json | 2 +- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 746ae4efa2..2e492bd0d5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@comfyorg/comfyui-frontend", "private": true, - "version": "1.35.1", + "version": "1.35.2", "type": "module", "repository": "https://github.com/Comfy-Org/ComfyUI_frontend", "homepage": "https://comfy.org", diff --git a/src/locales/en/main.json b/src/locales/en/main.json index d348d6477e..575c5ca8c6 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -2017,7 +2017,7 @@ "changeTo": "Change to {plan}", "credits": { "standard": "4,200", - "creator": "7,400", + "creator": "7,400", "pro": "21,100" }, "maxDuration": { @@ -2426,4 +2426,4 @@ "recentReleases": "Recent releases", "helpCenterMenu": "Help Center Menu" } -} +} \ No newline at end of file diff --git a/src/locales/en/nodeDefs.json b/src/locales/en/nodeDefs.json index f1bcfbbc81..95c3c1d6f4 100644 --- a/src/locales/en/nodeDefs.json +++ b/src/locales/en/nodeDefs.json @@ -4593,6 +4593,34 @@ } } }, + "KlingOmniProImageNode": { + "display_name": "Kling Omni Image (Pro)", + "description": "Create or edit images with the latest model from Kling.", + "inputs": { + "model_name": { + "name": "model_name" + }, + "prompt": { + "name": "prompt", + "tooltip": "A text prompt describing the image content. This can include both positive and negative descriptions." + }, + "resolution": { + "name": "resolution" + }, + "aspect_ratio": { + "name": "aspect_ratio" + }, + "reference_images": { + "name": "reference_images", + "tooltip": "Up to 10 additional reference images." + } + }, + "outputs": { + "0": { + "tooltip": null + } + } + }, "KlingOmniProImageToVideoNode": { "display_name": "Kling Omni Image to Video (Pro)", "description": "Use up to 7 reference images to generate a video with the latest Kling model.", diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index 31bda257ab..9c9e105b79 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -440,4 +440,4 @@ "pysssss_SnapToGrid": { "name": "Always snap to grid" } -} +} \ No newline at end of file From 8e28dda85c2b92fcb788316d3535caa834d663a2 Mon Sep 17 00:00:00 2001 From: Taehoon Kim <144179915+taehk98@users.noreply.github.com> Date: Thu, 11 Dec 2025 04:29:28 -0700 Subject: [PATCH 18/72] fix: unpacking a missing node causes it to disappear (#7341) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes the issue where unpacking a subgraph containing missing nodes causes those nodes to disappear. Missing nodes are now automatically restored as placeholder nodes that preserve their original data, allowing them to be recovered when the node types are installed later. ## Changes - **What**: - Modified `multiClone()` to preserve missing nodes as serialized data when creating subgraphs - Added `skipMissingNodes` option to `unpackSubgraph()` method to restore missing nodes as placeholder nodes instead of throwing errors - Updated `useSubgraphOperations.unpackSubgraph()` to automatically restore missing nodes as placeholders (removed confirmation dialog) - Replaced deprecated `LiteGraph.cloneObject()` with `structuredClone()` - Removed unused i18n keys and debugging logs ## Review Focus - **Placeholder node restoration**: Missing nodes are restored using the same mechanism as `LGraph.configure()` (creating `LGraphNode` with `last_serialization` and `has_errors` flags). This ensures compatibility with the existing missing node manager. - **Performance**: Optimized `getMissingNodeTypes()` to check `registered_node_types` first before attempting node creation, and uses Set for O(1) duplicate checking. - **Data preservation**: Missing nodes preserve their original type, title, and serialized data in `last_serialization`, allowing automatic recovery when node types are installed. - **Backward compatibility**: The `skipMissingNodes` option defaults to `false`, maintaining original behavior for other code paths. Only the UI-level `unpackSubgraph()` always uses `skipMissingNodes: true`. ## Demo Before: https://github.com/user-attachments/assets/e0327d05-802d-4a64-a9db-4d174e185d82 After: https://github.com/user-attachments/assets/37ab3140-0ada-480e-b9d5-fef8856f8b27 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7341-fix-unpacking-a-missing-node-causes-it-to-disappear-2c66d73d36508151ac6be70a7b2bc56d) by [Unito](https://www.unito.io) --- .../graph/useSubgraphOperations.ts | 27 +++++++++++-------- src/composables/useCoreCommands.ts | 13 +++------ src/lib/litegraph/src/LGraph.ts | 24 ++++++++++++++--- .../litegraph/src/subgraph/subgraphUtils.ts | 4 ++- src/services/litegraphService.ts | 5 ++-- 5 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/composables/graph/useSubgraphOperations.ts b/src/composables/graph/useSubgraphOperations.ts index b42cc2ec4c..45a665d4b4 100644 --- a/src/composables/graph/useSubgraphOperations.ts +++ b/src/composables/graph/useSubgraphOperations.ts @@ -37,6 +37,21 @@ export function useSubgraphOperations() { workflowStore.activeWorkflow?.changeTracker?.checkState() } + const doUnpack = ( + subgraphNodes: SubgraphNode[], + skipMissingNodes: boolean + ) => { + const canvas = canvasStore.getCanvas() + const graph = canvas.subgraph ?? canvas.graph + if (!graph) return + + for (const subgraphNode of subgraphNodes) { + nodeOutputStore.revokeSubgraphPreviews(subgraphNode) + graph.unpackSubgraph(subgraphNode, { skipMissingNodes }) + } + workflowStore.activeWorkflow?.changeTracker?.checkState() + } + const unpackSubgraph = () => { const canvas = canvasStore.getCanvas() const graph = canvas.subgraph ?? canvas.graph @@ -53,17 +68,7 @@ export function useSubgraphOperations() { if (subgraphNodes.length === 0) { return } - - subgraphNodes.forEach((subgraphNode) => { - // Revoke any image previews for the subgraph - nodeOutputStore.revokeSubgraphPreviews(subgraphNode) - - // Unpack the subgraph - graph.unpackSubgraph(subgraphNode) - }) - - // Trigger change tracking - workflowStore.activeWorkflow?.changeTracker?.checkState() + doUnpack(subgraphNodes, true) } const addSubgraphToLibrary = async () => { diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index 1cbfec9091..b582a0b641 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -1,6 +1,7 @@ import { useCurrentUser } from '@/composables/auth/useCurrentUser' import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems' +import { useSubgraphOperations } from '@/composables/graph/useSubgraphOperations' import { useExternalLink } from '@/composables/useExternalLink' import { useModelSelectorDialog } from '@/composables/useModelSelectorDialog' import { @@ -14,7 +15,6 @@ import { LGraphGroup, LGraphNode, LiteGraph, - SubgraphNode } from '@/lib/litegraph/src/litegraph' import type { Point } from '@/lib/litegraph/src/litegraph' import { useAssetBrowserDialog } from '@/platform/assets/composables/useAssetBrowserDialog' @@ -41,7 +41,6 @@ import { useLitegraphService } from '@/services/litegraphService' import type { ComfyCommand } from '@/stores/commandStore' import { useExecutionStore } from '@/stores/executionStore' import { useHelpCenterStore } from '@/stores/helpCenterStore' -import { useNodeOutputStore } from '@/stores/imagePreviewStore' import { useQueueSettingsStore, useQueueStore } from '@/stores/queueStore' import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore' import { useSubgraphStore } from '@/stores/subgraphStore' @@ -1010,14 +1009,8 @@ export function useCoreCommands(): ComfyCommand[] { label: 'Unpack the selected Subgraph', versionAdded: '1.26.3', function: () => { - const canvas = canvasStore.getCanvas() - const graph = canvas.subgraph ?? canvas.graph - if (!graph) throw new TypeError('Canvas has no graph or subgraph set.') - - const subgraphNode = app.canvas.selectedItems.values().next().value - if (!(subgraphNode instanceof SubgraphNode)) return - useNodeOutputStore().revokeSubgraphPreviews(subgraphNode) - graph.unpackSubgraph(subgraphNode) + const { unpackSubgraph } = useSubgraphOperations() + unpackSubgraph() } }, { diff --git a/src/lib/litegraph/src/LGraph.ts b/src/lib/litegraph/src/LGraph.ts index d5d6c18206..478a2a7847 100644 --- a/src/lib/litegraph/src/LGraph.ts +++ b/src/lib/litegraph/src/LGraph.ts @@ -1726,9 +1726,15 @@ export class LGraph return { subgraph, node: subgraphNode as SubgraphNode } } - unpackSubgraph(subgraphNode: SubgraphNode) { + unpackSubgraph( + subgraphNode: SubgraphNode, + options?: { skipMissingNodes?: boolean } + ) { if (!(subgraphNode instanceof SubgraphNode)) throw new Error('Can only unpack Subgraph Nodes') + + const skipMissingNodes = options?.skipMissingNodes ?? false + this.beforeChange() //NOTE: Create bounds can not be called on positionables directly as the subgraph is not being displayed and boundingRect is not initialized. //NOTE: NODE_TITLE_HEIGHT is explicitly excluded here @@ -1750,9 +1756,21 @@ export class LGraph const movedNodes = multiClone(subgraphNode.subgraph.nodes) const nodeIdMap = new Map() for (const n_info of movedNodes) { - const node = LiteGraph.createNode(String(n_info.type), n_info.title) + let node = LiteGraph.createNode(String(n_info.type), n_info.title) if (!node) { - throw new Error('Node not found') + if (skipMissingNodes) { + console.warn( + `Cannot unpack node of type "${n_info.type}" - node type not found. Creating placeholder node.` + ) + node = new LGraphNode(n_info.title || n_info.type || 'Missing Node') + node.last_serialization = n_info + node.has_errors = true + node.type = String(n_info.type) + } else { + throw new Error( + `Cannot unpack: node type "${n_info.type}" is not registered` + ) + } } nodeIdMap.set(n_info.id, ++this.last_node_id) diff --git a/src/lib/litegraph/src/subgraph/subgraphUtils.ts b/src/lib/litegraph/src/subgraph/subgraphUtils.ts index 70ffc7a5d2..38f74efaad 100644 --- a/src/lib/litegraph/src/subgraph/subgraphUtils.ts +++ b/src/lib/litegraph/src/subgraph/subgraphUtils.ts @@ -221,11 +221,13 @@ export function multiClone(nodes: Iterable): ISerialisedNode[] { const newNode = LiteGraph.createNode(node.type) if (!newNode) { console.warn('Failed to create node', node.type) + const serializedData = structuredClone(node.serialize()) + clonedNodes.push(serializedData) continue } // Must be cloned; litegraph "serialize" is mostly shallow clone - const data = LiteGraph.cloneObject(node.serialize()) + const data = structuredClone(node.serialize()) newNode.configure(data) clonedNodes.push(newNode.serialize()) diff --git a/src/services/litegraphService.ts b/src/services/litegraphService.ts index 7cc451c032..8ad39f84a5 100644 --- a/src/services/litegraphService.ts +++ b/src/services/litegraphService.ts @@ -2,6 +2,7 @@ import _ from 'es-toolkit/compat' import { downloadFile } from '@/base/common/downloadUtil' import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems' +import { useSubgraphOperations } from '@/composables/graph/useSubgraphOperations' import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage' import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview' import { useNodeImage, useNodeVideo } from '@/composables/node/useNodeImage' @@ -661,8 +662,8 @@ export const useLitegraphService = () => { { content: 'Unpack Subgraph', callback: () => { - useNodeOutputStore().revokeSubgraphPreviews(this) - this.graph.unpackSubgraph(this) + const { unpackSubgraph } = useSubgraphOperations() + unpackSubgraph() } } ) From a65e63a322ac93550f04bac8702508a74a01ca46 Mon Sep 17 00:00:00 2001 From: Yourz Date: Thu, 11 Dec 2025 20:48:22 +0800 Subject: [PATCH 19/72] fix: throttle sign-up and sign-in button (#7358) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Throttle signup button to prevent duplicate Firebase accounts ## Changes - **What**: - Add throttle to SignUp Button in SignUpForm component - Add throttle to SignIn Button in SignInForm component - Add loading state to SignUp Button in SignUpForm component ## Review Focus related to this Notion page: https://www.notion.so/comfy-org/Implement-Throttle-signup-button-to-prevent-duplicate-Firebase-accounts-2c46d73d36508193a8d1fb5146674956 ## Screenshots (if applicable) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7358-fix-throttle-sign-up-and-sign-in-button-2c66d73d365081278f4dde0f34d60153) by [Unito](https://www.unito.io) --- src/components/dialog/content/signin/SignInForm.vue | 7 ++++--- src/components/dialog/content/signin/SignUpForm.vue | 12 ++++++++++-- .../subscription/composables/useSubscription.ts | 4 ++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/dialog/content/signin/SignInForm.vue b/src/components/dialog/content/signin/SignInForm.vue index 1560911e09..23f955b71c 100644 --- a/src/components/dialog/content/signin/SignInForm.vue +++ b/src/components/dialog/content/signin/SignInForm.vue @@ -60,7 +60,7 @@ - +