mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-06-03 11:54:07 +00:00
Compare commits
1 Commits
feat/node-
...
austin/aud
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efa69de8e4 |
1558
pnpm-lock.yaml
generated
1558
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -130,7 +130,7 @@ catalog:
|
||||
vite-plugin-dts: ^4.5.4
|
||||
vite-plugin-html: ^3.2.2
|
||||
vite-plugin-vue-devtools: ^8.0.0
|
||||
vitest: ^4.0.16
|
||||
vitest: ^4.1.0
|
||||
vue: ^3.5.34
|
||||
vue-component-type-helpers: ^3.2.1
|
||||
vue-eslint-parser: ^10.4.0
|
||||
|
||||
@@ -67,7 +67,6 @@ import { LGraphNode, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useSurveyFeatureTracking } from '@/platform/surveys/useSurveyFeatureTracking'
|
||||
import { withNodeAddSource } from '@/platform/telemetry/nodeAdded/nodeAddSource'
|
||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
@@ -141,12 +140,10 @@ function closeDialog() {
|
||||
|
||||
function addNode(nodeDef: ComfyNodeDefImpl, dragEvent?: MouseEvent) {
|
||||
const followCursor = settingStore.get('Comfy.NodeSearchBoxImpl.FollowCursor')
|
||||
const node = withNodeAddSource('search_modal', () =>
|
||||
litegraphService.addNodeOnGraph(
|
||||
nodeDef,
|
||||
{ pos: getNewNodeLocation() },
|
||||
{ ghost: useSearchBoxV2.value && followCursor, dragEvent }
|
||||
)
|
||||
const node = litegraphService.addNodeOnGraph(
|
||||
nodeDef,
|
||||
{ pos: getNewNodeLocation() },
|
||||
{ ghost: useSearchBoxV2.value && followCursor, dragEvent }
|
||||
)
|
||||
if (!node) return
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ import ModelTreeLeaf from '@/components/sidebar/tabs/modelLibrary/ModelTreeLeaf.
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useTreeExpansion } from '@/composables/useTreeExpansion'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { withNodeAddSource } from '@/platform/telemetry/nodeAdded/nodeAddSource'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
import { useAssetDownloadStore } from '@/stores/assetDownloadStore'
|
||||
import type { ComfyModelDef, ModelFolder } from '@/stores/modelStore'
|
||||
@@ -156,8 +155,8 @@ const renderedRoot = computed<TreeExplorerNode<ModelOrFolder>>(() => {
|
||||
if (this.leaf && model) {
|
||||
const provider = modelToNodeStore.getNodeProvider(model.directory)
|
||||
if (provider) {
|
||||
const graphNode = withNodeAddSource('sidebar_drag', () =>
|
||||
useLitegraphService().addNodeOnGraph(provider.nodeDef)
|
||||
const graphNode = useLitegraphService().addNodeOnGraph(
|
||||
provider.nodeDef
|
||||
)
|
||||
const widget = graphNode?.widgets?.find(
|
||||
(widget) => widget.name === provider.key
|
||||
|
||||
@@ -189,7 +189,6 @@ import NodeTreeFolder from '@/components/sidebar/tabs/nodeLibrary/NodeTreeFolder
|
||||
import NodeTreeLeaf from '@/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useTreeExpansion } from '@/composables/useTreeExpansion'
|
||||
import { withNodeAddSource } from '@/platform/telemetry/nodeAdded/nodeAddSource'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
import {
|
||||
DEFAULT_GROUPING_ID,
|
||||
@@ -322,11 +321,8 @@ const renderedRoot = computed<TreeExplorerNode<ComfyNodeDefImpl>>(() => {
|
||||
}
|
||||
},
|
||||
handleClick(e: MouseEvent) {
|
||||
const nodeDef = this.data
|
||||
if (this.leaf && nodeDef) {
|
||||
withNodeAddSource('sidebar_drag', () =>
|
||||
useLitegraphService().addNodeOnGraph(nodeDef)
|
||||
)
|
||||
if (this.leaf && this.data) {
|
||||
useLitegraphService().addNodeOnGraph(this.data)
|
||||
} else {
|
||||
toggleNodeOnEvent(e, this)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ import NodePreview from '@/components/node/NodePreview.vue'
|
||||
import NodeTreeFolder from '@/components/sidebar/tabs/nodeLibrary/NodeTreeFolder.vue'
|
||||
import NodeTreeLeaf from '@/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue'
|
||||
import { useTreeExpansion } from '@/composables/useTreeExpansion'
|
||||
import { withNodeAddSource } from '@/platform/telemetry/nodeAdded/nodeAddSource'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore'
|
||||
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
||||
@@ -184,11 +183,8 @@ const renderedBookmarkedRoot = computed<TreeExplorerNode<ComfyNodeDefImpl>>(
|
||||
await nodeBookmarkStore.addBookmark(nodePath)
|
||||
},
|
||||
handleClick(e: MouseEvent) {
|
||||
const nodeDef = this.data
|
||||
if (this.leaf && nodeDef) {
|
||||
withNodeAddSource('sidebar_drag', () =>
|
||||
useLitegraphService().addNodeOnGraph(nodeDef)
|
||||
)
|
||||
if (this.leaf && this.data) {
|
||||
useLitegraphService().addNodeOnGraph(this.data)
|
||||
} else {
|
||||
toggleNodeOnEvent(e, node)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ref, shallowRef } from 'vue'
|
||||
|
||||
import { withNodeAddSource } from '@/platform/telemetry/nodeAdded/nodeAddSource'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
||||
@@ -38,8 +37,7 @@ function isOverCanvas(clientX: number, clientY: number): boolean {
|
||||
}
|
||||
|
||||
function addNodeAtPosition(clientX: number, clientY: number): boolean {
|
||||
const nodeDef = draggedNode.value
|
||||
if (!nodeDef) return false
|
||||
if (!draggedNode.value) return false
|
||||
const canvas = useCanvasStore().canvas
|
||||
if (!canvas) return false
|
||||
if (!isOverCanvas(clientX, clientY)) return false
|
||||
@@ -48,9 +46,7 @@ function addNodeAtPosition(clientX: number, clientY: number): boolean {
|
||||
clientX,
|
||||
clientY
|
||||
} as PointerEvent)
|
||||
const node = withNodeAddSource('sidebar_drag', () =>
|
||||
useLitegraphService().addNodeOnGraph(nodeDef, { pos })
|
||||
)
|
||||
const node = useLitegraphService().addNodeOnGraph(draggedNode.value, { pos })
|
||||
if (node) canvas.selectItems([node])
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { mapTaskOutputToAssetItem } from '@/platform/assets/composables/media/as
|
||||
import { useMediaAssetActions } from '@/platform/assets/composables/useMediaAssetActions'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { withNodeAddSource } from '@/platform/telemetry/nodeAdded/nodeAddSource'
|
||||
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
|
||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import type { ResultItem, ResultItemType } from '@/schemas/apiSchema'
|
||||
@@ -147,11 +146,9 @@ export function useJobMenu(
|
||||
|
||||
const nodeDef = nodeDefStore.nodeDefsByName[nodeType]
|
||||
if (!nodeDef) return
|
||||
const node = withNodeAddSource('programmatic', () =>
|
||||
litegraphService.addNodeOnGraph(nodeDef, {
|
||||
pos: litegraphService.getCanvasCenter()
|
||||
})
|
||||
)
|
||||
const node = litegraphService.addNodeOnGraph(nodeDef, {
|
||||
pos: litegraphService.getCanvasCenter()
|
||||
})
|
||||
|
||||
if (!node) return
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useSharedCanvasPositionConversion } from '@/composables/element/useCanv
|
||||
import { usePragmaticDroppable } from '@/composables/usePragmaticDragAndDrop'
|
||||
import type { LGraphNode, Point } from '@/lib/litegraph/src/litegraph'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { withNodeAddSource } from '@/platform/telemetry/nodeAdded/nodeAddSource'
|
||||
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
|
||||
import { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import { app as comfyApp } from '@/scripts/app'
|
||||
@@ -38,9 +37,7 @@ export const useCanvasDrop = (canvasRef: Ref<HTMLCanvasElement | null>) => {
|
||||
// Add an offset on y to make sure after adding the node, the cursor
|
||||
// is on the node (top left corner)
|
||||
pos[1] += LiteGraph.NODE_TITLE_HEIGHT
|
||||
withNodeAddSource('sidebar_drag', () =>
|
||||
litegraphService.addNodeOnGraph(nodeDef, { pos })
|
||||
)
|
||||
litegraphService.addNodeOnGraph(nodeDef, { pos })
|
||||
} else if (node.data instanceof ComfyModelDef) {
|
||||
const model = node.data
|
||||
const pos = basePos
|
||||
@@ -61,8 +58,11 @@ export const useCanvasDrop = (canvasRef: Ref<HTMLCanvasElement | null>) => {
|
||||
if (!targetGraphNode) {
|
||||
const provider = modelToNodeStore.getNodeProvider(model.directory)
|
||||
if (provider) {
|
||||
targetGraphNode = withNodeAddSource('sidebar_drag', () =>
|
||||
litegraphService.addNodeOnGraph(provider.nodeDef, { pos })
|
||||
targetGraphNode = litegraphService.addNodeOnGraph(
|
||||
provider.nodeDef,
|
||||
{
|
||||
pos
|
||||
}
|
||||
)
|
||||
targetProvider = provider
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationD
|
||||
import { downloadFile } from '@/base/common/downloadUtil'
|
||||
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { withNodeAddSource } from '@/platform/telemetry/nodeAdded/nodeAddSource'
|
||||
import { useWorkflowActionsService } from '@/platform/workflow/core/services/workflowActionsService'
|
||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import { extractWorkflowFromAsset } from '@/platform/workflow/utils/workflowExtractionUtil'
|
||||
@@ -62,8 +61,7 @@ function widgetValueVariantsForAsset(asset: AssetItem): string[] {
|
||||
variants.push(`${name} [input]`)
|
||||
}
|
||||
}
|
||||
const hash = asset.hash ?? asset.asset_hash
|
||||
if (hash) variants.push(hash)
|
||||
if (asset.asset_hash) variants.push(asset.asset_hash)
|
||||
return variants
|
||||
}
|
||||
|
||||
@@ -281,11 +279,9 @@ export function useMediaAssetActions() {
|
||||
return
|
||||
}
|
||||
|
||||
const node = withNodeAddSource('programmatic', () =>
|
||||
litegraphService.addNodeOnGraph(nodeDef, {
|
||||
pos: litegraphService.getCanvasCenter()
|
||||
})
|
||||
)
|
||||
const node = litegraphService.addNodeOnGraph(nodeDef, {
|
||||
pos: litegraphService.getCanvasCenter()
|
||||
})
|
||||
|
||||
if (!node) {
|
||||
toast.add({
|
||||
@@ -300,11 +296,12 @@ export function useMediaAssetActions() {
|
||||
const metadata = getOutputAssetMetadata(targetAsset.user_metadata)
|
||||
const assetType = getAssetType(targetAsset, 'input')
|
||||
|
||||
// In Cloud mode, use the content hash (the actual stored filename),
|
||||
// preferring hash and falling back to the deprecated asset_hash alias.
|
||||
// In OSS mode, use the original name.
|
||||
const cloudHash = targetAsset.hash ?? targetAsset.asset_hash
|
||||
const filename = isCloud && cloudHash ? cloudHash : targetAsset.name
|
||||
// In Cloud mode, use asset_hash (the actual stored filename)
|
||||
// In OSS mode, use the original name
|
||||
const filename =
|
||||
isCloud && targetAsset.asset_hash
|
||||
? targetAsset.asset_hash
|
||||
: targetAsset.name
|
||||
|
||||
// Create annotated path for the asset
|
||||
const annotated = createAnnotatedPath(
|
||||
@@ -428,14 +425,12 @@ export function useMediaAssetActions() {
|
||||
}
|
||||
|
||||
const center = litegraphService.getCanvasCenter()
|
||||
const node = withNodeAddSource('programmatic', () =>
|
||||
litegraphService.addNodeOnGraph(nodeDef, {
|
||||
pos: [
|
||||
center[0] + nodeIndex * NODE_OFFSET,
|
||||
center[1] + nodeIndex * NODE_OFFSET
|
||||
]
|
||||
})
|
||||
)
|
||||
const node = litegraphService.addNodeOnGraph(nodeDef, {
|
||||
pos: [
|
||||
center[0] + nodeIndex * NODE_OFFSET,
|
||||
center[1] + nodeIndex * NODE_OFFSET
|
||||
]
|
||||
})
|
||||
|
||||
if (!node) {
|
||||
failed++
|
||||
@@ -445,11 +440,10 @@ export function useMediaAssetActions() {
|
||||
const metadata = getOutputAssetMetadata(asset.user_metadata)
|
||||
const assetType = getAssetType(asset, 'input')
|
||||
|
||||
// In Cloud mode, use the content hash (the actual stored filename),
|
||||
// preferring hash and falling back to the deprecated asset_hash alias.
|
||||
// In OSS mode, use the original name.
|
||||
const cloudHash = asset.hash ?? asset.asset_hash
|
||||
const filename = isCloud && cloudHash ? cloudHash : asset.name
|
||||
// In Cloud mode, use asset_hash (the actual stored filename)
|
||||
// In OSS mode, use the original name
|
||||
const filename =
|
||||
isCloud && asset.asset_hash ? asset.asset_hash : asset.name
|
||||
|
||||
const annotated = createAnnotatedPath(
|
||||
{
|
||||
|
||||
@@ -5,7 +5,6 @@ import { z } from 'zod'
|
||||
const zAsset = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
hash: z.string().nullish(),
|
||||
asset_hash: z.string().nullish(),
|
||||
size: z.number().optional(), // TBD: Will be provided by history API in the future
|
||||
mime_type: z.string().nullish(),
|
||||
|
||||
@@ -206,5 +206,5 @@ export function getAssetCardTitle(asset: AssetItem): string {
|
||||
* values or media URLs that must round-trip through the view endpoint.
|
||||
*/
|
||||
export function getAssetUrlFilename(asset: AssetItem): string {
|
||||
return asset.hash ?? asset.asset_hash ?? asset.name
|
||||
return asset.asset_hash || asset.name
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useAssetsStore } from '@/stores/assetsStore'
|
||||
interface AssetRecord {
|
||||
id: string
|
||||
name: string
|
||||
hash?: string
|
||||
asset_hash?: string
|
||||
preview_url?: string
|
||||
preview_id?: string | null
|
||||
@@ -43,7 +42,7 @@ export async function findOutputAsset(
|
||||
name: string
|
||||
): Promise<AssetRecord | undefined> {
|
||||
const byHash = await fetchAssets({ asset_hash: name })
|
||||
const hashMatch = byHash.find((a) => (a.hash ?? a.asset_hash) === name)
|
||||
const hashMatch = byHash.find((a) => a.asset_hash === name)
|
||||
if (hashMatch) return hashMatch
|
||||
|
||||
const byName = await fetchAssets({ name_contains: name })
|
||||
|
||||
@@ -85,7 +85,7 @@ export function getAssetDetectionNames(
|
||||
): string[] {
|
||||
const names = new Set<string>()
|
||||
// Treat names and hashes as opaque match keys because Cloud may use either in widget values.
|
||||
addPathDetectionNames(names, asset.hash ?? asset.asset_hash, options)
|
||||
addPathDetectionNames(names, asset.asset_hash, options)
|
||||
addPathDetectionNames(names, asset.name, options)
|
||||
|
||||
const subfolder = asset.user_metadata?.subfolder
|
||||
|
||||
@@ -501,8 +501,7 @@ function isAssetInstalled(
|
||||
): boolean {
|
||||
if (candidate.hash && candidate.hashType) {
|
||||
const candidateHash = `${candidate.hashType}:${candidate.hash}`
|
||||
if (assets.some((a) => (a.hash ?? a.asset_hash) === candidateHash))
|
||||
return true
|
||||
if (assets.some((a) => a.asset_hash === candidateHash)) return true
|
||||
}
|
||||
|
||||
const normalizedName = normalizePath(candidate.name)
|
||||
|
||||
@@ -12,7 +12,6 @@ import type {
|
||||
HelpCenterClosedMetadata,
|
||||
HelpCenterOpenedMetadata,
|
||||
HelpResourceClickedMetadata,
|
||||
NodeAddedMetadata,
|
||||
NodeSearchMetadata,
|
||||
NodeSearchResultMetadata,
|
||||
PageViewMetadata,
|
||||
@@ -199,10 +198,6 @@ export class TelemetryRegistry implements TelemetryDispatcher {
|
||||
)
|
||||
}
|
||||
|
||||
trackNodeAdded(metadata: NodeAddedMetadata): void {
|
||||
this.dispatch((provider) => provider.trackNodeAdded?.(metadata))
|
||||
}
|
||||
|
||||
trackTemplateFilterChanged(metadata: TemplateFilterMetadata): void {
|
||||
this.dispatch((provider) => provider.trackTemplateFilterChanged?.(metadata))
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { ChangeTracker } from '@/scripts/changeTracker'
|
||||
|
||||
import { installNodeAddedTelemetry } from './installNodeAddedTelemetry'
|
||||
import { withNodeAddSource } from './nodeAddSource'
|
||||
|
||||
const trackNodeAdded = vi.fn()
|
||||
|
||||
vi.mock('..', () => ({
|
||||
useTelemetry: () => ({ trackNodeAdded })
|
||||
}))
|
||||
|
||||
function fakeGraph(): LGraph {
|
||||
return { onNodeAdded: undefined } as unknown as LGraph
|
||||
}
|
||||
|
||||
function fakeNode(type: string): LGraphNode {
|
||||
return { type } as unknown as LGraphNode
|
||||
}
|
||||
|
||||
describe('installNodeAddedTelemetry', () => {
|
||||
beforeEach(() => {
|
||||
trackNodeAdded.mockClear()
|
||||
ChangeTracker.isLoadingGraph = false
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
ChangeTracker.isLoadingGraph = false
|
||||
})
|
||||
|
||||
it('fires trackNodeAdded with the current source on add', () => {
|
||||
const graph = fakeGraph()
|
||||
installNodeAddedTelemetry(graph)
|
||||
|
||||
withNodeAddSource('sidebar_drag', () => {
|
||||
graph.onNodeAdded?.(fakeNode('KSampler'))
|
||||
})
|
||||
|
||||
expect(trackNodeAdded).toHaveBeenCalledExactlyOnceWith({
|
||||
node_type: 'KSampler',
|
||||
source: 'sidebar_drag'
|
||||
})
|
||||
})
|
||||
|
||||
it('defaults source to "unknown" outside withNodeAddSource', () => {
|
||||
const graph = fakeGraph()
|
||||
installNodeAddedTelemetry(graph)
|
||||
|
||||
graph.onNodeAdded?.(fakeNode('CheckpointLoader'))
|
||||
|
||||
expect(trackNodeAdded).toHaveBeenCalledWith({
|
||||
node_type: 'CheckpointLoader',
|
||||
source: 'unknown'
|
||||
})
|
||||
})
|
||||
|
||||
it('skips telemetry during workflow load', () => {
|
||||
const graph = fakeGraph()
|
||||
installNodeAddedTelemetry(graph)
|
||||
ChangeTracker.isLoadingGraph = true
|
||||
|
||||
graph.onNodeAdded?.(fakeNode('VAEDecode'))
|
||||
|
||||
expect(trackNodeAdded).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('preserves an existing onNodeAdded subscriber', () => {
|
||||
const graph = fakeGraph()
|
||||
const previous = vi.fn()
|
||||
graph.onNodeAdded = previous
|
||||
installNodeAddedTelemetry(graph)
|
||||
|
||||
const node = fakeNode('LoadImage')
|
||||
graph.onNodeAdded?.(node)
|
||||
|
||||
expect(previous).toHaveBeenCalledExactlyOnceWith(node)
|
||||
expect(trackNodeAdded).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { LGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { ChangeTracker } from '@/scripts/changeTracker'
|
||||
|
||||
import { useTelemetry } from '..'
|
||||
import { getCurrentNodeAddSource } from './nodeAddSource'
|
||||
|
||||
/**
|
||||
* Wire `app:node_added` telemetry into a graph. Wraps any existing
|
||||
* `onNodeAdded` callback so we don't displace other subscribers. Bulk
|
||||
* additions during workflow load are skipped — `workflow_imported`
|
||||
* already covers those.
|
||||
*/
|
||||
export function installNodeAddedTelemetry(graph: LGraph): void {
|
||||
const previous = graph.onNodeAdded
|
||||
graph.onNodeAdded = function (node) {
|
||||
previous?.call(this, node)
|
||||
if (ChangeTracker.isLoadingGraph) return
|
||||
useTelemetry()?.trackNodeAdded({
|
||||
node_type: node.type ?? 'unknown',
|
||||
source: getCurrentNodeAddSource()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { NodeAddSource } from '../types'
|
||||
|
||||
let currentSource: NodeAddSource = 'unknown'
|
||||
|
||||
export function getCurrentNodeAddSource(): NodeAddSource {
|
||||
return currentSource
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the node-add source for the duration of `fn`. Synchronous only —
|
||||
* the source is read by the synchronous LGraph.onNodeAdded callback that
|
||||
* fires inside `graph.add()`. Nesting restores the previous value on exit.
|
||||
*/
|
||||
export function withNodeAddSource<T>(source: NodeAddSource, fn: () => T): T {
|
||||
const previous = currentSource
|
||||
currentSource = source
|
||||
try {
|
||||
return fn()
|
||||
} finally {
|
||||
currentSource = previous
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import type {
|
||||
HelpCenterOpenedMetadata,
|
||||
HelpResourceClickedMetadata,
|
||||
NodeSearchMetadata,
|
||||
NodeAddedMetadata,
|
||||
NodeSearchResultMetadata,
|
||||
PageViewMetadata,
|
||||
PageVisibilityMetadata,
|
||||
@@ -317,13 +316,6 @@ export class GtmTelemetryProvider implements TelemetryProvider {
|
||||
})
|
||||
}
|
||||
|
||||
trackNodeAdded(metadata: NodeAddedMetadata): void {
|
||||
this.pushEvent('node_added', {
|
||||
node_type: metadata.node_type,
|
||||
source: metadata.source
|
||||
})
|
||||
}
|
||||
|
||||
trackTemplateFilterChanged(metadata: TemplateFilterMetadata): void {
|
||||
this.pushEvent('template_filter', {
|
||||
search_query: metadata.search_query,
|
||||
|
||||
@@ -26,7 +26,6 @@ import type {
|
||||
HelpCenterOpenedMetadata,
|
||||
HelpResourceClickedMetadata,
|
||||
NodeSearchMetadata,
|
||||
NodeAddedMetadata,
|
||||
NodeSearchResultMetadata,
|
||||
PageVisibilityMetadata,
|
||||
RunButtonProperties,
|
||||
@@ -401,10 +400,6 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
|
||||
this.trackEvent(TelemetryEvents.NODE_SEARCH_RESULT_SELECTED, metadata)
|
||||
}
|
||||
|
||||
trackNodeAdded(metadata: NodeAddedMetadata): void {
|
||||
this.trackEvent(TelemetryEvents.NODE_ADDED, metadata)
|
||||
}
|
||||
|
||||
trackTemplateFilterChanged(metadata: TemplateFilterMetadata): void {
|
||||
this.trackEvent(TelemetryEvents.TEMPLATE_FILTER_CHANGED, metadata)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import type {
|
||||
HelpCenterClosedMetadata,
|
||||
HelpCenterOpenedMetadata,
|
||||
HelpResourceClickedMetadata,
|
||||
NodeAddedMetadata,
|
||||
NodeSearchMetadata,
|
||||
NodeSearchResultMetadata,
|
||||
PageViewMetadata,
|
||||
@@ -458,10 +457,6 @@ export class PostHogTelemetryProvider implements TelemetryProvider {
|
||||
this.trackEvent(TelemetryEvents.NODE_SEARCH_RESULT_SELECTED, metadata)
|
||||
}
|
||||
|
||||
trackNodeAdded(metadata: NodeAddedMetadata): void {
|
||||
this.trackEvent(TelemetryEvents.NODE_ADDED, metadata)
|
||||
}
|
||||
|
||||
trackTemplateFilterChanged(metadata: TemplateFilterMetadata): void {
|
||||
this.trackEvent(TelemetryEvents.TEMPLATE_FILTER_CHANGED, metadata)
|
||||
}
|
||||
|
||||
@@ -232,23 +232,6 @@ export interface NodeSearchMetadata {
|
||||
query: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Node added metadata. `source` indicates how the user initiated the add.
|
||||
* Bulk additions during workflow load are excluded — workflow_imported
|
||||
* already covers that.
|
||||
*/
|
||||
export type NodeAddSource =
|
||||
| 'sidebar_drag'
|
||||
| 'search_modal'
|
||||
| 'paste'
|
||||
| 'programmatic'
|
||||
| 'unknown'
|
||||
|
||||
export interface NodeAddedMetadata {
|
||||
node_type: string
|
||||
source: NodeAddSource
|
||||
}
|
||||
|
||||
/**
|
||||
* Node search result selection metadata
|
||||
*/
|
||||
@@ -454,9 +437,6 @@ export interface TelemetryProvider {
|
||||
trackNodeSearch?(metadata: NodeSearchMetadata): void
|
||||
trackNodeSearchResultSelected?(metadata: NodeSearchResultMetadata): void
|
||||
|
||||
// Node-added-to-canvas analytics
|
||||
trackNodeAdded?(metadata: NodeAddedMetadata): void
|
||||
|
||||
// Template filter tracking events
|
||||
trackTemplateFilterChanged?(metadata: TemplateFilterMetadata): void
|
||||
|
||||
@@ -543,7 +523,6 @@ export const TelemetryEvents = {
|
||||
// Node Search Analytics
|
||||
NODE_SEARCH: 'app:node_search',
|
||||
NODE_SEARCH_RESULT_SELECTED: 'app:node_search_result_selected',
|
||||
NODE_ADDED: 'app:node_added',
|
||||
|
||||
// Template Filter Analytics
|
||||
TEMPLATE_FILTER_CHANGED: 'app:template_filter_changed',
|
||||
|
||||
@@ -183,7 +183,7 @@ const createInputMappingWidget = (
|
||||
getMediaTypeFromFilename(asset.name) ===
|
||||
NODE_MEDIA_TYPE_MAP[node.comfyClass ?? '']
|
||||
)
|
||||
.map((asset) => asset.hash ?? asset.asset_hash)
|
||||
.map((asset) => asset.asset_hash)
|
||||
.filter((hash): hash is string => !!hash)
|
||||
)
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ export function useWidgetSelectItems(options: UseWidgetSelectItemsOptions) {
|
||||
// already carry their own URL-resolvable filename. Expanding them via
|
||||
// resolveOutputAssetItems would synthesize sibling AssetItems without
|
||||
// an asset_hash and reintroduce the FE-227 hash→name fallback bug.
|
||||
if (asset.hash ?? asset.asset_hash) continue
|
||||
if (asset.asset_hash) continue
|
||||
|
||||
const meta = getOutputAssetMetadata(asset.user_metadata)
|
||||
if (!meta) continue
|
||||
|
||||
@@ -25,7 +25,6 @@ import { LGraphEventMode } from '@/lib/litegraph/src/types/globalEnums'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { installNodeAddedTelemetry } from '@/platform/telemetry/nodeAdded/installNodeAddedTelemetry'
|
||||
import type { WorkflowOpenSource } from '@/platform/telemetry/types'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { updatePendingWarnings } from '@/platform/workflow/core/utils/pendingWarnings'
|
||||
@@ -889,7 +888,6 @@ export class ComfyApp {
|
||||
this.addAfterConfigureHandler(graph)
|
||||
|
||||
this.rootGraphInternal = graph
|
||||
installNodeAddedTelemetry(graph)
|
||||
this.canvas = new LGraphCanvas(canvasEl, graph)
|
||||
// Make canvas states reactive so we can observe changes on them.
|
||||
this.canvas.state = reactive(this.canvas.state)
|
||||
|
||||
@@ -352,9 +352,8 @@ export const useAssetsStore = defineStore('assets', () => {
|
||||
const inputAssetsByFilename = computed(() => {
|
||||
const map = new Map<string, AssetItem>()
|
||||
for (const asset of inputAssets.value) {
|
||||
const hash = asset.hash ?? asset.asset_hash
|
||||
if (hash) {
|
||||
map.set(hash, asset)
|
||||
if (asset.asset_hash) {
|
||||
map.set(asset.asset_hash, asset)
|
||||
}
|
||||
}
|
||||
return map
|
||||
|
||||
@@ -4,7 +4,6 @@ import type {
|
||||
LGraphNode
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { withNodeAddSource } from '@/platform/telemetry/nodeAdded/nodeAddSource'
|
||||
|
||||
/**
|
||||
* Serialises an array of nodes using a modified version of the old Litegraph copy (& paste) function
|
||||
@@ -107,7 +106,7 @@ export function deserialiseAndCreate(data: string, canvas: LGraphCanvas): void {
|
||||
node.pos[1] += graph_mouse[1] - topLeft[1]
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
withNodeAddSource('paste', () => graph.add(node, true))
|
||||
graph.add(node, true)
|
||||
nodes.push(node)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user