diff --git a/src/components/custom/widget/WorkflowTemplateSelectorDialog.vue b/src/components/custom/widget/WorkflowTemplateSelectorDialog.vue index c64a8e19d0..1c0da31f71 100644 --- a/src/components/custom/widget/WorkflowTemplateSelectorDialog.vue +++ b/src/components/custom/widget/WorkflowTemplateSelectorDialog.vue @@ -380,7 +380,7 @@ diff --git a/src/components/topbar/CommandMenubar.vue b/src/components/topbar/CommandMenubar.vue index 36aa56948c..7883a1a026 100644 --- a/src/components/topbar/CommandMenubar.vue +++ b/src/components/topbar/CommandMenubar.vue @@ -80,6 +80,7 @@ import { useI18n } from 'vue-i18n' import SubgraphBreadcrumb from '@/components/breadcrumb/SubgraphBreadcrumb.vue' import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue' +import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemplateSelectorDialog' import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue' import { useSettingStore } from '@/platform/settings/settingStore' import { useColorPaletteService } from '@/services/colorPaletteService' @@ -168,7 +169,7 @@ const extraMenuItems = computed(() => [ key: 'browse-templates', label: t('menuLabels.Browse Templates'), icon: 'icon-[comfy--template]', - command: () => commandStore.execute('Comfy.BrowseTemplates') + command: () => useWorkflowTemplateSelectorDialog().show('menu') }, { key: 'settings', diff --git a/src/composables/useWorkflowTemplateSelectorDialog.ts b/src/composables/useWorkflowTemplateSelectorDialog.ts index ca05ffb1dd..db2993698f 100644 --- a/src/composables/useWorkflowTemplateSelectorDialog.ts +++ b/src/composables/useWorkflowTemplateSelectorDialog.ts @@ -1,4 +1,5 @@ import WorkflowTemplateSelectorDialog from '@/components/custom/widget/WorkflowTemplateSelectorDialog.vue' +import { useTelemetry } from '@/platform/telemetry' import { useDialogService } from '@/services/dialogService' import { useDialogStore } from '@/stores/dialogStore' @@ -12,7 +13,9 @@ export const useWorkflowTemplateSelectorDialog = () => { dialogStore.closeDialog({ key: DIALOG_KEY }) } - function show() { + function show(source: 'sidebar' | 'menu' | 'command' = 'command') { + useTelemetry()?.trackTemplateLibraryOpened({ source }) + dialogService.showLayoutDialog({ key: DIALOG_KEY, component: WorkflowTemplateSelectorDialog, diff --git a/src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts b/src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts index 296264757c..c06f9fdcb8 100644 --- a/src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts +++ b/src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts @@ -5,12 +5,20 @@ import type { ExecutionContext, ExecutionErrorMetadata, ExecutionSuccessMetadata, + NodeSearchMetadata, + NodeSearchResultMetadata, + PageVisibilityMetadata, RunButtonProperties, SurveyResponses, + TabCountMetadata, TelemetryEventName, TelemetryEventProperties, TelemetryProvider, - TemplateMetadata + TemplateFilterMetadata, + TemplateLibraryClosedMetadata, + TemplateLibraryMetadata, + TemplateMetadata, + WorkflowImportMetadata } from '../../types' import { TelemetryEvents } from '../../types' @@ -351,6 +359,38 @@ export class MixpanelTelemetryProvider implements TelemetryProvider { this.trackEvent(TelemetryEvents.TEMPLATE_WORKFLOW_OPENED, metadata) } + trackTemplateLibraryOpened(metadata: TemplateLibraryMetadata): void { + this.trackEvent(TelemetryEvents.TEMPLATE_LIBRARY_OPENED, metadata) + } + + trackTemplateLibraryClosed(metadata: TemplateLibraryClosedMetadata): void { + this.trackEvent(TelemetryEvents.TEMPLATE_LIBRARY_CLOSED, metadata) + } + + trackWorkflowImported(metadata: WorkflowImportMetadata): void { + this.trackEvent(TelemetryEvents.WORKFLOW_IMPORTED, metadata) + } + + trackPageVisibilityChanged(metadata: PageVisibilityMetadata): void { + this.trackEvent(TelemetryEvents.PAGE_VISIBILITY_CHANGED, metadata) + } + + trackTabCount(metadata: TabCountMetadata): void { + this.trackEvent(TelemetryEvents.TAB_COUNT_TRACKING, metadata) + } + + trackNodeSearch(metadata: NodeSearchMetadata): void { + this.trackEvent(TelemetryEvents.NODE_SEARCH, metadata) + } + + trackNodeSearchResultSelected(metadata: NodeSearchResultMetadata): void { + this.trackEvent(TelemetryEvents.NODE_SEARCH_RESULT_SELECTED, metadata) + } + + trackTemplateFilterChanged(metadata: TemplateFilterMetadata): void { + this.trackEvent(TelemetryEvents.TEMPLATE_FILTER_CHANGED, metadata) + } + trackWorkflowExecution(): void { if (this.isOnboardingMode) { // During onboarding, track basic execution without workflow context diff --git a/src/platform/telemetry/types.ts b/src/platform/telemetry/types.ts index f19c0751a2..ed5c28ecca 100644 --- a/src/platform/telemetry/types.ts +++ b/src/platform/telemetry/types.ts @@ -89,6 +89,76 @@ export interface TemplateMetadata { template_license?: string } +/** + * Workflow import metadata + */ +export interface WorkflowImportMetadata { + missing_node_count: number + missing_node_types: string[] +} + +/** + * Template library metadata + */ +export interface TemplateLibraryMetadata { + source: 'sidebar' | 'menu' | 'command' +} + +/** + * Template library closed metadata + */ +export interface TemplateLibraryClosedMetadata { + template_selected: boolean + time_spent_seconds: number +} + +/** + * Page visibility metadata + */ +export interface PageVisibilityMetadata { + visibility_state: 'visible' | 'hidden' +} + +/** + * Tab count metadata + */ +export interface TabCountMetadata { + tab_count: number +} + +/** + * Node search metadata + */ +export interface NodeSearchMetadata { + query: string +} + +/** + * Node search result selection metadata + */ +export interface NodeSearchResultMetadata { + node_type: string + last_query: string +} + +/** + * Template filter tracking metadata + */ +export interface TemplateFilterMetadata { + search_query?: string + selected_models: string[] + selected_use_cases: string[] + selected_licenses: string[] + sort_by: + | 'default' + | 'alphabetical' + | 'newest' + | 'vram-low-to-high' + | 'model-size-low-to-high' + filtered_count: number + total_count: number +} + /** * Core telemetry provider interface */ @@ -106,6 +176,24 @@ export interface TelemetryProvider { // Template workflow events trackTemplate(metadata: TemplateMetadata): void + trackTemplateLibraryOpened(metadata: TemplateLibraryMetadata): void + trackTemplateLibraryClosed(metadata: TemplateLibraryClosedMetadata): void + + // Workflow management events + trackWorkflowImported(metadata: WorkflowImportMetadata): void + + // Page visibility events + trackPageVisibilityChanged(metadata: PageVisibilityMetadata): void + + // Tab tracking events + trackTabCount(metadata: TabCountMetadata): void + + // Node search analytics events + trackNodeSearch(metadata: NodeSearchMetadata): void + trackNodeSearchResultSelected(metadata: NodeSearchResultMetadata): void + + // Template filter tracking events + trackTemplateFilterChanged(metadata: TemplateFilterMetadata): void // Workflow execution events trackWorkflowExecution(): void @@ -140,6 +228,24 @@ export const TelemetryEvents = { // Template Tracking TEMPLATE_WORKFLOW_OPENED: 'app:template_workflow_opened', + TEMPLATE_LIBRARY_OPENED: 'app:template_library_opened', + TEMPLATE_LIBRARY_CLOSED: 'app:template_library_closed', + + // Workflow Management + WORKFLOW_IMPORTED: 'app:workflow_imported', + + // Page Visibility + PAGE_VISIBILITY_CHANGED: 'app:page_visibility_changed', + + // Tab Tracking + TAB_COUNT_TRACKING: 'app:tab_count_tracking', + + // Node Search Analytics + NODE_SEARCH: 'app:node_search', + NODE_SEARCH_RESULT_SELECTED: 'app:node_search_result_selected', + + // Template Filter Analytics + TEMPLATE_FILTER_CHANGED: 'app:template_filter_changed', // Execution Lifecycle EXECUTION_START: 'execution_start', @@ -161,3 +267,11 @@ export type TelemetryEventProperties = | RunButtonProperties | ExecutionErrorMetadata | ExecutionSuccessMetadata + | WorkflowImportMetadata + | TemplateLibraryMetadata + | TemplateLibraryClosedMetadata + | PageVisibilityMetadata + | TabCountMetadata + | NodeSearchMetadata + | NodeSearchResultMetadata + | TemplateFilterMetadata