Compare commits

...

4 Commits

Author SHA1 Message Date
DrJKL
52d2c8b483 refactor: alias createUuidv4 to the uuid library instead of a hand-rolled impl
Re-export uuid's v4 as createUuidv4, deleting the hand-rolled
crypto/Math.random implementation. Keeps the createUuidv4 name, UUID type,
and zeroUuid so litegraph's public API (LiteGraph.uuidv4, exported
createUuidv4) and the extension ecosystem are unaffected. uuid's v4 uses
crypto.getRandomValues, so it keeps working in non-secure contexts (LAN
HTTP) where crypto.randomUUID would not.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 19:04:17 -07:00
DrJKL
434e6534fb refactor: remove dead comments and an internal barrel file
- Drop commented-out IMAGE_EXCLUDED_PROPS block in widgetPropFilter.
- Drop commented-out code in useSelectionOperations and LGraphCanvas.
- Remove src/components/ui/toggle-group/index.ts barrel; import the two
  components directly per the no-internal-barrels rule.

Left src/types/nodeSource.ts: it re-exports an external package as a stable
internal alias for 15 call sites, not an internal barrel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 18:47:57 -07:00
DrJKL
1cacae1b34 refactor: inline pure-derived aboutPanel and topbarBadge stores
Both stores held no own state, only a computed deriving badges from
extensionStore. Each had a single consumer, so the derivation moves into
the consuming component and the stores are deleted.

Left useActionBarButtonStore and useHelpCenterStore (shared across 2+
consumers) and useNodeHelpStore (dedicated test) as stores.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 18:47:56 -07:00
DrJKL
85992db615 refactor: use structuredClone for plain workflow-JSON deep copies
Replace JSON.parse(JSON.stringify(x)) with structuredClone(x) at the two
typed plain-JSON sites (migrateReroute, workflowStore). Left the generic
clone() helpers, LGraphNode widget-value clone, and LiteGraphGlobal.cloneObject
on JSON-clone: those strip non-serializable refs (proxies/functions) on
purpose and structuredClone would throw. workflowService.activeState stays
JSON-cloned because its real type is nullable and relied on the any from
JSON.parse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 18:47:56 -07:00
16 changed files with 94 additions and 154 deletions

View File

@@ -5,7 +5,7 @@
</h2>
<div class="space-y-2">
<a
v-for="badge in aboutPanelStore.badges"
v-for="badge in badges"
:key="badge.url"
:href="badge.url"
target="_blank"
@@ -34,11 +34,75 @@
<script setup lang="ts">
import Divider from 'primevue/divider'
import Tag from 'primevue/tag'
import { computed } from 'vue'
import SystemStatsPanel from '@/components/common/SystemStatsPanel.vue'
import { useAboutPanelStore } from '@/stores/aboutPanelStore'
import { useExternalLink } from '@/composables/useExternalLink'
import { isCloud, isDesktop } from '@/platform/distribution/types'
import { useExtensionStore } from '@/stores/extensionStore'
import { useSystemStatsStore } from '@/stores/systemStatsStore'
import type { AboutPageBadge } from '@/types/comfy'
import { electronAPI } from '@/utils/envUtil'
import { formatCommitHash } from '@/utils/formatUtil'
const systemStatsStore = useSystemStatsStore()
const aboutPanelStore = useAboutPanelStore()
const extensionStore = useExtensionStore()
const { staticUrls } = useExternalLink()
const frontendVersion = __COMFYUI_FRONTEND_VERSION__
const coreVersion = computed(
() => systemStatsStore?.systemStats?.system?.comfyui_version ?? ''
)
const templatesVersion = computed(
() => systemStatsStore?.systemStats?.system?.installed_templates_version ?? ''
)
const requiredTemplatesVersion = computed(
() => systemStatsStore?.systemStats?.system?.required_templates_version ?? ''
)
const isTemplatesOutdated = computed(
() =>
templatesVersion.value !== '' &&
requiredTemplatesVersion.value !== '' &&
templatesVersion.value !== requiredTemplatesVersion.value
)
const coreBadges = computed<AboutPageBadge[]>(() => [
// In electron, ComfyUI is packaged without the git repo, so the python
// server's API doesn't have the version info.
{
label: `ComfyUI ${
isDesktop
? 'v' + electronAPI().getComfyUIVersion()
: formatCommitHash(coreVersion.value)
}`,
url: isCloud ? staticUrls.comfyOrg : staticUrls.github,
icon: isCloud ? 'pi pi-cloud' : 'pi pi-github'
},
{
label: `ComfyUI_frontend v${frontendVersion}`,
url: staticUrls.githubFrontend,
icon: 'pi pi-github'
},
...(templatesVersion.value
? [
{
label: `Templates v${templatesVersion.value}`,
url: 'https://pypi.org/project/comfyui-workflow-templates/',
icon: 'pi pi-book',
...(isTemplatesOutdated.value ? { severity: 'danger' as const } : {})
}
]
: []),
{
label: 'Discord',
url: staticUrls.discord,
icon: 'pi pi-discord'
},
{ label: 'ComfyOrg', url: staticUrls.comfyOrg, icon: 'pi pi-globe' }
])
const badges = computed<AboutPageBadge[]>(() => [
...coreBadges.value,
...extensionStore.extensions.flatMap((e) => e.aboutPageBadges ?? [])
])
</script>

