mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-02 22:37:32 +00:00
track cloud-specific onboarding events and add performance optimizations for hosted cloud app (#6158)
## Summary - Complete telemetry implementation with circular dependency fix - Add build performance optimizations from main branch ### Telemetry Features - ✅ Final telemetry events: signup opened, survey flow, email verification - ✅ Onboarding mode to prevent circular dependencies during app initialization - ✅ Lazy composable loading with dynamic imports for workflow tracking - ✅ Survey responses as both event properties and persistent user properties - ✅ User identification method for onboarding flow - ✅ Deferred user property setting until user is authenticated ### Performance Optimizations - ✅ Tree-shaking enabled to remove unused code - ✅ Manual chunk splitting for vendor libraries (primevue, vue, tiptap, chart.js, etc.) - ✅ Enhanced esbuild minification with console removal in production builds - ✅ GENERATE_SOURCEMAP environment variable control - ✅ Maintained ImportMap disabled for cloud performance ## Test plan - [x] Telemetry events track correctly in Mixpanel - [x] No circular dependency errors on app startup - [x] Survey responses appear as both event properties and user properties - [x] Build optimizations reduce bundle size and improve loading performance - [x] All lint/format/typecheck passes ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6158-track-cloud-specific-onboarding-events-and-add-performance-optimizations-for-hosted-cloud-2926d73d365081a7b533dde249d5f734) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2104,7 +2104,7 @@
|
||||
"desc": "Zero setup required. Works on any device.",
|
||||
"explain": "Generate multiple outputs at once. Share workflows with ease.",
|
||||
"learnAboutButton": "Learn about Cloud",
|
||||
"wantToRun": "Want to run comfyUI locally instead?",
|
||||
"wantToRun": "Want to run ComfyUI locally instead?",
|
||||
"download": "Download ComfyUI"
|
||||
},
|
||||
"checkingStatus": "Checking your account status...",
|
||||
@@ -2121,7 +2121,7 @@
|
||||
"cloudStart_desc": "Zero setup required. Works on any device.",
|
||||
"cloudStart_explain": "Generate multiple outputs at once. Share workflows with ease.",
|
||||
"cloudStart_learnAboutButton": "Learn about Cloud",
|
||||
"cloudStart_wantToRun": "Want to run comfyUI locally instead?",
|
||||
"cloudStart_wantToRun": "Want to run ComfyUI locally instead?",
|
||||
"cloudStart_download": "Download ComfyUI",
|
||||
"cloudStart_invited": "YOU'RE INVITED",
|
||||
"cloudStart_invited_signin": "Sign in to continue onto Cloud.",
|
||||
|
||||
@@ -100,6 +100,8 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import SignUpForm from '@/components/dialog/content/signin/SignUpForm.vue'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import type { SignUpData } from '@/schemas/signInSchema'
|
||||
import { translateAuthError } from '@/utils/authErrorTranslation'
|
||||
import { isInChina } from '@/utils/networkUtil'
|
||||
@@ -155,6 +157,11 @@ const signUpWithEmail = async (values: SignUpData) => {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// Track signup screen opened
|
||||
if (isCloud) {
|
||||
useTelemetry()?.trackSignupOpened()
|
||||
}
|
||||
|
||||
userIsInChina.value = await isInChina()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -222,6 +222,8 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { getSurveyCompletedStatus, submitSurvey } from '@/api/auth'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
@@ -233,6 +235,11 @@ onMounted(async () => {
|
||||
if (surveyCompleted) {
|
||||
// User already completed survey, redirect to waitlist
|
||||
await router.replace({ name: 'cloud-waitlist' })
|
||||
} else {
|
||||
// Track survey opened event
|
||||
if (isCloud) {
|
||||
useTelemetry()?.trackSurvey('opened')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check survey status:', error)
|
||||
@@ -342,7 +349,25 @@ const onSubmitSurvey = async () => {
|
||||
: surveyData.value.industry,
|
||||
making: surveyData.value.making
|
||||
}
|
||||
|
||||
await submitSurvey(payload)
|
||||
|
||||
// Track survey submitted event with responses
|
||||
if (isCloud) {
|
||||
useTelemetry()?.trackSurvey('submitted', {
|
||||
industry: payload.industry,
|
||||
team_size: undefined, // Not collected in this survey
|
||||
use_case: payload.useCase,
|
||||
familiarity: payload.familiarity,
|
||||
intended_use:
|
||||
payload.useCase === 'personal'
|
||||
? 'personal'
|
||||
: payload.useCase === 'client'
|
||||
? 'client'
|
||||
: 'inhouse'
|
||||
})
|
||||
}
|
||||
|
||||
await router.push({ name: 'cloud-user-check' })
|
||||
} finally {
|
||||
isSubmitting.value = false
|
||||
|
||||
@@ -40,6 +40,8 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useFirebaseAuth } from 'vuefire'
|
||||
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||
|
||||
@@ -106,6 +108,12 @@ const goBack = async () => {
|
||||
async function onSend() {
|
||||
try {
|
||||
await authStore.verifyEmail()
|
||||
|
||||
// Track email verification requested
|
||||
if (isCloud) {
|
||||
useTelemetry()?.trackEmailVerification('requested')
|
||||
}
|
||||
|
||||
useToastStore().add({
|
||||
severity: 'success',
|
||||
summary: t('cloudVerifyEmail_toast_success', {
|
||||
@@ -121,6 +129,11 @@ async function onSend() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// Track email verification screen opened
|
||||
if (isCloud) {
|
||||
useTelemetry()?.trackEmailVerification('opened')
|
||||
}
|
||||
|
||||
// If the user is already verified (email link already clicked),
|
||||
// continue to the next step automatically.
|
||||
if (authStore.isEmailVerified) {
|
||||
@@ -135,6 +148,10 @@ onMounted(async () => {
|
||||
if (auth.currentUser && !redirectInProgress.value) {
|
||||
await auth.currentUser.reload()
|
||||
if (auth.currentUser?.emailVerified) {
|
||||
// Track email verification completed
|
||||
if (isCloud) {
|
||||
useTelemetry()?.trackEmailVerification('completed')
|
||||
}
|
||||
void redirectToNextStep()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import type { OverridedMixpanel } from 'mixpanel-browser'
|
||||
|
||||
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import { useWorkflowTemplatesStore } from '@/platform/workflow/templates/repositories/workflowTemplatesStore'
|
||||
|
||||
import type {
|
||||
AuthMetadata,
|
||||
ExecutionContext,
|
||||
@@ -39,6 +35,16 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
|
||||
private eventQueue: QueuedEvent[] = []
|
||||
private isInitialized = false
|
||||
|
||||
// Onboarding mode - starts true, set to false when app is fully ready
|
||||
private isOnboardingMode = true
|
||||
|
||||
// Lazy-loaded composables - only imported once when app is ready
|
||||
private _workflowStore: any = null
|
||||
private _templatesStore: any = null
|
||||
private _currentUser: any = null
|
||||
private _settingStore: any = null
|
||||
private _composablesReady = false
|
||||
|
||||
constructor() {
|
||||
const token = __MIXPANEL_TOKEN__
|
||||
|
||||
@@ -54,14 +60,10 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
|
||||
api_host: 'https://mp.comfy.org',
|
||||
cross_subdomain_cookie: true,
|
||||
persistence: 'cookie',
|
||||
save_referrer: true,
|
||||
loaded: () => {
|
||||
this.isInitialized = true
|
||||
this.flushEventQueue() // flush events that were queued while initializing
|
||||
useCurrentUser().onUserResolved((user) => {
|
||||
if (this.mixpanel && user.id) {
|
||||
this.mixpanel.identify(user.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -94,6 +96,154 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify the current user for telemetry tracking.
|
||||
* Can be called during onboarding without circular dependencies.
|
||||
*/
|
||||
identifyUser(userId: string): void {
|
||||
if (!this.mixpanel) return
|
||||
|
||||
try {
|
||||
this.mixpanel.identify(userId)
|
||||
|
||||
// If we have pending survey responses, set them now that user is identified
|
||||
if (this.pendingSurveyResponses) {
|
||||
this.setSurveyUserProperties(this.pendingSurveyResponses)
|
||||
this.pendingSurveyResponses = null
|
||||
}
|
||||
|
||||
// Load existing survey data if available (only when app is ready)
|
||||
if (!this.isOnboardingMode) {
|
||||
this.initializeExistingSurveyData()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to identify user:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark that the main app is fully initialized and advanced telemetry features can be used.
|
||||
* Call this after the app bootstrap is complete.
|
||||
*/
|
||||
markAppReady(): void {
|
||||
this.isOnboardingMode = false
|
||||
// Trigger composable initialization now that it's safe
|
||||
void this.initializeComposables()
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initialization of Vue composables to avoid circular dependencies during module loading.
|
||||
* Only imports and initializes composables once when app is ready.
|
||||
*/
|
||||
private async initializeComposables(): Promise<boolean> {
|
||||
if (this._composablesReady || this.isOnboardingMode) {
|
||||
return this._composablesReady
|
||||
}
|
||||
|
||||
try {
|
||||
// Dynamic imports to avoid circular dependencies during module loading
|
||||
const [
|
||||
{ useWorkflowStore },
|
||||
{ useWorkflowTemplatesStore },
|
||||
{ useCurrentUser },
|
||||
{ useSettingStore }
|
||||
] = await Promise.all([
|
||||
import('@/platform/workflow/management/stores/workflowStore'),
|
||||
import(
|
||||
'@/platform/workflow/templates/repositories/workflowTemplatesStore'
|
||||
),
|
||||
import('@/composables/auth/useCurrentUser'),
|
||||
import('@/platform/settings/settingStore')
|
||||
])
|
||||
|
||||
// Initialize composables once
|
||||
this._workflowStore = useWorkflowStore()
|
||||
this._templatesStore = useWorkflowTemplatesStore()
|
||||
this._currentUser = useCurrentUser()
|
||||
this._settingStore = useSettingStore()
|
||||
|
||||
this._composablesReady = true
|
||||
|
||||
// Now that composables are ready, set up user tracking
|
||||
if (this.mixpanel) {
|
||||
this._currentUser.onUserResolved((user: any) => {
|
||||
if (this.mixpanel && user.id) {
|
||||
this.mixpanel.identify(user.id)
|
||||
this.initializeExistingSurveyData()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize composables:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private initializeExistingSurveyData(): void {
|
||||
if (!this.mixpanel) return
|
||||
|
||||
try {
|
||||
// If composables are ready, use cached store
|
||||
if (this._settingStore) {
|
||||
const surveyData = this._settingStore.get('onboarding_survey')
|
||||
|
||||
if (surveyData && typeof surveyData === 'object') {
|
||||
const survey = surveyData as any
|
||||
this.mixpanel.people.set({
|
||||
survey_industry: survey.industry,
|
||||
survey_team_size: survey.team_size,
|
||||
survey_use_case: survey.useCase,
|
||||
survey_familiarity: survey.familiarity,
|
||||
survey_intended_use:
|
||||
survey.useCase === 'personal'
|
||||
? 'personal'
|
||||
: survey.useCase === 'client'
|
||||
? 'client'
|
||||
: 'inhouse'
|
||||
})
|
||||
}
|
||||
}
|
||||
// If in onboarding mode, try dynamic import (safe since user is identified)
|
||||
else if (this.isOnboardingMode) {
|
||||
import('@/platform/settings/settingStore')
|
||||
.then(({ useSettingStore }) => {
|
||||
try {
|
||||
const settingStore = useSettingStore()
|
||||
const surveyData = settingStore.get('onboarding_survey')
|
||||
|
||||
if (surveyData && typeof surveyData === 'object') {
|
||||
const survey = surveyData as any
|
||||
this.mixpanel?.people.set({
|
||||
survey_industry: survey.industry,
|
||||
survey_team_size: survey.team_size,
|
||||
survey_use_case: survey.useCase,
|
||||
survey_familiarity: survey.familiarity,
|
||||
survey_intended_use:
|
||||
survey.useCase === 'personal'
|
||||
? 'personal'
|
||||
: survey.useCase === 'client'
|
||||
? 'client'
|
||||
: 'inhouse'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to load existing survey data during onboarding:',
|
||||
error
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to import settings store:', error)
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize existing survey data:', error)
|
||||
}
|
||||
}
|
||||
|
||||
private trackEvent(
|
||||
eventName: TelemetryEventName,
|
||||
properties?: TelemetryEventProperties
|
||||
@@ -117,6 +267,10 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
|
||||
}
|
||||
}
|
||||
|
||||
trackSignupOpened(): void {
|
||||
this.trackEvent(TelemetryEvents.USER_SIGN_UP_OPENED)
|
||||
}
|
||||
|
||||
trackAuth(metadata: AuthMetadata): void {
|
||||
this.trackEvent(TelemetryEvents.USER_AUTH_COMPLETED, metadata)
|
||||
}
|
||||
@@ -131,6 +285,16 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
|
||||
}
|
||||
|
||||
trackRunButton(options?: { subscribe_to_run?: boolean }): void {
|
||||
if (this.isOnboardingMode) {
|
||||
// During onboarding, track basic run button click without workflow context
|
||||
this.trackEvent(TelemetryEvents.RUN_BUTTON_CLICKED, {
|
||||
subscribe_to_run: options?.subscribe_to_run || false,
|
||||
workflow_type: 'custom',
|
||||
workflow_name: 'untitled'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const executionContext = this.getExecutionContext()
|
||||
|
||||
const runButtonProperties: RunButtonProperties = {
|
||||
@@ -151,7 +315,48 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
|
||||
? TelemetryEvents.USER_SURVEY_OPENED
|
||||
: TelemetryEvents.USER_SURVEY_SUBMITTED
|
||||
|
||||
this.trackEvent(eventName, responses)
|
||||
// Include survey responses as event properties for submitted events
|
||||
const eventProperties =
|
||||
stage === 'submitted' && responses
|
||||
? {
|
||||
industry: responses.industry,
|
||||
team_size: responses.team_size,
|
||||
use_case: responses.use_case,
|
||||
familiarity: responses.familiarity,
|
||||
intended_use: responses.intended_use
|
||||
}
|
||||
: undefined
|
||||
|
||||
this.trackEvent(eventName, eventProperties)
|
||||
|
||||
// Also set survey responses as persistent user properties
|
||||
if (stage === 'submitted' && responses && this.mixpanel) {
|
||||
// During onboarding, we need to defer user property setting until user is identified
|
||||
if (this.isOnboardingMode) {
|
||||
// Store responses to be set once user is identified
|
||||
this.pendingSurveyResponses = responses
|
||||
} else {
|
||||
this.setSurveyUserProperties(responses)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private pendingSurveyResponses: SurveyResponses | null = null
|
||||
|
||||
private setSurveyUserProperties(responses: SurveyResponses): void {
|
||||
if (!this.mixpanel) return
|
||||
|
||||
try {
|
||||
this.mixpanel.people.set({
|
||||
survey_industry: responses.industry,
|
||||
survey_team_size: responses.team_size,
|
||||
survey_use_case: responses.use_case,
|
||||
survey_familiarity: responses.familiarity,
|
||||
survey_intended_use: responses.intended_use
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to set survey user properties:', error)
|
||||
}
|
||||
}
|
||||
|
||||
trackEmailVerification(stage: 'opened' | 'requested' | 'completed'): void {
|
||||
@@ -177,45 +382,76 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
|
||||
}
|
||||
|
||||
trackWorkflowExecution(): void {
|
||||
if (this.isOnboardingMode) {
|
||||
// During onboarding, track basic execution without workflow context
|
||||
this.trackEvent(TelemetryEvents.WORKFLOW_EXECUTION_STARTED, {
|
||||
is_template: false,
|
||||
workflow_name: undefined
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const context = this.getExecutionContext()
|
||||
this.trackEvent(TelemetryEvents.WORKFLOW_EXECUTION_STARTED, context)
|
||||
}
|
||||
|
||||
getExecutionContext(): ExecutionContext {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const templatesStore = useWorkflowTemplatesStore()
|
||||
const activeWorkflow = workflowStore.activeWorkflow
|
||||
// Try to initialize composables if not ready and not in onboarding mode
|
||||
if (!this._composablesReady && !this.isOnboardingMode) {
|
||||
void this.initializeComposables()
|
||||
}
|
||||
|
||||
if (activeWorkflow?.filename) {
|
||||
const isTemplate = templatesStore.knownTemplateNames.has(
|
||||
activeWorkflow.filename
|
||||
)
|
||||
if (
|
||||
!this._composablesReady ||
|
||||
!this._workflowStore ||
|
||||
!this._templatesStore
|
||||
) {
|
||||
return {
|
||||
is_template: false,
|
||||
workflow_name: undefined
|
||||
}
|
||||
}
|
||||
|
||||
if (isTemplate) {
|
||||
const template = templatesStore.getTemplateByName(
|
||||
try {
|
||||
const activeWorkflow = this._workflowStore.activeWorkflow
|
||||
|
||||
if (activeWorkflow?.filename) {
|
||||
const isTemplate = this._templatesStore.knownTemplateNames.has(
|
||||
activeWorkflow.filename
|
||||
)
|
||||
|
||||
if (isTemplate) {
|
||||
const template = this._templatesStore.getTemplateByName(
|
||||
activeWorkflow.filename
|
||||
)
|
||||
return {
|
||||
is_template: true,
|
||||
workflow_name: activeWorkflow.filename,
|
||||
template_source: template?.sourceModule,
|
||||
template_category: template?.category,
|
||||
template_tags: template?.tags,
|
||||
template_models: template?.models,
|
||||
template_use_case: template?.useCase,
|
||||
template_license: template?.license
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
is_template: true,
|
||||
workflow_name: activeWorkflow.filename,
|
||||
template_source: template?.sourceModule,
|
||||
template_category: template?.category,
|
||||
template_tags: template?.tags,
|
||||
template_models: template?.models,
|
||||
template_use_case: template?.useCase,
|
||||
template_license: template?.license
|
||||
is_template: false,
|
||||
workflow_name: activeWorkflow.filename
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
is_template: false,
|
||||
workflow_name: activeWorkflow.filename
|
||||
workflow_name: undefined
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get execution context:', error)
|
||||
return {
|
||||
is_template: false,
|
||||
workflow_name: undefined
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
is_template: false,
|
||||
workflow_name: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ export interface TemplateMetadata {
|
||||
*/
|
||||
export interface TelemetryProvider {
|
||||
// Authentication flow events
|
||||
trackSignupOpened(): void
|
||||
trackAuth(metadata: AuthMetadata): void
|
||||
|
||||
// Subscription flow events
|
||||
@@ -94,6 +95,10 @@ export interface TelemetryProvider {
|
||||
|
||||
// Workflow execution events
|
||||
trackWorkflowExecution(): void
|
||||
|
||||
// App lifecycle management
|
||||
markAppReady?(): void
|
||||
identifyUser?(userId: string): void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,6 +106,7 @@ export interface TelemetryProvider {
|
||||
*/
|
||||
export const TelemetryEvents = {
|
||||
// Authentication Flow
|
||||
USER_SIGN_UP_OPENED: 'user_sign_up_opened',
|
||||
USER_AUTH_COMPLETED: 'user_auth_completed',
|
||||
|
||||
// Subscription Flow
|
||||
|
||||
@@ -520,7 +520,9 @@ const zSettings = z.object({
|
||||
'main.sub.setting.name': z.any(),
|
||||
'single.setting': z.any(),
|
||||
'LiteGraph.Node.DefaultPadding': z.boolean(),
|
||||
'LiteGraph.Pointer.TrackpadGestures': z.boolean()
|
||||
'LiteGraph.Pointer.TrackpadGestures': z.boolean(),
|
||||
/** Onboarding survey data */
|
||||
onboarding_survey: z.record(z.unknown()).optional()
|
||||
})
|
||||
|
||||
export type EmbeddingsResponse = z.infer<typeof zEmbeddingsResponse>
|
||||
|
||||
@@ -19,14 +19,15 @@ const SHOULD_MINIFY = process.env.ENABLE_MINIFY === 'true'
|
||||
// vite dev server will listen on all addresses, including LAN and public addresses
|
||||
const VITE_REMOTE_DEV = process.env.VITE_REMOTE_DEV === 'true'
|
||||
const DISABLE_TEMPLATES_PROXY = process.env.DISABLE_TEMPLATES_PROXY === 'true'
|
||||
const DISABLE_VUE_PLUGINS = false // Always enable Vue DevTools for development
|
||||
const DISABLE_VUE_PLUGINS = process.env.DISABLE_VUE_PLUGINS === 'true'
|
||||
const GENERATE_SOURCEMAP = process.env.GENERATE_SOURCEMAP !== 'false'
|
||||
|
||||
// CLOUD PERFORMANCE: ImportMap entries for Vue/PrimeVue temporarily disabled (see generateImportMapPlugin below)
|
||||
// This reduces 600+ HTTP requests to ~8 bundled files for better cloud deployment performance
|
||||
|
||||
// Hardcoded to staging cloud for testing frontend changes against cloud backend
|
||||
const DEV_SERVER_COMFYUI_URL =
|
||||
process.env.DEV_SERVER_COMFYUI_URL || 'https://stagingcloud.comfy.org'
|
||||
process.env.DEV_SERVER_COMFYUI_URL || 'https://testcloud.comfy.org'
|
||||
// To use local backend, change to: 'http://127.0.0.1:8188'
|
||||
|
||||
// Optional: Add API key to .env as STAGING_API_KEY if needed for authentication
|
||||
@@ -235,19 +236,68 @@ export default defineConfig({
|
||||
build: {
|
||||
minify: SHOULD_MINIFY ? 'esbuild' : false,
|
||||
target: 'es2022',
|
||||
sourcemap: true,
|
||||
sourcemap: GENERATE_SOURCEMAP,
|
||||
rollupOptions: {
|
||||
// Disabling tree-shaking
|
||||
// Prevent vite remove unused exports
|
||||
treeshake: false
|
||||
treeshake: true,
|
||||
output: {
|
||||
manualChunks: (id) => {
|
||||
if (!id.includes('node_modules')) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (id.includes('primevue') || id.includes('@primeuix')) {
|
||||
return 'vendor-primevue'
|
||||
}
|
||||
|
||||
if (id.includes('@tiptap')) {
|
||||
return 'vendor-tiptap'
|
||||
}
|
||||
|
||||
if (id.includes('chart.js')) {
|
||||
return 'vendor-chart'
|
||||
}
|
||||
|
||||
if (id.includes('three') || id.includes('@xterm')) {
|
||||
return 'vendor-visualization'
|
||||
}
|
||||
|
||||
if (id.includes('/vue') || id.includes('pinia')) {
|
||||
return 'vendor-vue'
|
||||
}
|
||||
|
||||
return 'vendor-other'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
esbuild: {
|
||||
minifyIdentifiers: false,
|
||||
minifyIdentifiers: SHOULD_MINIFY,
|
||||
keepNames: true,
|
||||
minifySyntax: SHOULD_MINIFY,
|
||||
minifyWhitespace: SHOULD_MINIFY
|
||||
minifyWhitespace: SHOULD_MINIFY,
|
||||
pure: SHOULD_MINIFY
|
||||
? [
|
||||
'console.log',
|
||||
'console.debug',
|
||||
'console.info',
|
||||
'console.trace',
|
||||
'console.dir',
|
||||
'console.dirxml',
|
||||
'console.group',
|
||||
'console.groupCollapsed',
|
||||
'console.groupEnd',
|
||||
'console.table',
|
||||
'console.time',
|
||||
'console.timeEnd',
|
||||
'console.timeLog',
|
||||
'console.count',
|
||||
'console.countReset',
|
||||
'console.profile',
|
||||
'console.profileEnd',
|
||||
'console.clear'
|
||||
]
|
||||
: []
|
||||
},
|
||||
|
||||
test: {
|
||||
|
||||
Reference in New Issue
Block a user