From e8b45204f2922fe46d2b25c0f0b0894f13e926a8 Mon Sep 17 00:00:00 2001
From: Christian Byrne
Date: Tue, 20 Jan 2026 14:22:25 -0800
Subject: [PATCH 01/49] feat(panel): add collapsible Advanced Inputs section
for widgets marked advanced (#8146)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds a collapsible 'Advanced Inputs' section to the right-side panel
that displays widgets marked with `options.advanced = true`.
## Changes
- Filters normal widgets to exclude advanced ones
- Adds new `advancedWidgetsSectionDataList` computed for advanced
widgets
- Renders a collapsible section (collapsed by default) for advanced
widgets
## Related
- Backend PR that adds `advanced` flag: comfyanonymous/ComfyUI#11939
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8146-feat-panel-add-collapsible-Advanced-Inputs-section-for-widgets-marked-advanced-2ec6d73d36508120af1af27110a6fb96)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action
Co-authored-by: Rizumu Ayaka
---
.../parameters/TabNormalInputs.vue | 38 ++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/src/components/rightSidePanel/parameters/TabNormalInputs.vue b/src/components/rightSidePanel/parameters/TabNormalInputs.vue
index 30689f1a4..add128876 100644
--- a/src/components/rightSidePanel/parameters/TabNormalInputs.vue
+++ b/src/components/rightSidePanel/parameters/TabNormalInputs.vue
@@ -25,13 +25,31 @@ const widgetsSectionDataList = computed((): NodeWidgetsListList => {
return nodes.map((node) => {
const { widgets = [] } = node
const shownWidgets = widgets
- .filter((w) => !(w.options?.canvasOnly || w.options?.hidden))
+ .filter(
+ (w) =>
+ !(w.options?.canvasOnly || w.options?.hidden || w.options?.advanced)
+ )
.map((widget) => ({ node, widget }))
return { widgets: shownWidgets, node }
})
})
+const advancedWidgetsSectionDataList = computed((): NodeWidgetsListList => {
+ return nodes
+ .map((node) => {
+ const { widgets = [] } = node
+ const advancedWidgets = widgets
+ .filter(
+ (w) =>
+ !(w.options?.canvasOnly || w.options?.hidden) && w.options?.advanced
+ )
+ .map((widget) => ({ node, widget }))
+ return { widgets: advancedWidgets, node }
+ })
+ .filter(({ widgets }) => widgets.length > 0)
+})
+
const isMultipleNodesSelected = computed(
() => widgetsSectionDataList.value.length > 1
)
@@ -56,6 +74,12 @@ const label = computed(() => {
: t('rightSidePanel.inputsNone')
: undefined // SectionWidgets display node titles by default
})
+
+const advancedLabel = computed(() => {
+ return !mustShowNodeTitle && !isMultipleNodesSelected.value
+ ? t('rightSidePanel.advancedInputs')
+ : undefined // SectionWidgets display node titles by default
+})
@@ -93,4 +117,16 @@ const label = computed(() => {
class="border-b border-interface-stroke"
/>
+
+
+
From f5a784e5619dda3e81ca1771650a3edf1e1520c3 Mon Sep 17 00:00:00 2001
From: Christian Byrne
Date: Tue, 20 Jan 2026 15:52:40 -0800
Subject: [PATCH 02/49] fix: add plurilization to node pack count in custom
node manager dialog (#8191)
---
src/locales/en/main.json | 1 +
.../manager/packCard/PackCard.test.ts | 25 ++++++++++++++++---
.../components/manager/packCard/PackCard.vue | 9 ++++---
3 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/src/locales/en/main.json b/src/locales/en/main.json
index fa557ee96..e87121414 100644
--- a/src/locales/en/main.json
+++ b/src/locales/en/main.json
@@ -158,6 +158,7 @@
"choose_file_to_upload": "choose file to upload",
"capture": "capture",
"nodes": "Nodes",
+ "nodesCount": "{count} nodes | {count} node | {count} nodes",
"community": "Community",
"all": "All",
"versionMismatchWarning": "Version Compatibility Warning",
diff --git a/src/workbench/extensions/manager/components/manager/packCard/PackCard.test.ts b/src/workbench/extensions/manager/components/manager/packCard/PackCard.test.ts
index 3e5116739..b3a92e5a3 100644
--- a/src/workbench/extensions/manager/components/manager/packCard/PackCard.test.ts
+++ b/src/workbench/extensions/manager/components/manager/packCard/PackCard.test.ts
@@ -10,15 +10,22 @@ import type {
RegistryPack
} from '@/workbench/extensions/manager/types/comfyManagerTypes'
+const translateMock = vi.hoisted(() =>
+ vi.fn((key: string, choice?: number) =>
+ typeof choice === 'number' ? `${key}-${choice}` : key
+ )
+)
+const dateMock = vi.hoisted(() => vi.fn(() => '2024. 1. 1.'))
+
// Mock dependencies
vi.mock('vue-i18n', () => ({
useI18n: vi.fn(() => ({
- d: vi.fn(() => '2024. 1. 1.'),
- t: vi.fn((key: string) => key)
+ d: dateMock,
+ t: translateMock
})),
createI18n: vi.fn(() => ({
global: {
- t: vi.fn((key: string) => key),
+ t: translateMock,
te: vi.fn(() => true)
}
}))
@@ -187,6 +194,18 @@ describe('PackCard', () => {
// Should still render without errors
expect(wrapper.exists()).toBe(true)
})
+
+ it('should use localized singular/plural nodes label', () => {
+ const packWithNodes = {
+ ...mockNodePack,
+ comfy_nodes: ['node-a']
+ } as MergedNodePack
+
+ const wrapper = createWrapper({ nodePack: packWithNodes })
+
+ expect(wrapper.text()).toContain('g.nodesCount-1')
+ expect(translateMock).toHaveBeenCalledWith('g.nodesCount', 1)
+ })
})
describe('component structure', () => {
diff --git a/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue b/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue
index 6900e71df..17ffdd102 100644
--- a/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue
+++ b/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue
@@ -36,8 +36,8 @@
-
- {{ nodesCount }} {{ $t('g.nodes') }}
+
+ {{ nodesLabel }}
()
-const { d } = useI18n()
+const { d, t } = useI18n()
const colorPaletteStore = useColorPaletteStore()
const isLightTheme = computed(
@@ -115,6 +115,9 @@ const isDisabled = computed(
const nodesCount = computed(() =>
isMergedNodePack(nodePack) ? nodePack.comfy_nodes?.length : undefined
)
+const nodesLabel = computed(() =>
+ nodesCount.value ? t('g.nodesCount', nodesCount.value) : ''
+)
const publisherName = computed(() => {
if (!nodePack) return null
From e6ef99e92ca446bbd869190e725ae2f219ce1a88 Mon Sep 17 00:00:00 2001
From: Simula_r <18093452+simula-r@users.noreply.github.com>
Date: Tue, 20 Jan 2026 16:13:54 -0800
Subject: [PATCH 03/49] feat: add isCloud guard to team workspaces feature flag
(#8192)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Ensures the team_workspaces_enabled feature flag only returns true when
running in cloud environment, preventing the feature from activating in
local/desktop installations.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8192-feat-add-isCloud-guard-to-team-workspaces-feature-flag-2ee6d73d3650810bb1d7c1721ebcdd44)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.5
Co-authored-by: GitHub Action
---
src/composables/useFeatureFlags.ts | 3 +++
.../assets/components/MediaAssetFilterBar.vue | 26 +++++++++++++++----
2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/src/composables/useFeatureFlags.ts b/src/composables/useFeatureFlags.ts
index 136b7ccd1..ca54bb9c6 100644
--- a/src/composables/useFeatureFlags.ts
+++ b/src/composables/useFeatureFlags.ts
@@ -1,5 +1,6 @@
import { computed, reactive, readonly } from 'vue'
+import { isCloud } from '@/platform/distribution/types'
import { remoteConfig } from '@/platform/remoteConfig/remoteConfig'
import { api } from '@/scripts/api'
@@ -95,6 +96,8 @@ export function useFeatureFlags() {
)
},
get teamWorkspacesEnabled() {
+ if (!isCloud) return false
+
return (
remoteConfig.value.team_workspaces_enabled ??
api.getServerFeature(ServerFeatureFlag.TEAM_WORKSPACES_ENABLED, false)
diff --git a/src/platform/assets/components/MediaAssetFilterBar.vue b/src/platform/assets/components/MediaAssetFilterBar.vue
index 0442f5f56..6e4ec5e4c 100644
--- a/src/platform/assets/components/MediaAssetFilterBar.vue
+++ b/src/platform/assets/components/MediaAssetFilterBar.vue
@@ -6,20 +6,36 @@
@update:model-value="handleSearchChange"
/>
-
+
+ @update:media-type-filters="handleMediaTypeFiltersChange"
+ />
-
+
-
+
-
+
From b1dfbfaa09cd5f76ca8c11715a913283dfd4b3c3 Mon Sep 17 00:00:00 2001
From: Alexander Brown
Date: Tue, 20 Jan 2026 16:44:08 -0800
Subject: [PATCH 04/49] chore: Replace prettier with oxfmt (#8177)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Configure oxfmt ignorePatterns to exclude non-JS/TS files (md, json,
css, yaml, etc.) to match previous Prettier behavior.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8177-chore-configure-oxfmt-to-format-only-JS-TS-Vue-files-2ee6d73d3650815080f3cc8a4a932109)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp
---
.claude/commands/setup_repo.md | 2 +-
.github/workflows/ci-lint-format.yaml | 6 +-
.i18nrc.cjs | 18 +-
.oxfmtrc.json | 20 ++
.prettierignore | 2 -
.prettierrc | 11 -
.vscode/extensions.json | 15 +-
AGENTS.md | 8 +-
eslint.config.ts | 6 +-
lint-staged.config.mjs | 25 --
lint-staged.config.ts | 5 +-
package.json | 10 +-
pnpm-lock.yaml | 323 ++++++-------------
pnpm-workspace.yaml | 4 +-
src/composables/useContextMenuTranslation.ts | 3 +-
vite.config.mts | 3 +-
16 files changed, 158 insertions(+), 303 deletions(-)
create mode 100644 .oxfmtrc.json
delete mode 100644 .prettierignore
delete mode 100644 .prettierrc
delete mode 100644 lint-staged.config.mjs
diff --git a/.claude/commands/setup_repo.md b/.claude/commands/setup_repo.md
index d82e22ec6..71dee96a5 100644
--- a/.claude/commands/setup_repo.md
+++ b/.claude/commands/setup_repo.md
@@ -122,7 +122,7 @@ echo " pnpm build - Build for production"
echo " pnpm test:unit - Run unit tests"
echo " pnpm typecheck - Run TypeScript checks"
echo " pnpm lint - Run ESLint"
-echo " pnpm format - Format code with Prettier"
+echo " pnpm format - Format code with oxfmt"
echo ""
echo "Next steps:"
echo "1. Run 'pnpm dev' to start developing"
diff --git a/.github/workflows/ci-lint-format.yaml b/.github/workflows/ci-lint-format.yaml
index 3ce6d6aa9..c97f6255c 100644
--- a/.github/workflows/ci-lint-format.yaml
+++ b/.github/workflows/ci-lint-format.yaml
@@ -42,7 +42,7 @@ jobs:
- name: Run Stylelint with auto-fix
run: pnpm stylelint:fix
- - name: Run Prettier with auto-format
+ - name: Run oxfmt with auto-format
run: pnpm format
- name: Check for changes
@@ -60,7 +60,7 @@ jobs:
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add .
- git commit -m "[automated] Apply ESLint and Prettier fixes"
+ git commit -m "[automated] Apply ESLint and Oxfmt fixes"
git push
- name: Final validation
@@ -80,7 +80,7 @@ jobs:
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
- body: '## 🔧 Auto-fixes Applied\n\nThis PR has been automatically updated to fix linting and formatting issues.\n\n**⚠️ Important**: Your local branch is now behind. Run `git pull` before making additional changes to avoid conflicts.\n\n### Changes made:\n- ESLint auto-fixes\n- Prettier formatting'
+ body: '## 🔧 Auto-fixes Applied\n\nThis PR has been automatically updated to fix linting and formatting issues.\n\n**⚠️ Important**: Your local branch is now behind. Run `git pull` before making additional changes to avoid conflicts.\n\n### Changes made:\n- ESLint auto-fixes\n- Oxfmt formatting'
})
- name: Comment on PR about manual fix needed
diff --git a/.i18nrc.cjs b/.i18nrc.cjs
index 86ce06eaa..4369f0a70 100644
--- a/.i18nrc.cjs
+++ b/.i18nrc.cjs
@@ -1,7 +1,7 @@
// This file is intentionally kept in CommonJS format (.cjs)
// to resolve compatibility issues with dependencies that require CommonJS.
// Do not convert this file to ESModule format unless all dependencies support it.
-const { defineConfig } = require('@lobehub/i18n-cli');
+const { defineConfig } = require('@lobehub/i18n-cli')
module.exports = defineConfig({
modelName: 'gpt-4.1',
@@ -10,7 +10,19 @@ module.exports = defineConfig({
entry: 'src/locales/en',
entryLocale: 'en',
output: 'src/locales',
- outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar', 'tr', 'pt-BR', 'fa'],
+ outputLocales: [
+ 'zh',
+ 'zh-TW',
+ 'ru',
+ 'ja',
+ 'ko',
+ 'fr',
+ 'es',
+ 'ar',
+ 'tr',
+ 'pt-BR',
+ 'fa'
+ ],
reference: `Special names to keep untranslated: flux, photomaker, clip, vae, cfg, stable audio, stable cascade, stable zero, controlnet, lora, HiDream, Civitai, Hugging Face.
'latent' is the short form of 'latent space'.
'mask' is in the context of image processing.
@@ -26,4 +38,4 @@ module.exports = defineConfig({
- Use Arabic-Indic numerals (۰-۹) for numbers where appropriate.
- Maintain consistency with terminology used in Persian software and design applications.
`
-});
+})
diff --git a/.oxfmtrc.json b/.oxfmtrc.json
new file mode 100644
index 000000000..5da4febe2
--- /dev/null
+++ b/.oxfmtrc.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
+ "singleQuote": true,
+ "tabWidth": 2,
+ "semi": false,
+ "trailingComma": "none",
+ "printWidth": 80,
+ "ignorePatterns": [
+ "packages/registry-types/src/comfyRegistryTypes.ts",
+ "src/types/generatedManagerTypes.ts",
+ "**/*.md",
+ "**/*.json",
+ "**/*.css",
+ "**/*.yaml",
+ "**/*.yml",
+ "**/*.html",
+ "**/*.svg",
+ "**/*.xml"
+ ]
+}
diff --git a/.prettierignore b/.prettierignore
deleted file mode 100644
index 4403edd8e..000000000
--- a/.prettierignore
+++ /dev/null
@@ -1,2 +0,0 @@
-packages/registry-types/src/comfyRegistryTypes.ts
-src/types/generatedManagerTypes.ts
diff --git a/.prettierrc b/.prettierrc
deleted file mode 100644
index aa43a43ac..000000000
--- a/.prettierrc
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "singleQuote": true,
- "tabWidth": 2,
- "semi": false,
- "trailingComma": "none",
- "printWidth": 80,
- "importOrder": ["^@core/(.*)$", "", "^@/(.*)$", "^[./]"],
- "importOrderSeparation": true,
- "importOrderSortSpecifiers": true,
- "plugins": ["@prettier/plugin-oxc", "@trivago/prettier-plugin-sort-imports"]
-}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 54f28d400..9cbac42d7 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,25 +1,22 @@
{
"recommendations": [
+ "antfu.vite",
"austenc.tailwind-docs",
"bradlc.vscode-tailwindcss",
"davidanson.vscode-markdownlint",
"dbaeumer.vscode-eslint",
+ "donjayamanne.githistory",
"eamodio.gitlens",
- "esbenp.prettier-vscode",
- "figma.figma-vscode-extension",
"github.vscode-github-actions",
"github.vscode-pull-request-github",
"hbenl.vscode-test-explorer",
+ "kisstkondoros.vscode-codemetrics",
"lokalise.i18n-ally",
"ms-playwright.playwright",
+ "oxc.oxc-vscode",
+ "sonarsource.sonarlint-vscode",
"vitest.explorer",
"vue.volar",
- "sonarsource.sonarlint-vscode",
- "deque-systems.vscode-axe-linter",
- "kisstkondoros.vscode-codemetrics",
- "donjayamanne.githistory",
- "wix.vscode-import-cost",
- "prograhammer.tslint-vue",
- "antfu.vite"
+ "wix.vscode-import-cost"
]
}
diff --git a/AGENTS.md b/AGENTS.md
index da2953783..9938865a9 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -27,10 +27,10 @@ See @docs/guidance/*.md for file-type-specific conventions (auto-loaded by glob)
- Build output: `dist/`
- Configs
- `vite.config.mts`
- - `vitest.config.ts`
- `playwright.config.ts`
- `eslint.config.ts`
- - `.prettierrc`
+ - `.oxfmtrc.json`
+ - `.oxlintrc.json`
- etc.
## Monorepo Architecture
@@ -46,7 +46,7 @@ The project uses **Nx** for build orchestration and task management
- `pnpm test:unit`: Run Vitest unit tests
- `pnpm test:browser`: Run Playwright E2E tests (`browser_tests/`)
- `pnpm lint` / `pnpm lint:fix`: Lint (ESLint)
-- `pnpm format` / `pnpm format:check`: Prettier
+- `pnpm format` / `pnpm format:check`: oxfmt
- `pnpm typecheck`: Vue TSC type checking
- `pnpm storybook`: Start Storybook development server
@@ -72,7 +72,7 @@ The project uses **Nx** for build orchestration and task management
- Composition API only
- Tailwind 4 styling
- Avoid `
diff --git a/src/components/sidebar/SidebarIcon.test.ts b/src/components/sidebar/SidebarIcon.test.ts
index 7564e7bcd..284a29825 100644
--- a/src/components/sidebar/SidebarIcon.test.ts
+++ b/src/components/sidebar/SidebarIcon.test.ts
@@ -1,6 +1,5 @@
import { mount } from '@vue/test-utils'
import PrimeVue from 'primevue/config'
-import OverlayBadge from 'primevue/overlaybadge'
import Tooltip from 'primevue/tooltip'
import { describe, expect, it } from 'vitest'
import { createI18n } from 'vue-i18n'
@@ -33,8 +32,7 @@ describe('SidebarIcon', () => {
return mount(SidebarIcon, {
global: {
plugins: [PrimeVue, i18n],
- directives: { tooltip: Tooltip },
- components: { OverlayBadge }
+ directives: { tooltip: Tooltip }
},
props: { ...exampleProps, ...props },
...options
@@ -54,9 +52,9 @@ describe('SidebarIcon', () => {
it('creates badge when iconBadge prop is set', () => {
const badge = '2'
const wrapper = mountSidebarIcon({ iconBadge: badge })
- const badgeEl = wrapper.findComponent(OverlayBadge)
+ const badgeEl = wrapper.find('.sidebar-icon-badge')
expect(badgeEl.exists()).toBe(true)
- expect(badgeEl.find('.p-badge').text()).toEqual(badge)
+ expect(badgeEl.text()).toEqual(badge)
})
it('shows tooltip on hover', async () => {
diff --git a/src/components/sidebar/SidebarIcon.vue b/src/components/sidebar/SidebarIcon.vue
index 88900c1a7..10dfca8f8 100644
--- a/src/components/sidebar/SidebarIcon.vue
+++ b/src/components/sidebar/SidebarIcon.vue
@@ -17,22 +17,28 @@
>
-
+
{{
t(label)
@@ -42,7 +48,6 @@
- {{ label }}
+
+ {{ label }}
+
diff --git a/src/components/common/statusBadge.variants.ts b/src/components/common/statusBadge.variants.ts
new file mode 100644
index 000000000..479a0dda8
--- /dev/null
+++ b/src/components/common/statusBadge.variants.ts
@@ -0,0 +1,26 @@
+import type { VariantProps } from 'cva'
+import { cva } from 'cva'
+
+export const statusBadgeVariants = cva({
+ base: 'inline-flex items-center justify-center rounded-full',
+ variants: {
+ severity: {
+ default: 'bg-primary-background text-base-foreground',
+ secondary: 'bg-secondary-background text-base-foreground',
+ warn: 'bg-warning-background text-base-background',
+ danger: 'bg-destructive-background text-white',
+ contrast: 'bg-base-foreground text-base-background'
+ },
+ variant: {
+ label: 'h-3.5 px-1 text-xxxs font-semibold uppercase',
+ dot: 'size-2',
+ circle: 'size-3.5 text-xxxs font-semibold'
+ }
+ },
+ defaultVariants: {
+ severity: 'default',
+ variant: 'label'
+ }
+})
+
+export type StatusBadgeVariants = VariantProps
From d9e5b07c738255b2ec38155dab5aa6ac1e689786 Mon Sep 17 00:00:00 2001
From: Jin Yi
Date: Wed, 21 Jan 2026 14:15:58 +0900
Subject: [PATCH 12/49] [bugfix] Clear queue button now properly removes
initializing jobs from UI (#8203)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
- Fix: Initializing jobs now properly disappear from UI when cancelled
or cleared
- Add `clearInitializationByPromptIds` batch function for optimized Set
operations
- Handle Cloud vs local environment correctly (use `api.deleteItem` for
Cloud, `api.interrupt` for local)
## Problem
When clicking 'Clear queue' button or X button on initializing jobs, the
jobs remained visible in both AssetsSidebarListView and JobQueue
components until page refresh.
## Root Cause
1. `initializingPromptIds` in `executionStore` was not being cleared
when jobs were cancelled/deleted
2. Cloud environment requires `api.deleteItem()` instead of
`api.interrupt()` for cancellation
## Changes
- `src/stores/executionStore.ts`: Export `clearInitializationByPromptId`
and add batch `clearInitializationByPromptIds` function
- `src/composables/queue/useJobMenu.ts`: Add Cloud branch handling and
initialization cleanup
- `src/components/queue/QueueProgressOverlay.vue`: Fix `onCancelItem()`,
`cancelQueuedWorkflows()`, `interruptAll()`
- `src/components/sidebar/tabs/AssetsSidebarTab.vue`: Add initialization
cleanup to `handleClearQueue()`
[screen-capture.webm](https://github.com/user-attachments/assets/0bf911c2-d8f4-427c-96e0-4784e8fe0f08)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8203-bugfix-Clear-queue-button-now-properly-removes-initializing-jobs-from-UI-2ef6d73d36508162a55bd84ad39ab49c)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.5
---
src/components/queue/QueueProgressOverlay.vue | 20 ++++++++++++++++++-
.../sidebar/tabs/AssetsSidebarTab.vue | 8 ++++++++
src/composables/queue/useJobMenu.test.ts | 14 ++++++++++++-
src/composables/queue/useJobMenu.ts | 10 +++++++++-
src/stores/executionStore.ts | 14 +++++++++++++
5 files changed, 63 insertions(+), 3 deletions(-)
diff --git a/src/components/queue/QueueProgressOverlay.vue b/src/components/queue/QueueProgressOverlay.vue
index d6b3edcd8..626709593 100644
--- a/src/components/queue/QueueProgressOverlay.vue
+++ b/src/components/queue/QueueProgressOverlay.vue
@@ -200,7 +200,13 @@ const onCancelItem = wrapWithErrorHandlingAsync(async (item: JobListItem) => {
if (item.state === 'running' || item.state === 'initialization') {
// Running/initializing jobs: interrupt execution
- await api.interrupt(promptId)
+ // Cloud backend uses deleteItem, local uses interrupt
+ if (isCloud) {
+ await api.deleteItem('queue', promptId)
+ } else {
+ await api.interrupt(promptId)
+ }
+ executionStore.clearInitializationByPromptId(promptId)
await queueStore.update()
} else if (item.state === 'pending') {
// Pending jobs: remove from queue
@@ -268,7 +274,15 @@ const inspectJobAsset = wrapWithErrorHandlingAsync(
)
const cancelQueuedWorkflows = wrapWithErrorHandlingAsync(async () => {
+ // Capture pending promptIds before clearing
+ const pendingPromptIds = queueStore.pendingTasks
+ .map((task) => task.promptId)
+ .filter((id): id is string => typeof id === 'string' && id.length > 0)
+
await commandStore.execute('Comfy.ClearPendingTasks')
+
+ // Clear initialization state for removed prompts
+ executionStore.clearInitializationByPromptIds(pendingPromptIds)
})
const interruptAll = wrapWithErrorHandlingAsync(async () => {
@@ -284,10 +298,14 @@ const interruptAll = wrapWithErrorHandlingAsync(async () => {
// on cloud to ensure we cancel the workflow the user clicked.
if (isCloud) {
await Promise.all(promptIds.map((id) => api.deleteItem('queue', id)))
+ executionStore.clearInitializationByPromptIds(promptIds)
+ await queueStore.update()
return
}
await Promise.all(promptIds.map((id) => api.interrupt(id)))
+ executionStore.clearInitializationByPromptIds(promptIds)
+ await queueStore.update()
})
const showClearHistoryDialog = () => {
diff --git a/src/components/sidebar/tabs/AssetsSidebarTab.vue b/src/components/sidebar/tabs/AssetsSidebarTab.vue
index d481b0b9a..8db99e96f 100644
--- a/src/components/sidebar/tabs/AssetsSidebarTab.vue
+++ b/src/components/sidebar/tabs/AssetsSidebarTab.vue
@@ -244,6 +244,7 @@ import { useSettingStore } from '@/platform/settings/settingStore'
import { getJobDetail } from '@/services/jobOutputCache'
import { useCommandStore } from '@/stores/commandStore'
import { useDialogStore } from '@/stores/dialogStore'
+import { useExecutionStore } from '@/stores/executionStore'
import { ResultItemImpl, useQueueStore } from '@/stores/queueStore'
import { formatDuration, getMediaTypeFromFilename } from '@/utils/formatUtil'
import { cn } from '@/utils/tailwindUtil'
@@ -257,6 +258,7 @@ interface JobOutputItem {
const { t, n } = useI18n()
const commandStore = useCommandStore()
const queueStore = useQueueStore()
+const executionStore = useExecutionStore()
const settingStore = useSettingStore()
const activeTab = ref<'input' | 'output'>('output')
@@ -511,7 +513,13 @@ const handleBulkDelete = async (assets: AssetItem[]) => {
}
const handleClearQueue = async () => {
+ const pendingPromptIds = queueStore.pendingTasks
+ .map((task) => task.promptId)
+ .filter((id): id is string => typeof id === 'string' && id.length > 0)
+
await commandStore.execute('Comfy.ClearPendingTasks')
+
+ executionStore.clearInitializationByPromptIds(pendingPromptIds)
}
const handleBulkAddToWorkflow = async (assets: AssetItem[]) => {
diff --git a/src/composables/queue/useJobMenu.test.ts b/src/composables/queue/useJobMenu.test.ts
index 1b339d09c..9913b3b3e 100644
--- a/src/composables/queue/useJobMenu.test.ts
+++ b/src/composables/queue/useJobMenu.test.ts
@@ -5,6 +5,10 @@ import type { Ref } from 'vue'
import type { JobListItem } from '@/composables/queue/useJobList'
import type { MenuEntry } from '@/composables/queue/useJobMenu'
+vi.mock('@/platform/distribution/types', () => ({
+ isCloud: false
+}))
+
const downloadFileMock = vi.fn()
vi.mock('@/base/common/downloadUtil', () => ({
downloadFile: (...args: any[]) => downloadFileMock(...args)
@@ -55,7 +59,8 @@ const workflowStoreMock = {
createTemporary: vi.fn()
}
vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
- useWorkflowStore: () => workflowStoreMock
+ useWorkflowStore: () => workflowStoreMock,
+ ComfyWorkflow: class {}
}))
const interruptMock = vi.fn()
@@ -104,6 +109,13 @@ vi.mock('@/stores/queueStore', () => ({
useQueueStore: () => queueStoreMock
}))
+const executionStoreMock = {
+ clearInitializationByPromptId: vi.fn()
+}
+vi.mock('@/stores/executionStore', () => ({
+ useExecutionStore: () => executionStoreMock
+}))
+
const getJobWorkflowMock = vi.fn()
vi.mock('@/services/jobOutputCache', () => ({
getJobWorkflow: (...args: any[]) => getJobWorkflowMock(...args)
diff --git a/src/composables/queue/useJobMenu.ts b/src/composables/queue/useJobMenu.ts
index b4208304e..b50a24846 100644
--- a/src/composables/queue/useJobMenu.ts
+++ b/src/composables/queue/useJobMenu.ts
@@ -6,6 +6,7 @@ import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
import { st, t } from '@/i18n'
import { mapTaskOutputToAssetItem } from '@/platform/assets/composables/media/assetMappers'
import { useMediaAssetActions } from '@/platform/assets/composables/useMediaAssetActions'
+import { isCloud } from '@/platform/distribution/types'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
@@ -15,6 +16,7 @@ import { downloadBlob } from '@/scripts/utils'
import { useDialogService } from '@/services/dialogService'
import { getJobWorkflow } from '@/services/jobOutputCache'
import { useLitegraphService } from '@/services/litegraphService'
+import { useExecutionStore } from '@/stores/executionStore'
import { useNodeDefStore } from '@/stores/nodeDefStore'
import { useQueueStore } from '@/stores/queueStore'
import type { ResultItemImpl, TaskItemImpl } from '@/stores/queueStore'
@@ -44,6 +46,7 @@ export function useJobMenu(
const workflowStore = useWorkflowStore()
const workflowService = useWorkflowService()
const queueStore = useQueueStore()
+ const executionStore = useExecutionStore()
const { copyToClipboard } = useCopyToClipboard()
const litegraphService = useLitegraphService()
const nodeDefStore = useNodeDefStore()
@@ -72,10 +75,15 @@ export function useJobMenu(
const target = resolveItem(item)
if (!target) return
if (target.state === 'running' || target.state === 'initialization') {
- await api.interrupt(target.id)
+ if (isCloud) {
+ await api.deleteItem('queue', target.id)
+ } else {
+ await api.interrupt(target.id)
+ }
} else if (target.state === 'pending') {
await api.deleteItem('queue', target.id)
}
+ executionStore.clearInitializationByPromptId(target.id)
await queueStore.update()
}
diff --git a/src/stores/executionStore.ts b/src/stores/executionStore.ts
index 429adb807..dda732af5 100644
--- a/src/stores/executionStore.ts
+++ b/src/stores/executionStore.ts
@@ -425,6 +425,18 @@ export const useExecutionStore = defineStore('execution', () => {
initializingPromptIds.value = next
}
+ function clearInitializationByPromptIds(promptIds: string[]) {
+ if (!promptIds.length) return
+ const current = initializingPromptIds.value
+ const toRemove = promptIds.filter((id) => current.has(id))
+ if (!toRemove.length) return
+ const next = new Set(current)
+ for (const id of toRemove) {
+ next.delete(id)
+ }
+ initializingPromptIds.value = next
+ }
+
function isPromptInitializing(
promptId: string | number | undefined
): boolean {
@@ -650,6 +662,8 @@ export const useExecutionStore = defineStore('execution', () => {
runningWorkflowCount,
initializingPromptIds,
isPromptInitializing,
+ clearInitializationByPromptId,
+ clearInitializationByPromptIds,
bindExecutionEvents,
unbindExecutionEvents,
storePrompt,
From c7b5f4705579feefef28cfaf8f938aad504bd324 Mon Sep 17 00:00:00 2001
From: Christian Byrne
Date: Tue, 20 Jan 2026 22:02:16 -0800
Subject: [PATCH 13/49] feat(canvas): hide widgets marked advanced unless
node.showAdvanced is true (#8147)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Hides widgets marked with `options.advanced = true` on the Vue Node
canvas unless `node.showAdvanced` is true.
## Changes
- Updates `NodeWidgets.vue` template to check `widget.options.advanced`
combined with `nodeData.showAdvanced`
- Updates `gridTemplateRows` computed to exclude hidden advanced widgets
- Adds `showAdvanced` to `VueNodeData` interface in
`useGraphNodeManager.ts`
## Related
- Backend PR that adds `advanced` flag: comfyanonymous/ComfyUI#11939
- Toggle button PR: feat/advanced-widgets-toggle-button
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8147-feat-canvas-hide-widgets-marked-advanced-unless-node-showAdvanced-is-true-2ec6d73d36508179931ce78a6ffd6b0a)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action
---
src/composables/graph/useGraphNodeManager.ts | 11 ++++++++++-
src/lib/litegraph/src/LGraphNodeProperties.ts | 4 +++-
.../extensions/vueNodes/components/NodeWidgets.vue | 12 ++++++++++--
3 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/src/composables/graph/useGraphNodeManager.ts b/src/composables/graph/useGraphNodeManager.ts
index db9173058..33822d1c5 100644
--- a/src/composables/graph/useGraphNodeManager.ts
+++ b/src/composables/graph/useGraphNodeManager.ts
@@ -77,6 +77,7 @@ export interface VueNodeData {
outputs?: INodeOutputSlot[]
resizable?: boolean
shape?: number
+ showAdvanced?: boolean
subgraphId?: string | null
titleMode?: TitleMode
widgets?: SafeWidgetData[]
@@ -314,7 +315,8 @@ export function extractVueNodeData(node: LGraphNode): VueNodeData {
color: node.color || undefined,
bgcolor: node.bgcolor || undefined,
resizable: node.resizable,
- shape: node.shape
+ shape: node.shape,
+ showAdvanced: node.showAdvanced
}
}
@@ -568,6 +570,13 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
? propertyEvent.newValue
: undefined
})
+ break
+ case 'showAdvanced':
+ vueNodeData.set(nodeId, {
+ ...currentData,
+ showAdvanced: Boolean(propertyEvent.newValue)
+ })
+ break
}
}
},
diff --git a/src/lib/litegraph/src/LGraphNodeProperties.ts b/src/lib/litegraph/src/LGraphNodeProperties.ts
index ae51d3739..9df938298 100644
--- a/src/lib/litegraph/src/LGraphNodeProperties.ts
+++ b/src/lib/litegraph/src/LGraphNodeProperties.ts
@@ -13,7 +13,9 @@ const DEFAULT_TRACKED_PROPERTIES: string[] = [
'flags.pinned',
'mode',
'color',
- 'bgcolor'
+ 'bgcolor',
+ 'shape',
+ 'showAdvanced'
]
/**
* Manages node properties with optional change tracking and instrumentation.
diff --git a/src/renderer/extensions/vueNodes/components/NodeWidgets.vue b/src/renderer/extensions/vueNodes/components/NodeWidgets.vue
index 9975ec864..ff47510e6 100644
--- a/src/renderer/extensions/vueNodes/components/NodeWidgets.vue
+++ b/src/renderer/extensions/vueNodes/components/NodeWidgets.vue
@@ -25,7 +25,10 @@
:key="`widget-${index}-${widget.name}`"
>