View File

@@ -51,7 +51,8 @@
<script setup lang="ts">
import Button from '@/components/ui/button/Button.vue'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
import ToggleGroup from '@/components/ui/toggle-group/ToggleGroup.vue'
import ToggleGroupItem from '@/components/ui/toggle-group/ToggleGroupItem.vue'
import type { GizmoMode } from '@/extensions/core/load3d/interfaces'
const gizmoEnabled = defineModel<boolean>('gizmoEnabled')

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex h-full shrink-0 items-center">
<TopbarBadge
v-for="badge in topbarBadgeStore.badges"
v-for="badge in badges"
:key="badge.text"
:badge
:display-mode="displayMode"
@@ -15,7 +15,8 @@
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
import { computed } from 'vue'
import { useTopbarBadgeStore } from '@/stores/topbarBadgeStore'
import { useExtensionStore } from '@/stores/extensionStore'
import type { TopbarBadge as TopbarBadgeType } from '@/types/comfy'
import TopbarBadge from './TopbarBadge.vue'
@@ -34,5 +35,8 @@ const displayMode = computed<'full' | 'compact' | 'icon-only'>(() => {
return 'icon-only'
})
const topbarBadgeStore = useTopbarBadgeStore()
const extensionStore = useExtensionStore()
const badges = computed<TopbarBadgeType[]>(() =>
extensionStore.extensions.flatMap((e) => e.topbarBadges ?? [])
)
</script>

View File

@@ -1,2 +0,0 @@
export { default as ToggleGroup } from './ToggleGroup.vue'
export { default as ToggleGroupItem } from './ToggleGroupItem.vue'

View File

@@ -1,4 +1,3 @@
// import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems' // Unused for now
import { t } from '@/i18n'
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { useToastStore } from '@/platform/updates/common/toastStore'
@@ -14,7 +13,6 @@ import { useDialogService } from '@/services/dialogService'
* Composable for handling basic selection operations like copy, paste, duplicate, delete, rename
*/
export function useSelectionOperations() {
// const { getSelectedNodes } = useSelectedLiteGraphItems() // Unused for now
const canvasStore = useCanvasStore()
const toastStore = useToastStore()
const dialogService = useDialogService()

View File

@@ -818,8 +818,6 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
options ||= {}
this.options = options
// if(graph === undefined)
// throw ("No graph assigned");
this.background_image = LGraphCanvas.DEFAULT_BACKGROUND_IMAGE
this.ds = new DragAndScale(canvas)
@@ -4178,7 +4176,6 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
}
const { created, nodes, links, reroutes } = results
// const failedNodes: ISerialisedNode[] = []
const subgraphIdMap: Record<string, string> = {}
// SubgraphV2: Remove always-clone behaviour
//Update subgraph ids

View File

