diff --git a/src/App.vue b/src/App.vue index 6b7c56be0..a6ff7c4f4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -17,6 +17,7 @@ import { computed, onMounted } from 'vue' import GlobalDialog from '@/components/dialog/GlobalDialog.vue' import config from '@/config' import { t } from '@/i18n' +import { useSettingStore } from '@/platform/settings/settingStore' import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore' import { app } from '@/scripts/app' import { useDialogService } from '@/services/dialogService' @@ -47,7 +48,7 @@ const showContextMenu = (event: MouseEvent) => { } } -onMounted(() => { +onMounted(async () => { window['__COMFYUI_FRONTEND_VERSION__'] = config.app_version if (isElectron()) { @@ -77,5 +78,17 @@ onMounted(() => { // Initialize conflict detection in background // This runs async and doesn't block UI setup void conflictDetection.initializeConflictDetection() + + // Show cloud notification for macOS desktop users (one-time) + const isMacOS = navigator.platform.toLowerCase().includes('mac') + const settingStore = useSettingStore() + const hasShownNotification = settingStore.get( + 'Comfy.Desktop.CloudNotificationShown' + ) + + if (isElectron() && isMacOS && !hasShownNotification) { + dialogService.showCloudNotification() + await settingStore.set('Comfy.Desktop.CloudNotificationShown', true) + } }) diff --git a/src/components/dialog/content/CloudNotificationContent.vue b/src/components/dialog/content/CloudNotificationContent.vue new file mode 100644 index 000000000..657d9dcb9 --- /dev/null +++ b/src/components/dialog/content/CloudNotificationContent.vue @@ -0,0 +1,104 @@ + + + + + + + + {{ t('cloudNotification.title') }} + + + + {{ t('cloudNotification.message') }} + + + + + + + + + + {{ t('cloudNotification.feature1Title') }} + + + {{ t('cloudNotification.feature1') }} + + + + + + + + + {{ t('cloudNotification.feature2Title') }} + + + {{ t('cloudNotification.feature2') }} + + + + + + + + + {{ t('cloudNotification.feature3Title') }} + + + {{ t('cloudNotification.feature3') }} + + + + + + + + + {{ t('cloudNotification.feature4') }} + + + + + + + + + + + + diff --git a/src/components/topbar/TopbarBadges.vue b/src/components/topbar/TopbarBadges.vue index 301973960..de0d8386f 100644 --- a/src/components/topbar/TopbarBadges.vue +++ b/src/components/topbar/TopbarBadges.vue @@ -1,5 +1,22 @@ + + + + {{ t('cloudNotification.badgeLabel') }} + + + {{ t('cloudNotification.badgeText') }} + + + + import { breakpointsTailwind, useBreakpoints } from '@vueuse/core' import { computed } from 'vue' +import { useI18n } from 'vue-i18n' +import { useSettingStore } from '@/platform/settings/settingStore' +import { useDialogService } from '@/services/dialogService' import { useTopbarBadgeStore } from '@/stores/topbarBadgeStore' +import type { TopbarBadge as TopbarBadgeType } from '@/types/comfy' +import { isElectron } from '@/utils/envUtil' import TopbarBadge from './TopbarBadge.vue' @@ -41,4 +63,35 @@ const displayMode = computed<'full' | 'compact' | 'icon-only'>(() => { }) const topbarBadgeStore = useTopbarBadgeStore() + +// Cloud notification badge +const { t } = useI18n() +const settingStore = useSettingStore() +const dialogService = useDialogService() + +const isMacOS = computed(() => navigator.platform.toLowerCase().includes('mac')) + +const hasShownNotification = computed(() => + settingStore.get('Comfy.Desktop.CloudNotificationShown') +) + +const shouldShowCloudBadge = computed( + () => isElectron() && isMacOS.value && hasShownNotification.value +) + +const cloudBadge = computed(() => { + if (!shouldShowCloudBadge.value) return null + + return { + text: 'Discover Comfy Cloud', + label: 'NEW', + icon: 'pi pi-cloud', + variant: 'info', + tooltip: 'Learn about Comfy Cloud' + } +}) + +const handleCloudBadgeClick = () => { + dialogService.showCloudNotification() +} diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 45c27782d..1a557b675 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -2212,5 +2212,21 @@ "description": "This workflow uses custom nodes you haven't installed yet.", "replacementInstruction": "Install these nodes to run this workflow, or replace them with installed alternatives. Missing nodes are highlighted in red on the canvas." } + }, + "cloudNotification": { + "title": "Discover Comfy Cloud", + "message": "Get access to industry-grade GPUs and run workflows up to 10x faster", + "feature1Title": "No Setup Required", + "feature1": "Start creating instantly with popular models pre-installed", + "feature2Title": "Powerful GPUs", + "feature2": "A100 and RTX PRO 6000 GPUs for heavy video models", + "feature3Title": "$20/month", + "feature3": "Simple subscription with unlimited workflow runs", + "feature4": "ComfyUI stays free and open source.\nCloud is optional—for instant access to high-end GPUs.", + "continueLocally": "Continue Locally", + "exploreCloud": "Explore Cloud", + "badgeTooltip": "Learn about Comfy Cloud", + "badgeLabel": "NEW", + "badgeText": "Discover Comfy Cloud" } } \ No newline at end of file diff --git a/src/platform/settings/constants/coreSettings.ts b/src/platform/settings/constants/coreSettings.ts index 0493f2f00..9a361e2c3 100644 --- a/src/platform/settings/constants/coreSettings.ts +++ b/src/platform/settings/constants/coreSettings.ts @@ -293,6 +293,12 @@ export const CORE_SETTINGS: SettingParams[] = [ type: 'boolean', defaultValue: true }, + { + id: 'Comfy.Desktop.CloudNotificationShown', + name: 'Cloud notification shown', + type: 'boolean', + defaultValue: false + }, { id: 'Comfy.Graph.ZoomSpeed', category: ['LiteGraph', 'Canvas', 'ZoomSpeed'], diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index abf2a0b78..6406b6f55 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -374,6 +374,7 @@ const zSettings = z.object({ 'Comfy.Workflow.ShowMissingNodesWarning': z.boolean(), 'Comfy.Workflow.ShowMissingModelsWarning': z.boolean(), 'Comfy.Workflow.WarnBlueprintOverwrite': z.boolean(), + 'Comfy.Desktop.CloudNotificationShown': z.boolean(), 'Comfy.DisableFloatRounding': z.boolean(), 'Comfy.DisableSliders': z.boolean(), 'Comfy.DOMClippingEnabled': z.boolean(), diff --git a/src/services/dialogService.ts b/src/services/dialogService.ts index ff08ad154..c4c9d8848 100644 --- a/src/services/dialogService.ts +++ b/src/services/dialogService.ts @@ -2,6 +2,7 @@ import { merge } from 'es-toolkit/compat' import type { Component } from 'vue' import ApiNodesSignInContent from '@/components/dialog/content/ApiNodesSignInContent.vue' +import CloudNotificationContent from '@/components/dialog/content/CloudNotificationContent.vue' import MissingNodesContent from '@/components/dialog/content/MissingNodesContent.vue' import MissingNodesFooter from '@/components/dialog/content/MissingNodesFooter.vue' import MissingNodesHeader from '@/components/dialog/content/MissingNodesHeader.vue' @@ -541,6 +542,16 @@ export const useDialogService = () => { show() } + function showCloudNotification() { + dialogStore.showDialog({ + key: 'global-cloud-notification', + component: CloudNotificationContent, + dialogComponentProps: { + closable: true + } + }) + } + return { showLoadWorkflowWarning, showMissingModelsWarning, @@ -555,6 +566,7 @@ export const useDialogService = () => { showTopUpCreditsDialog, showUpdatePasswordDialog, showExtensionDialog, + showCloudNotification, prompt, showErrorDialog, confirm,
+ {{ t('cloudNotification.message') }} +
+ {{ t('cloudNotification.feature4') }} +