From 7bf8e5af38c87a91b4a5aba2bf96427a7b3b4689 Mon Sep 17 00:00:00 2001 From: Benjamin Lu Date: Sat, 1 Nov 2025 23:09:41 -0700 Subject: [PATCH] feat(telemetry): add tracking for sidebar, run menu, dialogs, subgraphs, settings and credits (#6511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary - Add comprehensive telemetry for key UI interactions using existing telemetry hooks (cloud-enabled, no-op in OSS): Sidebar and top-level - Node library button: `node_library` - Model library button: `model_library` - Workflows button: `workflows` - Assets/Media button: `assets` - Templates button: `templates` - Keyboard Shortcuts: `keyboard_shortcuts` - Console: `console` - Help Center: `help_center` - Settings (from Comfy logo menu): `settings_menu` Floating canvas menu - Minimap toggle: `minimap_toggle` - Hide links toggle: `hide_links` Run button and queue - Run button handle (drag): `run_button_handle` - Run mode selection: `run_instant`, `run_on_change` - Queue multiple: `queue_multiple` fires on each run when batch count > 1 (moved from batch-count-change to run-time, per guidance) Error dialogs - Close (X/mask/ESC): `error_dialog_close` via dialog onClose - Show report: `error_show_report` - Help fix this: `error_help_fix_this` - Find issues: `error_find_issues` Nodes / Subgraphs - Selection toolbox “Node info”: `node_info` - Enter subgraph (node header enter): `open_subgraph` - Subgraph breadcrumb navigation: `subgraph_breadcrumb_item` and `subgraph_breadcrumb_root` Settings / Credits / Search - Settings menu button (under Comfy logo): `settings_menu` - Purchase credits (Settings > Credits panel): tracked via existing `trackAddApiCreditButtonClicked` - Purchase credits (Avatar popover Top Up): tracked via existing `trackAddApiCreditButtonClicked` - Debounced search telemetry already present for node search and template filters; left as-is Notes and answers - Error dialog onClose: only fires when the dialog actually closes (X, mask, ESC, or programmatic close). “Show report” and “Help fix this” do not close the dialog; they each emit their own events. - Telemetry is behind the cloud provider; calls are optional (`useTelemetry()?.…`). OSS builds send nothing. Open questions / follow-ups - Primary Run button click: today cloud-only `trackRunButton` exists; we can also emit a UI-level `run` click (`UI_BUTTON_CLICKED` style) alongside it if desired. Confirm preference and I can add it. - Subgraph usage richness: if we want structured analytics (e.g., action, depth, subgraph id, node count), I can add a dedicated provider method and include richer metadata at enter/breadcrumb. - Optional parity: track the Comfy menu’s “Browse Templates” item in addition to the sidebar Templates button. Quality - Ran `pnpm lint:fix` and `pnpm typecheck`; both pass locally. Implementation details - All handlers refactored to named functions where needed; used `void` for intentionally unawaited async calls per lint rules. - Event names kept consistent in `button_id` strings; happy to align to a different naming scheme if you prefer. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6511-feat-telemetry-add-tracking-for-sidebar-run-menu-dialogs-subgraphs-settings-and-cre-29e6d73d365081a1b8b4fdfbbf40e18b) by [Unito](https://www.unito.io) --------- Co-authored-by: Christian Byrne Co-authored-by: Claude --- src/components/actionbar/ComfyActionbar.vue | 10 ++++++ .../ComfyRunButton/ComfyQueueButton.vue | 14 +++++++- .../breadcrumb/SubgraphBreadcrumb.vue | 7 ++++ .../dialog/content/ErrorDialogContent.vue | 9 +++++ .../dialog/content/error/FindIssueButton.vue | 8 +++++ .../dialog/content/setting/CreditsPanel.vue | 2 ++ src/components/graph/GraphCanvasMenu.vue | 25 ++++++++++++-- .../graph/selectionToolbox/InfoButton.vue | 13 +++++++- src/components/sidebar/ComfyMenuButton.vue | 19 +++++++++-- src/components/sidebar/SideToolbar.vue | 33 ++++++++++++++++++- .../SidebarBottomPanelToggleButton.vue | 13 +++++++- .../sidebar/SidebarHelpCenterIcon.vue | 7 ++++ .../sidebar/SidebarShortcutsToggleButton.vue | 7 ++++ .../sidebar/SidebarTemplatesButton.vue | 7 ++++ src/components/topbar/CurrentUserPopover.vue | 3 ++ .../cloud/MixpanelTelemetryProvider.ts | 5 +++ src/platform/telemetry/types.ts | 16 ++++++++- .../vueNodes/components/LGraphNode.vue | 4 +++ src/services/dialogService.ts | 19 +++++++++-- 19 files changed, 210 insertions(+), 11 deletions(-) diff --git a/src/components/actionbar/ComfyActionbar.vue b/src/components/actionbar/ComfyActionbar.vue index f01beee25a..94666cb8fa 100644 --- a/src/components/actionbar/ComfyActionbar.vue +++ b/src/components/actionbar/ComfyActionbar.vue @@ -48,6 +48,7 @@ import { computed, nextTick, onMounted, ref, watch } from 'vue' import { t } from '@/i18n' import { useSettingStore } from '@/platform/settings/settingStore' +import { useTelemetry } from '@/platform/telemetry' import { cn } from '@/utils/tailwindUtil' import ComfyRunButton from './ComfyRunButton' @@ -132,6 +133,15 @@ watch(visible, async (newVisible) => { } }) +/** + * Track run button handle drag start using mousedown on the drag handle. + */ +useEventListener(dragHandleRef, 'mousedown', () => { + useTelemetry()?.trackUiButtonClicked({ + button_id: 'actionbar_run_handle_drag_start' + }) +}) + const lastDragState = ref({ x: x.value, y: y.value, diff --git a/src/components/actionbar/ComfyRunButton/ComfyQueueButton.vue b/src/components/actionbar/ComfyRunButton/ComfyQueueButton.vue index cba25fc5aa..762ea7f2d6 100644 --- a/src/components/actionbar/ComfyRunButton/ComfyQueueButton.vue +++ b/src/components/actionbar/ComfyRunButton/ComfyQueueButton.vue @@ -100,7 +100,7 @@ import BatchCountEdit from '../BatchCountEdit.vue' const workspaceStore = useWorkspaceStore() const queueCountStore = storeToRefs(useQueuePendingTaskCountStore()) -const { mode: queueMode } = storeToRefs(useQueueSettingsStore()) +const { mode: queueMode, batchCount } = storeToRefs(useQueueSettingsStore()) const { t } = useI18n() const queueModeMenuItemLookup = computed(() => { @@ -118,6 +118,9 @@ const queueModeMenuItemLookup = computed(() => { label: `${t('menu.run')} (${t('menu.onChange')})`, tooltip: t('menu.onChangeTooltip'), command: () => { + useTelemetry()?.trackUiButtonClicked({ + button_id: 'queue_mode_option_run_on_change_selected' + }) queueMode.value = 'change' } } @@ -128,6 +131,9 @@ const queueModeMenuItemLookup = computed(() => { label: `${t('menu.run')} (${t('menu.instant')})`, tooltip: t('menu.instantTooltip'), command: () => { + useTelemetry()?.trackUiButtonClicked({ + button_id: 'queue_mode_option_run_instant_selected' + }) queueMode.value = 'instant' } } @@ -160,6 +166,12 @@ const queuePrompt = async (e: Event) => { useTelemetry()?.trackRunButton({ subscribe_to_run: false }) + if (batchCount.value > 1) { + useTelemetry()?.trackUiButtonClicked({ + button_id: 'queue_run_multiple_batches_submitted' + }) + } + await commandStore.execute(commandId) } diff --git a/src/components/breadcrumb/SubgraphBreadcrumb.vue b/src/components/breadcrumb/SubgraphBreadcrumb.vue index c5ee7ab71b..e69fd896fa 100644 --- a/src/components/breadcrumb/SubgraphBreadcrumb.vue +++ b/src/components/breadcrumb/SubgraphBreadcrumb.vue @@ -40,6 +40,7 @@ import { computed, onUpdated, ref, watch } from 'vue' import SubgraphBreadcrumbItem from '@/components/breadcrumb/SubgraphBreadcrumbItem.vue' import { useOverflowObserver } from '@/composables/element/useOverflowObserver' +import { useTelemetry } from '@/platform/telemetry' import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore' import { useCanvasStore } from '@/renderer/core/canvas/canvasStore' import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore' @@ -73,6 +74,9 @@ const items = computed(() => { const items = navigationStore.navigationStack.map((subgraph) => ({ label: subgraph.name, command: () => { + useTelemetry()?.trackUiButtonClicked({ + button_id: 'breadcrumb_subgraph_item_selected' + }) const canvas = useCanvasStore().getCanvas() if (!canvas.graph) throw new TypeError('Canvas has no graph') @@ -97,6 +101,9 @@ const home = computed(() => ({ key: 'root', isBlueprint: isBlueprint.value, command: () => { + useTelemetry()?.trackUiButtonClicked({ + button_id: 'breadcrumb_subgraph_root_selected' + }) const canvas = useCanvasStore().getCanvas() if (!canvas.graph) throw new TypeError('Canvas has no graph') diff --git a/src/components/dialog/content/ErrorDialogContent.vue b/src/components/dialog/content/ErrorDialogContent.vue index 7b08cbe1b6..c709540c58 100644 --- a/src/components/dialog/content/ErrorDialogContent.vue +++ b/src/components/dialog/content/ErrorDialogContent.vue @@ -87,7 +87,13 @@ const repoOwner = 'comfyanonymous' const repoName = 'ComfyUI' const reportContent = ref('') const reportOpen = ref(false) +/** + * Open the error report content and track telemetry. + */ const showReport = () => { + useTelemetry()?.trackUiButtonClicked({ + button_id: 'error_dialog_show_report_clicked' + }) reportOpen.value = true } const toast = useToast() @@ -99,6 +105,9 @@ const title = computed( () => error.nodeType ?? error.exceptionType ?? t('errorDialog.defaultTitle') ) +/** + * Open contact support flow from error dialog and track telemetry. + */ const showContactSupport = async () => { telemetry?.trackHelpResourceClicked({ resource_type: 'help_feedback', diff --git a/src/components/dialog/content/error/FindIssueButton.vue b/src/components/dialog/content/error/FindIssueButton.vue index e4c32b4713..20f4f64896 100644 --- a/src/components/dialog/content/error/FindIssueButton.vue +++ b/src/components/dialog/content/error/FindIssueButton.vue @@ -11,6 +11,8 @@ import Button from 'primevue/button' import { computed } from 'vue' +import { useTelemetry } from '@/platform/telemetry' + const props = defineProps<{ errorMessage: string repoOwner: string @@ -19,7 +21,13 @@ const props = defineProps<{ const queryString = computed(() => props.errorMessage + ' is:issue') +/** + * Open GitHub issues search and track telemetry. + */ const openGitHubIssues = () => { + useTelemetry()?.trackUiButtonClicked({ + button_id: 'error_dialog_find_existing_issues_clicked' + }) const query = encodeURIComponent(queryString.value) const url = `https://github.com/${props.repoOwner}/${props.repoName}/issues?q=${query}` window.open(url, '_blank') diff --git a/src/components/dialog/content/setting/CreditsPanel.vue b/src/components/dialog/content/setting/CreditsPanel.vue index c87cde3d40..614312ad4b 100644 --- a/src/components/dialog/content/setting/CreditsPanel.vue +++ b/src/components/dialog/content/setting/CreditsPanel.vue @@ -164,6 +164,8 @@ watch( ) const handlePurchaseCreditsClick = () => { + // Track purchase credits entry from Settings > Credits panel + useTelemetry()?.trackAddApiCreditButtonClicked() dialogService.showTopUpCreditsDialog() } diff --git a/src/components/graph/GraphCanvasMenu.vue b/src/components/graph/GraphCanvasMenu.vue index 0923db0064..f1beafdabc 100644 --- a/src/components/graph/GraphCanvasMenu.vue +++ b/src/components/graph/GraphCanvasMenu.vue @@ -61,7 +61,7 @@ data-testid="toggle-minimap-button" :style="stringifiedMinimapStyles.buttonStyles" :class="minimapButtonClass" - @click="() => commandStore.execute('Comfy.Canvas.ToggleMinimap')" + @click="onMinimapToggleClick" >