@@ -257,7 +257,7 @@ export const useWorkflowStore = defineStore('workflow', () => {
workflowData?: ComfyWorkflowJSON
): ComfyWorkflowJSON => {
const base = workflowData
? (JSON.parse(JSON.stringify(workflowData)) as ComfyWorkflowJSON)
? structuredClone(workflowData)
: (JSON.parse(defaultGraphJSON) as ComfyWorkflowJSON)
if (!base.id) {

View File

@@ -184,7 +184,8 @@
<script setup lang="ts">
import Button from '@/components/ui/button/Button.vue'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
import ToggleGroup from '@/components/ui/toggle-group/ToggleGroup.vue'
import ToggleGroupItem from '@/components/ui/toggle-group/ToggleGroupItem.vue'
import { useSliderFromMouse } from '@/platform/workflow/sharing/composables/useSliderFromMouse'
import type { ThumbnailType } from '@/platform/workflow/sharing/types/comfyHubTypes'
import {

View File

@@ -5,7 +5,8 @@ import { defineComponent } from 'vue'
import { createI18n } from 'vue-i18n'
import { describe, expect, it, vi } from 'vitest'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
import ToggleGroup from '@/components/ui/toggle-group/ToggleGroup.vue'
import ToggleGroupItem from '@/components/ui/toggle-group/ToggleGroupItem.vue'
import type { IWidgetOptions } from '@/lib/litegraph/src/types/widgets'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'

View File

@@ -47,7 +47,8 @@ import ToggleSwitch from 'primevue/toggleswitch'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
import ToggleGroup from '@/components/ui/toggle-group/ToggleGroup.vue'
import ToggleGroupItem from '@/components/ui/toggle-group/ToggleGroupItem.vue'
import type { IWidgetOptions } from '@/lib/litegraph/src/types/widgets'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { useHideLayoutField } from '@/types/widgetTypes'

View File

@@ -108,7 +108,6 @@ The following table lists ALL 46 store instances in the system as of 2026-01-29:
| File | Store | Description | Category |
| ---------------------------- | ----------------------------- | ------------------------------------------------------- | ---------- |
| aboutPanelStore.ts | useAboutPanelStore | Manages the About panel state and badges | UI |
| apiKeyAuthStore.ts | useApiKeyAuthStore | Handles API key authentication | Auth |
| comfyManagerStore.ts | useComfyManagerStore | Manages ComfyUI application state | Core |
| comfyRegistryStore.ts | useComfyRegistryStore | Handles extensions registry | Registry |

View File

@@ -1,81 +0,0 @@
import { defineStore } from 'pinia'
import { computed } from 'vue'
import { useExternalLink } from '@/composables/useExternalLink'
import { isCloud, isDesktop } from '@/platform/distribution/types'
import type { AboutPageBadge } from '@/types/comfy'
import { electronAPI } from '@/utils/envUtil'
import { formatCommitHash } from '@/utils/formatUtil'
import { useExtensionStore } from './extensionStore'
import { useSystemStatsStore } from './systemStatsStore'
export const useAboutPanelStore = defineStore('aboutPanel', () => {
const frontendVersion = __COMFYUI_FRONTEND_VERSION__
const extensionStore = useExtensionStore()
const systemStatsStore = useSystemStatsStore()
const { staticUrls } = useExternalLink()
const coreVersion = computed(
() => systemStatsStore?.systemStats?.system?.comfyui_version ?? ''
)
const templatesVersion = computed(
() =>
systemStatsStore?.systemStats?.system?.installed_templates_version ?? ''
)
const requiredTemplatesVersion = computed(
() =>
systemStatsStore?.systemStats?.system?.required_templates_version ?? ''
)
const isTemplatesOutdated = computed(
() =>
templatesVersion.value !== '' &&
requiredTemplatesVersion.value !== '' &&
templatesVersion.value !== requiredTemplatesVersion.value
)
const coreBadges = computed<AboutPageBadge[]>(() => [
// In electron, the ComfyUI is packaged without the git repo,
// so the python server's API doesn't have the version info.
{
label: `ComfyUI ${
isDesktop
? 'v' + electronAPI().getComfyUIVersion()
: formatCommitHash(coreVersion.value)
}`,
url: isCloud ? staticUrls.comfyOrg : staticUrls.github,
icon: isCloud ? 'pi pi-cloud' : 'pi pi-github'
},
{
label: `ComfyUI_frontend v${frontendVersion}`,
url: staticUrls.githubFrontend,
icon: 'pi pi-github'
},
...(templatesVersion.value
? [
{
label: `Templates v${templatesVersion.value}`,
url: 'https://pypi.org/project/comfyui-workflow-templates/',
icon: 'pi pi-book',
...(isTemplatesOutdated.value
? { severity: 'danger' as const }
: {})
}
]
: []),
{
label: 'Discord',
url: staticUrls.discord,
icon: 'pi pi-discord'
},
{ label: 'ComfyOrg', url: staticUrls.comfyOrg, icon: 'pi pi-globe' }
])
const allBadges = computed<AboutPageBadge[]>(() => [
...coreBadges.value,
...extensionStore.extensions.flatMap((e) => e.aboutPageBadges ?? [])
])
return {
badges: allBadges
}
})

View File

@@ -1,18 +0,0 @@
import { defineStore } from 'pinia'
import { computed } from 'vue'
import type { TopbarBadge } from '@/types/comfy'
import { useExtensionStore } from './extensionStore'
export const useTopbarBadgeStore = defineStore('topbarBadge', () => {
const extensionStore = useExtensionStore()
const badges = computed<TopbarBadge[]>(() =>
extensionStore.extensions.flatMap((e) => e.topbarBadges ?? [])
)
return {
badges
}
})

View File

@@ -325,7 +325,7 @@ export const migrateLegacyRerouteNodes = (
}
// Create a deep copy of the workflow to avoid mutating the original
const newWorkflow = JSON.parse(JSON.stringify(workflow)) as WorkflowJSON04
const newWorkflow = structuredClone(workflow) as WorkflowJSON04
// Initialize extra structure if needed
if (!newWorkflow.extra) {

View File

@@ -1,34 +1,15 @@
import { v4 } from 'uuid'
/**
* @deprecated Use `v4` from `uuid` directly. Kept as litegraph's public API
* (`LiteGraph.uuidv4`, exported `createUuidv4`). uuid's v4 uses
* crypto.getRandomValues, so it works in non-secure contexts (LAN HTTP) where
* crypto.randomUUID() is unavailable.
*/
export const createUuidv4 = v4
// Using a template string for this is resulting in complex type workarounds. No current benefit beyond dev reading.
export type UUID = string
/** Special-case zero-UUID, consisting entirely of zeros. Used as a default value. */
export const zeroUuid = '00000000-0000-0000-0000-000000000000'
/** Pre-allocated storage for uuid random values. */
const randomStorage = new Uint32Array(31)
/**
* Creates a UUIDv4 string.
* @returns A new UUIDv4 string
* @remarks
* Original implementation from https://gist.github.com/jed/982883?permalink_comment_id=852670#gistcomment-852670
*
* Prefers the {@link crypto.randomUUID} method if available, falling back to
* {@link crypto.getRandomValues}, then finally the legacy {@link Math.random} method.
*/
export function createUuidv4(): UUID {
if (typeof crypto?.randomUUID === 'function') return crypto.randomUUID()
if (typeof crypto?.getRandomValues === 'function') {
const random = crypto.getRandomValues(randomStorage)
let i = 0
return '10000000-1000-4000-8000-100000000000'.replaceAll(/[018]/g, (a) =>
(
Number(a) ^
((random[i++] * 3.725_290_298_461_914e-9) >> (Number(a) * 0.25))
).toString(16)
)
}
return '10000000-1000-4000-8000-100000000000'.replaceAll(/[018]/g, (a) =>
(Number(a) ^ ((Math.random() * 16) >> (Number(a) * 0.25))).toString(16)
)
}

View File

@@ -28,12 +28,6 @@ export const PANEL_EXCLUDED_PROPS = [
'overlayClass'
] as const
// export const IMAGE_EXCLUDED_PROPS = [
// ...STANDARD_EXCLUDED_PROPS,
// 'imageClass',
// 'imageStyle'
// ] as const
export const GALLERIA_EXCLUDED_PROPS = [
...STANDARD_EXCLUDED_PROPS,
'thumbnailsPosition',