comfyui-frontend-package 1.38.5__py3-none-any.whl → 1.38.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. comfyui_frontend_package/static/assets/{AboutPanel-CHse5rOA.js → AboutPanel-lkjGFasi.js} +2 -2
  2. comfyui_frontend_package/static/assets/{AboutPanel-CHse5rOA.js.map → AboutPanel-lkjGFasi.js.map} +1 -1
  3. comfyui_frontend_package/static/assets/{AudioPreviewPlayer-CAa8V66L.js → AudioPreviewPlayer-BxCSKPl9.js} +2 -2
  4. comfyui_frontend_package/static/assets/{AudioPreviewPlayer-CAa8V66L.js.map → AudioPreviewPlayer-BxCSKPl9.js.map} +1 -1
  5. comfyui_frontend_package/static/assets/AudioPreviewPlayer-CkxKvcVf.js +1 -0
  6. comfyui_frontend_package/static/assets/{BaseViewTemplate-DA6zfigT.js → BaseViewTemplate-CjODF2hh.js} +2 -2
  7. comfyui_frontend_package/static/assets/{BaseViewTemplate-DA6zfigT.js.map → BaseViewTemplate-CjODF2hh.js.map} +1 -1
  8. comfyui_frontend_package/static/assets/{CloudAuthTimeoutView-9OPBS1hE.js → CloudAuthTimeoutView-D-QkjPNh.js} +2 -2
  9. comfyui_frontend_package/static/assets/{CloudAuthTimeoutView-9OPBS1hE.js.map → CloudAuthTimeoutView-D-QkjPNh.js.map} +1 -1
  10. comfyui_frontend_package/static/assets/{CloudBadge-BnLiAHDN.js → CloudBadge-B4nmLus2.js} +2 -2
  11. comfyui_frontend_package/static/assets/{CloudBadge-BnLiAHDN.js.map → CloudBadge-B4nmLus2.js.map} +1 -1
  12. comfyui_frontend_package/static/assets/{CloudForgotPasswordView-BqDR_C7K.js → CloudForgotPasswordView-DOEV9hGr.js} +2 -2
  13. comfyui_frontend_package/static/assets/{CloudForgotPasswordView-BqDR_C7K.js.map → CloudForgotPasswordView-DOEV9hGr.js.map} +1 -1
  14. comfyui_frontend_package/static/assets/{CloudLayoutView-vTrrVUOY.js → CloudLayoutView-ShKH6rRV.js} +2 -2
  15. comfyui_frontend_package/static/assets/{CloudLayoutView-vTrrVUOY.js.map → CloudLayoutView-ShKH6rRV.js.map} +1 -1
  16. comfyui_frontend_package/static/assets/{CloudLoginView-T17euJly.js → CloudLoginView-C3Te42U9.js} +2 -2
  17. comfyui_frontend_package/static/assets/{CloudLoginView-T17euJly.js.map → CloudLoginView-C3Te42U9.js.map} +1 -1
  18. comfyui_frontend_package/static/assets/CloudRunButtonWrapper-Cub7EB34.js +3 -0
  19. comfyui_frontend_package/static/assets/{CloudRunButtonWrapper-hQc4BNkX.js.map → CloudRunButtonWrapper-Cub7EB34.js.map} +1 -1
  20. comfyui_frontend_package/static/assets/{CloudSignupView-vEDby5k3.js → CloudSignupView-X2oiL3ZR.js} +2 -2
  21. comfyui_frontend_package/static/assets/{CloudSignupView-vEDby5k3.js.map → CloudSignupView-X2oiL3ZR.js.map} +1 -1
  22. comfyui_frontend_package/static/assets/{CloudSubscriptionRedirectView-DPyO745g.js → CloudSubscriptionRedirectView-UjNv8emo.js} +2 -2
  23. comfyui_frontend_package/static/assets/{CloudSubscriptionRedirectView-DPyO745g.js.map → CloudSubscriptionRedirectView-UjNv8emo.js.map} +1 -1
  24. comfyui_frontend_package/static/assets/{CloudSurveyView-CBtTd9Ru.js → CloudSurveyView-IaiucCTP.js} +2 -2
  25. comfyui_frontend_package/static/assets/{CloudSurveyView-CBtTd9Ru.js.map → CloudSurveyView-IaiucCTP.js.map} +1 -1
  26. comfyui_frontend_package/static/assets/ComfyQueueButton-BppnHbrl.js +1 -0
  27. comfyui_frontend_package/static/assets/{ComfyQueueButton-MZrp7wYJ.js → ComfyQueueButton-HjSIKZKO.js} +2 -2
  28. comfyui_frontend_package/static/assets/{ComfyQueueButton-MZrp7wYJ.js.map → ComfyQueueButton-HjSIKZKO.js.map} +1 -1
  29. comfyui_frontend_package/static/assets/{ExtensionPanel-CrWVGUtg.js → ExtensionPanel-Bzb9QtKj.js} +2 -2
  30. comfyui_frontend_package/static/assets/{ExtensionPanel-CrWVGUtg.js.map → ExtensionPanel-Bzb9QtKj.js.map} +1 -1
  31. comfyui_frontend_package/static/assets/{GlobalToast-BiCmpIvO.js → GlobalToast-BSCvu6Hw.js} +2 -2
  32. comfyui_frontend_package/static/assets/{GlobalToast-BiCmpIvO.js.map → GlobalToast-BSCvu6Hw.js.map} +1 -1
  33. comfyui_frontend_package/static/assets/{GraphView-BCkpNGgz.js → GraphView-gYVCtm1V.js} +5 -5
  34. comfyui_frontend_package/static/assets/GraphView-gYVCtm1V.js.map +1 -0
  35. comfyui_frontend_package/static/assets/{KeybindingPanel-CAXL5TlV.js → KeybindingPanel-DF-bG4iO.js} +2 -2
  36. comfyui_frontend_package/static/assets/{KeybindingPanel-CAXL5TlV.js.map → KeybindingPanel-DF-bG4iO.js.map} +1 -1
  37. comfyui_frontend_package/static/assets/{LegacyCreditsPanel-6vR8koQy.js → LegacyCreditsPanel-D-CboO8k.js} +2 -2
  38. comfyui_frontend_package/static/assets/{LegacyCreditsPanel-6vR8koQy.js.map → LegacyCreditsPanel-D-CboO8k.js.map} +1 -1
  39. comfyui_frontend_package/static/assets/Load3D-c9UwgGoI.js +1 -0
  40. comfyui_frontend_package/static/assets/{Load3D-ei1BUF9O.js → Load3D-vYr8M3jJ.js} +2 -2
  41. comfyui_frontend_package/static/assets/{Load3D-ei1BUF9O.js.map → Load3D-vYr8M3jJ.js.map} +1 -1
  42. comfyui_frontend_package/static/assets/{PanelTemplate-BjN5XNg2.js → PanelTemplate-BJda9e5J.js} +2 -2
  43. comfyui_frontend_package/static/assets/{PanelTemplate-BjN5XNg2.js.map → PanelTemplate-BJda9e5J.js.map} +1 -1
  44. comfyui_frontend_package/static/assets/{ServerConfigPanel-CxovH9Qk.js → ServerConfigPanel-VsC6xlZJ.js} +2 -2
  45. comfyui_frontend_package/static/assets/{ServerConfigPanel-CxovH9Qk.js.map → ServerConfigPanel-VsC6xlZJ.js.map} +1 -1
  46. comfyui_frontend_package/static/assets/{SubscribeButton-CTOQRkfg.js → SubscribeButton-DZBycfCA.js} +2 -2
  47. comfyui_frontend_package/static/assets/{SubscribeButton-CTOQRkfg.js.map → SubscribeButton-DZBycfCA.js.map} +1 -1
  48. comfyui_frontend_package/static/assets/{SubscribeToRun-DnXzV8y0.js → SubscribeToRun-4YolxBOL.js} +2 -2
  49. comfyui_frontend_package/static/assets/{SubscribeToRun-DnXzV8y0.js.map → SubscribeToRun-4YolxBOL.js.map} +1 -1
  50. comfyui_frontend_package/static/assets/{SubscriptionPanel-D9uv7z8f.js → SubscriptionPanel-B6txX4Vm.js} +2 -2
  51. comfyui_frontend_package/static/assets/{SubscriptionPanel-D9uv7z8f.js.map → SubscriptionPanel-B6txX4Vm.js.map} +1 -1
  52. comfyui_frontend_package/static/assets/{SubscriptionRequiredDialogContent--VmT16oc.js → SubscriptionRequiredDialogContent-COEF2VQ_.js} +2 -2
  53. comfyui_frontend_package/static/assets/{SubscriptionRequiredDialogContent--VmT16oc.js.map → SubscriptionRequiredDialogContent-COEF2VQ_.js.map} +1 -1
  54. comfyui_frontend_package/static/assets/{UserCheckView-spD3LyMu.js → UserCheckView-x-fkcYzc.js} +2 -2
  55. comfyui_frontend_package/static/assets/{UserCheckView-spD3LyMu.js.map → UserCheckView-x-fkcYzc.js.map} +1 -1
  56. comfyui_frontend_package/static/assets/{UserPanel-Su6NtJ5q.js → UserPanel-7N9QknQj.js} +2 -2
  57. comfyui_frontend_package/static/assets/{UserPanel-Su6NtJ5q.js.map → UserPanel-7N9QknQj.js.map} +1 -1
  58. comfyui_frontend_package/static/assets/{UserSelectView-C5LBOPcv.js → UserSelectView-BYjOkfSa.js} +2 -2
  59. comfyui_frontend_package/static/assets/{UserSelectView-C5LBOPcv.js.map → UserSelectView-BYjOkfSa.js.map} +1 -1
  60. comfyui_frontend_package/static/assets/{ValueControlPopover-BdlDzT8l.js → ValueControlPopover-BPAa35QG.js} +2 -2
  61. comfyui_frontend_package/static/assets/{ValueControlPopover-BdlDzT8l.js.map → ValueControlPopover-BPAa35QG.js.map} +1 -1
  62. comfyui_frontend_package/static/assets/{WidgetAudioUI-BDZxDx_r.js → WidgetAudioUI-Dw-r3Ews.js} +2 -2
  63. comfyui_frontend_package/static/assets/{WidgetAudioUI-BDZxDx_r.js.map → WidgetAudioUI-Dw-r3Ews.js.map} +1 -1
  64. comfyui_frontend_package/static/assets/{WidgetImageCrop-CYRW7t2Q.js → WidgetImageCrop-kERy9g5I.js} +2 -2
  65. comfyui_frontend_package/static/assets/{WidgetImageCrop-CYRW7t2Q.js.map → WidgetImageCrop-kERy9g5I.js.map} +1 -1
  66. comfyui_frontend_package/static/assets/{WidgetInputNumber-BOKO36G3.js → WidgetInputNumber-BaClCNAC.js} +1 -1
  67. comfyui_frontend_package/static/assets/WidgetInputNumber-DU_D0Fzy.js +3 -0
  68. comfyui_frontend_package/static/assets/WidgetInputNumber-DU_D0Fzy.js.map +1 -0
  69. comfyui_frontend_package/static/assets/{WidgetLegacy-Bslv9wZZ.js → WidgetLegacy-B4nipUM9.js} +1 -1
  70. comfyui_frontend_package/static/assets/{WidgetRecordAudio-Bzy8PIzN.js → WidgetRecordAudio-Nk8dH238.js} +2 -2
  71. comfyui_frontend_package/static/assets/{WidgetRecordAudio-Bzy8PIzN.js.map → WidgetRecordAudio-Nk8dH238.js.map} +1 -1
  72. comfyui_frontend_package/static/assets/WidgetSelect-DzZPpO_-.js +1 -0
  73. comfyui_frontend_package/static/assets/{WidgetSelect-zgrFVzHH.js → WidgetSelect-nSQrk_hd.js} +2 -2
  74. comfyui_frontend_package/static/assets/{WidgetSelect-zgrFVzHH.js.map → WidgetSelect-nSQrk_hd.js.map} +1 -1
  75. comfyui_frontend_package/static/assets/{WidgetWithControl-Dh2FWOiA.js → WidgetWithControl-Da6zUB5e.js} +3 -3
  76. comfyui_frontend_package/static/assets/{WidgetWithControl-Dh2FWOiA.js.map → WidgetWithControl-Da6zUB5e.js.map} +1 -1
  77. comfyui_frontend_package/static/assets/{api-CUAc7rDA.js → api-Dwq2LQIW.js} +4 -4
  78. comfyui_frontend_package/static/assets/api-Dwq2LQIW.js.map +1 -0
  79. comfyui_frontend_package/static/assets/{audioService-DvndbCi2.js → audioService-DvVaKhuU.js} +2 -2
  80. comfyui_frontend_package/static/assets/{audioService-DvndbCi2.js.map → audioService-DvVaKhuU.js.map} +1 -1
  81. comfyui_frontend_package/static/assets/{audioUtils-DpjpcKbH.js → audioUtils-DD4rUYVZ.js} +2 -2
  82. comfyui_frontend_package/static/assets/{audioUtils-DpjpcKbH.js.map → audioUtils-DD4rUYVZ.js.map} +1 -1
  83. comfyui_frontend_package/static/assets/{auth-B8ZZ0KKQ.js → auth-B9axG-yZ.js} +2 -2
  84. comfyui_frontend_package/static/assets/{auth-B8ZZ0KKQ.js.map → auth-B9axG-yZ.js.map} +1 -1
  85. comfyui_frontend_package/static/assets/auth-D74DTev8.js +1 -0
  86. comfyui_frontend_package/static/assets/{cloudBadges-C1a7fBky.js → cloudBadges-D5mGJbRy.js} +2 -2
  87. comfyui_frontend_package/static/assets/{cloudBadges-C1a7fBky.js.map → cloudBadges-D5mGJbRy.js.map} +1 -1
  88. comfyui_frontend_package/static/assets/{cloudFeedbackTopbarButton-DR0T8sWG.js → cloudFeedbackTopbarButton-RZUssOmb.js} +2 -2
  89. comfyui_frontend_package/static/assets/{cloudFeedbackTopbarButton-DR0T8sWG.js.map → cloudFeedbackTopbarButton-RZUssOmb.js.map} +1 -1
  90. comfyui_frontend_package/static/assets/{cloudRemoteConfig-DhMjC5TB.js → cloudRemoteConfig-F9J0iGyF.js} +2 -2
  91. comfyui_frontend_package/static/assets/{cloudRemoteConfig-DhMjC5TB.js.map → cloudRemoteConfig-F9J0iGyF.js.map} +1 -1
  92. comfyui_frontend_package/static/assets/{cloudSessionCookie-Duxk6ux1.js → cloudSessionCookie-MEORlqtg.js} +2 -2
  93. comfyui_frontend_package/static/assets/{cloudSessionCookie-Duxk6ux1.js.map → cloudSessionCookie-MEORlqtg.js.map} +1 -1
  94. comfyui_frontend_package/static/assets/{cloudSubscription-B8l6B9Nx.js → cloudSubscription-CuWNXKVx.js} +2 -2
  95. comfyui_frontend_package/static/assets/{cloudSubscription-B8l6B9Nx.js.map → cloudSubscription-CuWNXKVx.js.map} +1 -1
  96. comfyui_frontend_package/static/assets/{core-DBfeqMDR.js → core-IYu8XAIx.js} +4 -4
  97. comfyui_frontend_package/static/assets/{core-DBfeqMDR.js.map → core-IYu8XAIx.js.map} +1 -1
  98. comfyui_frontend_package/static/assets/{dialogService-BZ1FmjZL.js → dialogService-YG0RH337.js} +9 -9
  99. comfyui_frontend_package/static/assets/{dialogService-BZ1FmjZL.js.map → dialogService-YG0RH337.js.map} +1 -1
  100. comfyui_frontend_package/static/assets/firebaseAuthStore-DnNaPbuZ.js +1 -0
  101. comfyui_frontend_package/static/assets/{graphHasMissingNodes-C79Wi51S.js → graphHasMissingNodes-BhD1N6zI.js} +2 -2
  102. comfyui_frontend_package/static/assets/{graphHasMissingNodes-C79Wi51S.js.map → graphHasMissingNodes-BhD1N6zI.js.map} +1 -1
  103. comfyui_frontend_package/static/assets/{index-CGxJFSof.js → index-BMy3twho.js} +3 -3
  104. comfyui_frontend_package/static/assets/{index-CGxJFSof.js.map → index-BMy3twho.js.map} +1 -1
  105. comfyui_frontend_package/static/assets/{index-DNpOhRra.css → index-KMO9qFHH.css} +1 -1
  106. comfyui_frontend_package/static/assets/{keybindingService-B88NjeAU.js → keybindingService-CBLPjYHI.js} +2 -2
  107. comfyui_frontend_package/static/assets/{keybindingService-B88NjeAU.js.map → keybindingService-CBLPjYHI.js.map} +1 -1
  108. comfyui_frontend_package/static/assets/{releaseStore-x0vHjxrw.js → releaseStore-DDOxzkVb.js} +2 -2
  109. comfyui_frontend_package/static/assets/{releaseStore-x0vHjxrw.js.map → releaseStore-DDOxzkVb.js.map} +1 -1
  110. comfyui_frontend_package/static/assets/releaseStore-iVkqunL8.js +1 -0
  111. comfyui_frontend_package/static/assets/{subscriptionCheckoutUtil-B_OvUP2T.js → subscriptionCheckoutUtil-DswSOreM.js} +2 -2
  112. comfyui_frontend_package/static/assets/{subscriptionCheckoutUtil-B_OvUP2T.js.map → subscriptionCheckoutUtil-DswSOreM.js.map} +1 -1
  113. comfyui_frontend_package/static/assets/{useCurrentUser-BJcn2Vgo.js → useCurrentUser-NdaCJzIK.js} +1 -1
  114. comfyui_frontend_package/static/assets/{useErrorHandling-CI8_F4yx.js → useErrorHandling-Cfa5N_7c.js} +2 -2
  115. comfyui_frontend_package/static/assets/{useErrorHandling-CI8_F4yx.js.map → useErrorHandling-Cfa5N_7c.js.map} +1 -1
  116. comfyui_frontend_package/static/assets/{useSubscriptionDialog-Chxkdny5.js → useSubscriptionDialog-792qfEJ2.js} +3 -3
  117. comfyui_frontend_package/static/assets/{useSubscriptionDialog-Chxkdny5.js.map → useSubscriptionDialog-792qfEJ2.js.map} +1 -1
  118. comfyui_frontend_package/static/assets/useSubscriptionDialog-B-eGeK3j.js +1 -0
  119. comfyui_frontend_package/static/assets/{userStore-BkgQPjq6.js → userStore-BAS9m9W6.js} +2 -2
  120. comfyui_frontend_package/static/assets/{userStore-BkgQPjq6.js.map → userStore-BAS9m9W6.js.map} +1 -1
  121. comfyui_frontend_package/static/assets/vendor-three-BFcUNSs9.js.map +1 -1
  122. comfyui_frontend_package/static/index.html +1 -1
  123. {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/METADATA +1 -1
  124. {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/RECORD +126 -126
  125. comfyui_frontend_package/static/assets/AudioPreviewPlayer-BoEdyGI_.js +0 -1
  126. comfyui_frontend_package/static/assets/CloudRunButtonWrapper-hQc4BNkX.js +0 -3
  127. comfyui_frontend_package/static/assets/ComfyQueueButton-BbQnRThI.js +0 -1
  128. comfyui_frontend_package/static/assets/GraphView-BCkpNGgz.js.map +0 -1
  129. comfyui_frontend_package/static/assets/Load3D-DHBmC_AU.js +0 -1
  130. comfyui_frontend_package/static/assets/WidgetInputNumber-DGKypM5j.js +0 -3
  131. comfyui_frontend_package/static/assets/WidgetInputNumber-DGKypM5j.js.map +0 -1
  132. comfyui_frontend_package/static/assets/WidgetSelect-DsJGH12l.js +0 -1
  133. comfyui_frontend_package/static/assets/api-CUAc7rDA.js.map +0 -1
  134. comfyui_frontend_package/static/assets/auth-D3RiiqZ8.js +0 -1
  135. comfyui_frontend_package/static/assets/firebaseAuthStore-CZgxeMyf.js +0 -1
  136. comfyui_frontend_package/static/assets/releaseStore-CubqSv5t.js +0 -1
  137. comfyui_frontend_package/static/assets/useSubscriptionDialog-BzMzio2H.js +0 -1
  138. {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/WHEEL +0 -0
  139. {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/top_level.txt +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"SubscriptionPanel-D9uv7z8f.js","names":[],"sources":["../../src/platform/cloud/subscription/composables/useSubscriptionActions.ts","../../src/platform/cloud/subscription/composables/useSubscriptionCredits.ts","../../src/platform/cloud/subscription/components/SubscriptionPanel.vue","../../src/platform/cloud/subscription/components/SubscriptionPanel.vue"],"sourcesContent":["import { onMounted, ref } from 'vue'\n\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { useDialogService } from '@/services/dialogService'\nimport { useCommandStore } from '@/stores/commandStore'\n\n/**\n * Composable for handling subscription panel actions and loading states\n */\nexport function useSubscriptionActions() {\n const dialogService = useDialogService()\n const authActions = useFirebaseAuthActions()\n const commandStore = useCommandStore()\n const telemetry = useTelemetry()\n const { fetchStatus } = useSubscription()\n\n const isLoadingSupport = ref(false)\n\n onMounted(() => {\n void handleRefresh()\n })\n\n const handleAddApiCredits = () => {\n dialogService.showTopUpCreditsDialog()\n }\n\n const handleMessageSupport = async () => {\n try {\n isLoadingSupport.value = true\n if (isCloud) {\n telemetry?.trackHelpResourceClicked({\n resource_type: 'help_feedback',\n is_external: true,\n source: 'subscription'\n })\n }\n await commandStore.execute('Comfy.ContactSupport')\n } catch (error) {\n console.error('[useSubscriptionActions] Error contacting support:', error)\n } finally {\n isLoadingSupport.value = false\n }\n }\n\n const handleRefresh = async () => {\n try {\n await Promise.all([authActions.fetchBalance(), fetchStatus()])\n } catch (error) {\n console.error('[useSubscriptionActions] Error refreshing data:', error)\n }\n }\n\n const handleLearnMoreClick = () => {\n window.open('https://docs.comfy.org/get_started/cloud', '_blank')\n }\n\n return {\n isLoadingSupport,\n handleAddApiCredits,\n handleMessageSupport,\n handleRefresh,\n handleLearnMoreClick\n }\n}\n","import { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport { formatCreditsFromCents } from '@/base/credits/comfyCredits'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\n\n/**\n * Composable for handling subscription credit calculations and formatting\n */\nexport function useSubscriptionCredits() {\n const authStore = useFirebaseAuthStore()\n const { locale } = useI18n()\n\n const formatBalance = (maybeCents?: number) => {\n // Backend returns cents despite the *_micros naming convention.\n const cents = maybeCents ?? 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value,\n numberOptions: {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0\n }\n })\n return amount\n }\n\n const totalCredits = computed(() =>\n formatBalance(authStore.balance?.amount_micros)\n )\n\n const monthlyBonusCredits = computed(() =>\n formatBalance(authStore.balance?.cloud_credit_balance_micros)\n )\n\n const prepaidCredits = computed(() =>\n formatBalance(authStore.balance?.prepaid_balance_micros)\n )\n\n const isLoadingBalance = computed(() => authStore.isFetchingBalance)\n\n return {\n totalCredits,\n monthlyBonusCredits,\n prepaidCredits,\n isLoadingBalance\n }\n}\n","<template>\n <TabPanel value=\"PlanCredits\" class=\"subscription-container h-full\">\n <div class=\"flex h-full flex-col gap-6\">\n <div class=\"flex items-center gap-2\">\n <span class=\"text-2xl font-inter font-semibold leading-tight\">\n {{\n isActiveSubscription\n ? $t('subscription.title')\n : $t('subscription.titleUnsubscribed')\n }}\n </span>\n <div class=\"pt-1\">\n <CloudBadge\n reverse-order\n background-color=\"var(--p-dialog-background)\"\n />\n </div>\n </div>\n\n <div class=\"grow overflow-auto\">\n <div class=\"rounded-2xl border border-interface-stroke p-6\">\n <div>\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm font-bold text-text-primary\">\n {{ subscriptionTierName }}\n </div>\n <div class=\"flex items-baseline gap-1 font-inter font-semibold\">\n <span class=\"text-2xl\">${{ tierPrice }}</span>\n <span class=\"text-base\">{{\n $t('subscription.perMonth')\n }}</span>\n </div>\n <div\n v-if=\"isActiveSubscription\"\n class=\"text-sm text-text-secondary\"\n >\n <template v-if=\"isCancelled\">\n {{\n $t('subscription.expiresDate', {\n date: formattedEndDate\n })\n }}\n </template>\n <template v-else>\n {{\n $t('subscription.renewsDate', {\n date: formattedRenewalDate\n })\n }}\n </template>\n </div>\n </div>\n\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"secondary\"\n class=\"ml-auto rounded-lg px-4 py-2 text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected\"\n @click=\"\n async () => {\n await authActions.accessBillingPortal()\n }\n \"\n >\n {{ $t('subscription.manageSubscription') }}\n </Button>\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"primary\"\n class=\"rounded-lg px-4 py-2 text-sm font-normal text-text-primary\"\n @click=\"showSubscriptionDialog\"\n >\n {{ $t('subscription.upgradePlan') }}\n </Button>\n\n <SubscribeButton\n v-else\n :label=\"$t('subscription.subscribeNow')\"\n size=\"sm\"\n :fluid=\"false\"\n class=\"text-xs\"\n @subscribed=\"handleRefresh\"\n />\n </div>\n </div>\n\n <div class=\"flex flex-col lg:flex-row gap-6 pt-9\">\n <div class=\"flex flex-col shrink-0\">\n <div class=\"flex flex-col gap-3\">\n <div\n :class=\"\n cn(\n 'relative flex flex-col gap-6 rounded-2xl p-5',\n 'bg-modal-panel-background'\n )\n \"\n >\n <Button\n variant=\"muted-textonly\"\n size=\"icon-sm\"\n class=\"absolute top-4 right-4\"\n :loading=\"isLoadingBalance\"\n @click=\"handleRefresh\"\n >\n <i class=\"pi pi-sync text-text-secondary text-sm\" />\n </Button>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm text-muted\">\n {{ $t('subscription.totalCredits') }}\n </div>\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"8rem\"\n height=\"2rem\"\n />\n <div v-else class=\"text-2xl font-bold\">\n {{ totalCredits }}\n </div>\n </div>\n\n <!-- Credit Breakdown -->\n <table class=\"text-sm text-muted\">\n <tbody>\n <tr>\n <td class=\"pr-4 font-bold text-left align-middle\">\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"5rem\"\n height=\"1rem\"\n />\n <span v-else>{{ includedCreditsDisplay }}</span>\n </td>\n <td class=\"align-middle\" :title=\"creditsRemainingLabel\">\n {{ creditsRemainingLabel }}\n </td>\n </tr>\n <tr>\n <td class=\"pr-4 font-bold text-left align-middle\">\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"3rem\"\n height=\"1rem\"\n />\n <span v-else>{{ prepaidCredits }}</span>\n </td>\n <td\n class=\"align-middle\"\n :title=\"$t('subscription.creditsYouveAdded')\"\n >\n {{ $t('subscription.creditsYouveAdded') }}\n </td>\n </tr>\n </tbody>\n </table>\n\n <div class=\"flex items-center justify-between\">\n <a\n :href=\"usageHistoryUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-sm underline text-center text-muted\"\n >\n {{ $t('subscription.viewUsageHistory') }}\n </a>\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"secondary\"\n class=\"p-2 min-h-8 rounded-lg text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected\"\n @click=\"handleAddApiCredits\"\n >\n {{ $t('subscription.addCredits') }}\n </Button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm text-text-primary\">\n {{ $t('subscription.yourPlanIncludes') }}\n </div>\n\n <div class=\"flex flex-col gap-0\">\n <div\n v-for=\"benefit in tierBenefits\"\n :key=\"benefit.key\"\n class=\"flex items-center gap-2 py-2\"\n >\n <i\n v-if=\"benefit.type === 'feature'\"\n class=\"pi pi-check text-xs text-text-primary\"\n />\n <span\n v-else-if=\"benefit.type === 'metric' && benefit.value\"\n class=\"text-sm font-normal whitespace-nowrap text-text-primary\"\n >\n {{ benefit.value }}\n </span>\n <span class=\"text-sm text-muted\">\n {{ benefit.label }}\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- View More Details - Outside main content -->\n <div class=\"flex items-center gap-2 py-4\">\n <i class=\"pi pi-external-link text-muted\"></i>\n <a\n href=\"https://www.comfy.org/cloud/pricing\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-sm underline hover:opacity-80 text-muted\"\n >\n {{ $t('subscription.viewMoreDetailsPlans') }}\n </a>\n </div>\n </div>\n\n <div\n class=\"flex items-center justify-between border-t border-interface-stroke pt-3\"\n >\n <div class=\"flex gap-2\">\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleLearnMoreClick\"\n >\n <i class=\"pi pi-question-circle text-text-secondary text-xs\" />\n {{ $t('subscription.learnMore') }}\n </Button>\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleOpenPartnerNodesInfo\"\n >\n <i class=\"pi pi-question-circle text-text-secondary text-xs\" />\n {{ $t('subscription.partnerNodesCredits') }}\n </Button>\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n :loading=\"isLoadingSupport\"\n @click=\"handleMessageSupport\"\n >\n <i class=\"pi pi-comment text-text-secondary text-xs\" />\n {{ $t('subscription.messageSupport') }}\n </Button>\n </div>\n\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleInvoiceHistory\"\n >\n {{ $t('subscription.invoiceHistory') }}\n <i class=\"pi pi-external-link text-text-secondary text-xs\" />\n </Button>\n </div>\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\nimport TabPanel from 'primevue/tabpanel'\nimport { computed, onBeforeUnmount, onMounted } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport CloudBadge from '@/components/topbar/CloudBadge.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useExternalLink } from '@/composables/useExternalLink'\nimport { getComfyPlatformBaseUrl } from '@/config/comfyApi'\nimport SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { useSubscriptionActions } from '@/platform/cloud/subscription/composables/useSubscriptionActions'\nimport { useSubscriptionCredits } from '@/platform/cloud/subscription/composables/useSubscriptionCredits'\nimport { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'\nimport {\n DEFAULT_TIER_KEY,\n TIER_TO_KEY,\n getTierCredits,\n getTierFeatures,\n getTierPrice\n} from '@/platform/cloud/subscription/constants/tierPricing'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst { buildDocsUrl, docsPaths } = useExternalLink()\nconst authActions = useFirebaseAuthActions()\nconst { t, n } = useI18n()\n\nconst {\n isActiveSubscription,\n isCancelled,\n formattedRenewalDate,\n formattedEndDate,\n subscriptionTier,\n subscriptionTierName,\n subscriptionStatus,\n isYearlySubscription,\n handleInvoiceHistory\n} = useSubscription()\n\nconst { show: showSubscriptionDialog } = useSubscriptionDialog()\n\nconst tierKey = computed(() => {\n const tier = subscriptionTier.value\n if (!tier) return DEFAULT_TIER_KEY\n return TIER_TO_KEY[tier] ?? DEFAULT_TIER_KEY\n})\nconst tierPrice = computed(() =>\n getTierPrice(tierKey.value, isYearlySubscription.value)\n)\nconst usageHistoryUrl = computed(\n () => `${getComfyPlatformBaseUrl()}/profile/usage`\n)\n\nconst refillsDate = computed(() => {\n if (!subscriptionStatus.value?.renewal_date) return ''\n const date = new Date(subscriptionStatus.value.renewal_date)\n const day = String(date.getDate()).padStart(2, '0')\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const year = String(date.getFullYear()).slice(-2)\n return `${month}/${day}/${year}`\n})\n\nconst creditsRemainingLabel = computed(() =>\n isYearlySubscription.value\n ? t('subscription.creditsRemainingThisYear', {\n date: refillsDate.value\n })\n : t('subscription.creditsRemainingThisMonth', {\n date: refillsDate.value\n })\n)\n\nconst planTotalCredits = computed(() => {\n const credits = getTierCredits(tierKey.value)\n const total = isYearlySubscription.value ? credits * 12 : credits\n return n(total)\n})\n\nconst includedCreditsDisplay = computed(\n () => `${monthlyBonusCredits.value} / ${planTotalCredits.value}`\n)\n\n// Tier benefits for v-for loop\ntype BenefitType = 'metric' | 'feature'\n\ninterface Benefit {\n key: string\n type: BenefitType\n label: string\n value?: string\n}\n\nconst tierBenefits = computed((): Benefit[] => {\n const key = tierKey.value\n\n const benefits: Benefit[] = [\n {\n key: 'maxDuration',\n type: 'metric',\n value: t(`subscription.maxDuration.${key}`),\n label: t('subscription.maxDurationLabel')\n },\n {\n key: 'gpu',\n type: 'feature',\n label: t('subscription.gpuLabel')\n },\n {\n key: 'addCredits',\n type: 'feature',\n label: t('subscription.addCreditsLabel')\n }\n ]\n\n if (getTierFeatures(key).customLoRAs) {\n benefits.push({\n key: 'customLoRAs',\n type: 'feature',\n label: t('subscription.customLoRAsLabel')\n })\n }\n\n return benefits\n})\n\nconst { totalCredits, monthlyBonusCredits, prepaidCredits, isLoadingBalance } =\n useSubscriptionCredits()\n\nconst {\n isLoadingSupport,\n handleAddApiCredits,\n handleMessageSupport,\n handleRefresh,\n handleLearnMoreClick\n} = useSubscriptionActions()\n\n// Focus-based polling: refresh balance when user returns from Stripe checkout\nconst PENDING_TOPUP_KEY = 'pending_topup_timestamp'\nconst TOPUP_EXPIRY_MS = 5 * 60 * 1000 // 5 minutes\n\nfunction handleWindowFocus() {\n const timestampStr = localStorage.getItem(PENDING_TOPUP_KEY)\n if (!timestampStr) return\n\n const timestamp = parseInt(timestampStr, 10)\n\n // Clear expired tracking (older than 5 minutes)\n if (Date.now() - timestamp > TOPUP_EXPIRY_MS) {\n localStorage.removeItem(PENDING_TOPUP_KEY)\n return\n }\n\n // Refresh and clear tracking to prevent repeated calls\n void handleRefresh()\n localStorage.removeItem(PENDING_TOPUP_KEY)\n}\n\nonMounted(() => {\n window.addEventListener('focus', handleWindowFocus)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('focus', handleWindowFocus)\n})\n\nconst handleOpenPartnerNodesInfo = () => {\n window.open(\n buildDocsUrl(docsPaths.partnerNodesPricing, { includeLocale: true }),\n '_blank'\n )\n}\n</script>\n\n<style scoped>\n:deep(.bg-comfy-menu-secondary) {\n background-color: transparent;\n}\n</style>\n","<template>\n <TabPanel value=\"PlanCredits\" class=\"subscription-container h-full\">\n <div class=\"flex h-full flex-col gap-6\">\n <div class=\"flex items-center gap-2\">\n <span class=\"text-2xl font-inter font-semibold leading-tight\">\n {{\n isActiveSubscription\n ? $t('subscription.title')\n : $t('subscription.titleUnsubscribed')\n }}\n </span>\n <div class=\"pt-1\">\n <CloudBadge\n reverse-order\n background-color=\"var(--p-dialog-background)\"\n />\n </div>\n </div>\n\n <div class=\"grow overflow-auto\">\n <div class=\"rounded-2xl border border-interface-stroke p-6\">\n <div>\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm font-bold text-text-primary\">\n {{ subscriptionTierName }}\n </div>\n <div class=\"flex items-baseline gap-1 font-inter font-semibold\">\n <span class=\"text-2xl\">${{ tierPrice }}</span>\n <span class=\"text-base\">{{\n $t('subscription.perMonth')\n }}</span>\n </div>\n <div\n v-if=\"isActiveSubscription\"\n class=\"text-sm text-text-secondary\"\n >\n <template v-if=\"isCancelled\">\n {{\n $t('subscription.expiresDate', {\n date: formattedEndDate\n })\n }}\n </template>\n <template v-else>\n {{\n $t('subscription.renewsDate', {\n date: formattedRenewalDate\n })\n }}\n </template>\n </div>\n </div>\n\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"secondary\"\n class=\"ml-auto rounded-lg px-4 py-2 text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected\"\n @click=\"\n async () => {\n await authActions.accessBillingPortal()\n }\n \"\n >\n {{ $t('subscription.manageSubscription') }}\n </Button>\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"primary\"\n class=\"rounded-lg px-4 py-2 text-sm font-normal text-text-primary\"\n @click=\"showSubscriptionDialog\"\n >\n {{ $t('subscription.upgradePlan') }}\n </Button>\n\n <SubscribeButton\n v-else\n :label=\"$t('subscription.subscribeNow')\"\n size=\"sm\"\n :fluid=\"false\"\n class=\"text-xs\"\n @subscribed=\"handleRefresh\"\n />\n </div>\n </div>\n\n <div class=\"flex flex-col lg:flex-row gap-6 pt-9\">\n <div class=\"flex flex-col shrink-0\">\n <div class=\"flex flex-col gap-3\">\n <div\n :class=\"\n cn(\n 'relative flex flex-col gap-6 rounded-2xl p-5',\n 'bg-modal-panel-background'\n )\n \"\n >\n <Button\n variant=\"muted-textonly\"\n size=\"icon-sm\"\n class=\"absolute top-4 right-4\"\n :loading=\"isLoadingBalance\"\n @click=\"handleRefresh\"\n >\n <i class=\"pi pi-sync text-text-secondary text-sm\" />\n </Button>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm text-muted\">\n {{ $t('subscription.totalCredits') }}\n </div>\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"8rem\"\n height=\"2rem\"\n />\n <div v-else class=\"text-2xl font-bold\">\n {{ totalCredits }}\n </div>\n </div>\n\n <!-- Credit Breakdown -->\n <table class=\"text-sm text-muted\">\n <tbody>\n <tr>\n <td class=\"pr-4 font-bold text-left align-middle\">\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"5rem\"\n height=\"1rem\"\n />\n <span v-else>{{ includedCreditsDisplay }}</span>\n </td>\n <td class=\"align-middle\" :title=\"creditsRemainingLabel\">\n {{ creditsRemainingLabel }}\n </td>\n </tr>\n <tr>\n <td class=\"pr-4 font-bold text-left align-middle\">\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"3rem\"\n height=\"1rem\"\n />\n <span v-else>{{ prepaidCredits }}</span>\n </td>\n <td\n class=\"align-middle\"\n :title=\"$t('subscription.creditsYouveAdded')\"\n >\n {{ $t('subscription.creditsYouveAdded') }}\n </td>\n </tr>\n </tbody>\n </table>\n\n <div class=\"flex items-center justify-between\">\n <a\n :href=\"usageHistoryUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-sm underline text-center text-muted\"\n >\n {{ $t('subscription.viewUsageHistory') }}\n </a>\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"secondary\"\n class=\"p-2 min-h-8 rounded-lg text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected\"\n @click=\"handleAddApiCredits\"\n >\n {{ $t('subscription.addCredits') }}\n </Button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm text-text-primary\">\n {{ $t('subscription.yourPlanIncludes') }}\n </div>\n\n <div class=\"flex flex-col gap-0\">\n <div\n v-for=\"benefit in tierBenefits\"\n :key=\"benefit.key\"\n class=\"flex items-center gap-2 py-2\"\n >\n <i\n v-if=\"benefit.type === 'feature'\"\n class=\"pi pi-check text-xs text-text-primary\"\n />\n <span\n v-else-if=\"benefit.type === 'metric' && benefit.value\"\n class=\"text-sm font-normal whitespace-nowrap text-text-primary\"\n >\n {{ benefit.value }}\n </span>\n <span class=\"text-sm text-muted\">\n {{ benefit.label }}\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- View More Details - Outside main content -->\n <div class=\"flex items-center gap-2 py-4\">\n <i class=\"pi pi-external-link text-muted\"></i>\n <a\n href=\"https://www.comfy.org/cloud/pricing\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-sm underline hover:opacity-80 text-muted\"\n >\n {{ $t('subscription.viewMoreDetailsPlans') }}\n </a>\n </div>\n </div>\n\n <div\n class=\"flex items-center justify-between border-t border-interface-stroke pt-3\"\n >\n <div class=\"flex gap-2\">\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleLearnMoreClick\"\n >\n <i class=\"pi pi-question-circle text-text-secondary text-xs\" />\n {{ $t('subscription.learnMore') }}\n </Button>\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleOpenPartnerNodesInfo\"\n >\n <i class=\"pi pi-question-circle text-text-secondary text-xs\" />\n {{ $t('subscription.partnerNodesCredits') }}\n </Button>\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n :loading=\"isLoadingSupport\"\n @click=\"handleMessageSupport\"\n >\n <i class=\"pi pi-comment text-text-secondary text-xs\" />\n {{ $t('subscription.messageSupport') }}\n </Button>\n </div>\n\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleInvoiceHistory\"\n >\n {{ $t('subscription.invoiceHistory') }}\n <i class=\"pi pi-external-link text-text-secondary text-xs\" />\n </Button>\n </div>\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\nimport TabPanel from 'primevue/tabpanel'\nimport { computed, onBeforeUnmount, onMounted } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport CloudBadge from '@/components/topbar/CloudBadge.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useExternalLink } from '@/composables/useExternalLink'\nimport { getComfyPlatformBaseUrl } from '@/config/comfyApi'\nimport SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { useSubscriptionActions } from '@/platform/cloud/subscription/composables/useSubscriptionActions'\nimport { useSubscriptionCredits } from '@/platform/cloud/subscription/composables/useSubscriptionCredits'\nimport { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'\nimport {\n DEFAULT_TIER_KEY,\n TIER_TO_KEY,\n getTierCredits,\n getTierFeatures,\n getTierPrice\n} from '@/platform/cloud/subscription/constants/tierPricing'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst { buildDocsUrl, docsPaths } = useExternalLink()\nconst authActions = useFirebaseAuthActions()\nconst { t, n } = useI18n()\n\nconst {\n isActiveSubscription,\n isCancelled,\n formattedRenewalDate,\n formattedEndDate,\n subscriptionTier,\n subscriptionTierName,\n subscriptionStatus,\n isYearlySubscription,\n handleInvoiceHistory\n} = useSubscription()\n\nconst { show: showSubscriptionDialog } = useSubscriptionDialog()\n\nconst tierKey = computed(() => {\n const tier = subscriptionTier.value\n if (!tier) return DEFAULT_TIER_KEY\n return TIER_TO_KEY[tier] ?? DEFAULT_TIER_KEY\n})\nconst tierPrice = computed(() =>\n getTierPrice(tierKey.value, isYearlySubscription.value)\n)\nconst usageHistoryUrl = computed(\n () => `${getComfyPlatformBaseUrl()}/profile/usage`\n)\n\nconst refillsDate = computed(() => {\n if (!subscriptionStatus.value?.renewal_date) return ''\n const date = new Date(subscriptionStatus.value.renewal_date)\n const day = String(date.getDate()).padStart(2, '0')\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const year = String(date.getFullYear()).slice(-2)\n return `${month}/${day}/${year}`\n})\n\nconst creditsRemainingLabel = computed(() =>\n isYearlySubscription.value\n ? t('subscription.creditsRemainingThisYear', {\n date: refillsDate.value\n })\n : t('subscription.creditsRemainingThisMonth', {\n date: refillsDate.value\n })\n)\n\nconst planTotalCredits = computed(() => {\n const credits = getTierCredits(tierKey.value)\n const total = isYearlySubscription.value ? credits * 12 : credits\n return n(total)\n})\n\nconst includedCreditsDisplay = computed(\n () => `${monthlyBonusCredits.value} / ${planTotalCredits.value}`\n)\n\n// Tier benefits for v-for loop\ntype BenefitType = 'metric' | 'feature'\n\ninterface Benefit {\n key: string\n type: BenefitType\n label: string\n value?: string\n}\n\nconst tierBenefits = computed((): Benefit[] => {\n const key = tierKey.value\n\n const benefits: Benefit[] = [\n {\n key: 'maxDuration',\n type: 'metric',\n value: t(`subscription.maxDuration.${key}`),\n label: t('subscription.maxDurationLabel')\n },\n {\n key: 'gpu',\n type: 'feature',\n label: t('subscription.gpuLabel')\n },\n {\n key: 'addCredits',\n type: 'feature',\n label: t('subscription.addCreditsLabel')\n }\n ]\n\n if (getTierFeatures(key).customLoRAs) {\n benefits.push({\n key: 'customLoRAs',\n type: 'feature',\n label: t('subscription.customLoRAsLabel')\n })\n }\n\n return benefits\n})\n\nconst { totalCredits, monthlyBonusCredits, prepaidCredits, isLoadingBalance } =\n useSubscriptionCredits()\n\nconst {\n isLoadingSupport,\n handleAddApiCredits,\n handleMessageSupport,\n handleRefresh,\n handleLearnMoreClick\n} = useSubscriptionActions()\n\n// Focus-based polling: refresh balance when user returns from Stripe checkout\nconst PENDING_TOPUP_KEY = 'pending_topup_timestamp'\nconst TOPUP_EXPIRY_MS = 5 * 60 * 1000 // 5 minutes\n\nfunction handleWindowFocus() {\n const timestampStr = localStorage.getItem(PENDING_TOPUP_KEY)\n if (!timestampStr) return\n\n const timestamp = parseInt(timestampStr, 10)\n\n // Clear expired tracking (older than 5 minutes)\n if (Date.now() - timestamp > TOPUP_EXPIRY_MS) {\n localStorage.removeItem(PENDING_TOPUP_KEY)\n return\n }\n\n // Refresh and clear tracking to prevent repeated calls\n void handleRefresh()\n localStorage.removeItem(PENDING_TOPUP_KEY)\n}\n\nonMounted(() => {\n window.addEventListener('focus', handleWindowFocus)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('focus', handleWindowFocus)\n})\n\nconst handleOpenPartnerNodesInfo = () => {\n window.open(\n buildDocsUrl(docsPaths.partnerNodesPricing, { includeLocale: true }),\n '_blank'\n )\n}\n</script>\n\n<style scoped>\n:deep(.bg-comfy-menu-secondary) {\n background-color: transparent;\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"SubscriptionPanel-B6txX4Vm.js","names":[],"sources":["../../src/platform/cloud/subscription/composables/useSubscriptionActions.ts","../../src/platform/cloud/subscription/composables/useSubscriptionCredits.ts","../../src/platform/cloud/subscription/components/SubscriptionPanel.vue","../../src/platform/cloud/subscription/components/SubscriptionPanel.vue"],"sourcesContent":["import { onMounted, ref } from 'vue'\n\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { useDialogService } from '@/services/dialogService'\nimport { useCommandStore } from '@/stores/commandStore'\n\n/**\n * Composable for handling subscription panel actions and loading states\n */\nexport function useSubscriptionActions() {\n const dialogService = useDialogService()\n const authActions = useFirebaseAuthActions()\n const commandStore = useCommandStore()\n const telemetry = useTelemetry()\n const { fetchStatus } = useSubscription()\n\n const isLoadingSupport = ref(false)\n\n onMounted(() => {\n void handleRefresh()\n })\n\n const handleAddApiCredits = () => {\n dialogService.showTopUpCreditsDialog()\n }\n\n const handleMessageSupport = async () => {\n try {\n isLoadingSupport.value = true\n if (isCloud) {\n telemetry?.trackHelpResourceClicked({\n resource_type: 'help_feedback',\n is_external: true,\n source: 'subscription'\n })\n }\n await commandStore.execute('Comfy.ContactSupport')\n } catch (error) {\n console.error('[useSubscriptionActions] Error contacting support:', error)\n } finally {\n isLoadingSupport.value = false\n }\n }\n\n const handleRefresh = async () => {\n try {\n await Promise.all([authActions.fetchBalance(), fetchStatus()])\n } catch (error) {\n console.error('[useSubscriptionActions] Error refreshing data:', error)\n }\n }\n\n const handleLearnMoreClick = () => {\n window.open('https://docs.comfy.org/get_started/cloud', '_blank')\n }\n\n return {\n isLoadingSupport,\n handleAddApiCredits,\n handleMessageSupport,\n handleRefresh,\n handleLearnMoreClick\n }\n}\n","import { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport { formatCreditsFromCents } from '@/base/credits/comfyCredits'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\n\n/**\n * Composable for handling subscription credit calculations and formatting\n */\nexport function useSubscriptionCredits() {\n const authStore = useFirebaseAuthStore()\n const { locale } = useI18n()\n\n const formatBalance = (maybeCents?: number) => {\n // Backend returns cents despite the *_micros naming convention.\n const cents = maybeCents ?? 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value,\n numberOptions: {\n minimumFractionDigits: 0,\n maximumFractionDigits: 0\n }\n })\n return amount\n }\n\n const totalCredits = computed(() =>\n formatBalance(authStore.balance?.amount_micros)\n )\n\n const monthlyBonusCredits = computed(() =>\n formatBalance(authStore.balance?.cloud_credit_balance_micros)\n )\n\n const prepaidCredits = computed(() =>\n formatBalance(authStore.balance?.prepaid_balance_micros)\n )\n\n const isLoadingBalance = computed(() => authStore.isFetchingBalance)\n\n return {\n totalCredits,\n monthlyBonusCredits,\n prepaidCredits,\n isLoadingBalance\n }\n}\n","<template>\n <TabPanel value=\"PlanCredits\" class=\"subscription-container h-full\">\n <div class=\"flex h-full flex-col gap-6\">\n <div class=\"flex items-center gap-2\">\n <span class=\"text-2xl font-inter font-semibold leading-tight\">\n {{\n isActiveSubscription\n ? $t('subscription.title')\n : $t('subscription.titleUnsubscribed')\n }}\n </span>\n <div class=\"pt-1\">\n <CloudBadge\n reverse-order\n background-color=\"var(--p-dialog-background)\"\n />\n </div>\n </div>\n\n <div class=\"grow overflow-auto\">\n <div class=\"rounded-2xl border border-interface-stroke p-6\">\n <div>\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm font-bold text-text-primary\">\n {{ subscriptionTierName }}\n </div>\n <div class=\"flex items-baseline gap-1 font-inter font-semibold\">\n <span class=\"text-2xl\">${{ tierPrice }}</span>\n <span class=\"text-base\">{{\n $t('subscription.perMonth')\n }}</span>\n </div>\n <div\n v-if=\"isActiveSubscription\"\n class=\"text-sm text-text-secondary\"\n >\n <template v-if=\"isCancelled\">\n {{\n $t('subscription.expiresDate', {\n date: formattedEndDate\n })\n }}\n </template>\n <template v-else>\n {{\n $t('subscription.renewsDate', {\n date: formattedRenewalDate\n })\n }}\n </template>\n </div>\n </div>\n\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"secondary\"\n class=\"ml-auto rounded-lg px-4 py-2 text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected\"\n @click=\"\n async () => {\n await authActions.accessBillingPortal()\n }\n \"\n >\n {{ $t('subscription.manageSubscription') }}\n </Button>\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"primary\"\n class=\"rounded-lg px-4 py-2 text-sm font-normal text-text-primary\"\n @click=\"showSubscriptionDialog\"\n >\n {{ $t('subscription.upgradePlan') }}\n </Button>\n\n <SubscribeButton\n v-else\n :label=\"$t('subscription.subscribeNow')\"\n size=\"sm\"\n :fluid=\"false\"\n class=\"text-xs\"\n @subscribed=\"handleRefresh\"\n />\n </div>\n </div>\n\n <div class=\"flex flex-col lg:flex-row gap-6 pt-9\">\n <div class=\"flex flex-col shrink-0\">\n <div class=\"flex flex-col gap-3\">\n <div\n :class=\"\n cn(\n 'relative flex flex-col gap-6 rounded-2xl p-5',\n 'bg-modal-panel-background'\n )\n \"\n >\n <Button\n variant=\"muted-textonly\"\n size=\"icon-sm\"\n class=\"absolute top-4 right-4\"\n :loading=\"isLoadingBalance\"\n @click=\"handleRefresh\"\n >\n <i class=\"pi pi-sync text-text-secondary text-sm\" />\n </Button>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm text-muted\">\n {{ $t('subscription.totalCredits') }}\n </div>\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"8rem\"\n height=\"2rem\"\n />\n <div v-else class=\"text-2xl font-bold\">\n {{ totalCredits }}\n </div>\n </div>\n\n <!-- Credit Breakdown -->\n <table class=\"text-sm text-muted\">\n <tbody>\n <tr>\n <td class=\"pr-4 font-bold text-left align-middle\">\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"5rem\"\n height=\"1rem\"\n />\n <span v-else>{{ includedCreditsDisplay }}</span>\n </td>\n <td class=\"align-middle\" :title=\"creditsRemainingLabel\">\n {{ creditsRemainingLabel }}\n </td>\n </tr>\n <tr>\n <td class=\"pr-4 font-bold text-left align-middle\">\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"3rem\"\n height=\"1rem\"\n />\n <span v-else>{{ prepaidCredits }}</span>\n </td>\n <td\n class=\"align-middle\"\n :title=\"$t('subscription.creditsYouveAdded')\"\n >\n {{ $t('subscription.creditsYouveAdded') }}\n </td>\n </tr>\n </tbody>\n </table>\n\n <div class=\"flex items-center justify-between\">\n <a\n :href=\"usageHistoryUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-sm underline text-center text-muted\"\n >\n {{ $t('subscription.viewUsageHistory') }}\n </a>\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"secondary\"\n class=\"p-2 min-h-8 rounded-lg text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected\"\n @click=\"handleAddApiCredits\"\n >\n {{ $t('subscription.addCredits') }}\n </Button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm text-text-primary\">\n {{ $t('subscription.yourPlanIncludes') }}\n </div>\n\n <div class=\"flex flex-col gap-0\">\n <div\n v-for=\"benefit in tierBenefits\"\n :key=\"benefit.key\"\n class=\"flex items-center gap-2 py-2\"\n >\n <i\n v-if=\"benefit.type === 'feature'\"\n class=\"pi pi-check text-xs text-text-primary\"\n />\n <span\n v-else-if=\"benefit.type === 'metric' && benefit.value\"\n class=\"text-sm font-normal whitespace-nowrap text-text-primary\"\n >\n {{ benefit.value }}\n </span>\n <span class=\"text-sm text-muted\">\n {{ benefit.label }}\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- View More Details - Outside main content -->\n <div class=\"flex items-center gap-2 py-4\">\n <i class=\"pi pi-external-link text-muted\"></i>\n <a\n href=\"https://www.comfy.org/cloud/pricing\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-sm underline hover:opacity-80 text-muted\"\n >\n {{ $t('subscription.viewMoreDetailsPlans') }}\n </a>\n </div>\n </div>\n\n <div\n class=\"flex items-center justify-between border-t border-interface-stroke pt-3\"\n >\n <div class=\"flex gap-2\">\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleLearnMoreClick\"\n >\n <i class=\"pi pi-question-circle text-text-secondary text-xs\" />\n {{ $t('subscription.learnMore') }}\n </Button>\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleOpenPartnerNodesInfo\"\n >\n <i class=\"pi pi-question-circle text-text-secondary text-xs\" />\n {{ $t('subscription.partnerNodesCredits') }}\n </Button>\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n :loading=\"isLoadingSupport\"\n @click=\"handleMessageSupport\"\n >\n <i class=\"pi pi-comment text-text-secondary text-xs\" />\n {{ $t('subscription.messageSupport') }}\n </Button>\n </div>\n\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleInvoiceHistory\"\n >\n {{ $t('subscription.invoiceHistory') }}\n <i class=\"pi pi-external-link text-text-secondary text-xs\" />\n </Button>\n </div>\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\nimport TabPanel from 'primevue/tabpanel'\nimport { computed, onBeforeUnmount, onMounted } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport CloudBadge from '@/components/topbar/CloudBadge.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useExternalLink } from '@/composables/useExternalLink'\nimport { getComfyPlatformBaseUrl } from '@/config/comfyApi'\nimport SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { useSubscriptionActions } from '@/platform/cloud/subscription/composables/useSubscriptionActions'\nimport { useSubscriptionCredits } from '@/platform/cloud/subscription/composables/useSubscriptionCredits'\nimport { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'\nimport {\n DEFAULT_TIER_KEY,\n TIER_TO_KEY,\n getTierCredits,\n getTierFeatures,\n getTierPrice\n} from '@/platform/cloud/subscription/constants/tierPricing'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst { buildDocsUrl, docsPaths } = useExternalLink()\nconst authActions = useFirebaseAuthActions()\nconst { t, n } = useI18n()\n\nconst {\n isActiveSubscription,\n isCancelled,\n formattedRenewalDate,\n formattedEndDate,\n subscriptionTier,\n subscriptionTierName,\n subscriptionStatus,\n isYearlySubscription,\n handleInvoiceHistory\n} = useSubscription()\n\nconst { show: showSubscriptionDialog } = useSubscriptionDialog()\n\nconst tierKey = computed(() => {\n const tier = subscriptionTier.value\n if (!tier) return DEFAULT_TIER_KEY\n return TIER_TO_KEY[tier] ?? DEFAULT_TIER_KEY\n})\nconst tierPrice = computed(() =>\n getTierPrice(tierKey.value, isYearlySubscription.value)\n)\nconst usageHistoryUrl = computed(\n () => `${getComfyPlatformBaseUrl()}/profile/usage`\n)\n\nconst refillsDate = computed(() => {\n if (!subscriptionStatus.value?.renewal_date) return ''\n const date = new Date(subscriptionStatus.value.renewal_date)\n const day = String(date.getDate()).padStart(2, '0')\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const year = String(date.getFullYear()).slice(-2)\n return `${month}/${day}/${year}`\n})\n\nconst creditsRemainingLabel = computed(() =>\n isYearlySubscription.value\n ? t('subscription.creditsRemainingThisYear', {\n date: refillsDate.value\n })\n : t('subscription.creditsRemainingThisMonth', {\n date: refillsDate.value\n })\n)\n\nconst planTotalCredits = computed(() => {\n const credits = getTierCredits(tierKey.value)\n const total = isYearlySubscription.value ? credits * 12 : credits\n return n(total)\n})\n\nconst includedCreditsDisplay = computed(\n () => `${monthlyBonusCredits.value} / ${planTotalCredits.value}`\n)\n\n// Tier benefits for v-for loop\ntype BenefitType = 'metric' | 'feature'\n\ninterface Benefit {\n key: string\n type: BenefitType\n label: string\n value?: string\n}\n\nconst tierBenefits = computed((): Benefit[] => {\n const key = tierKey.value\n\n const benefits: Benefit[] = [\n {\n key: 'maxDuration',\n type: 'metric',\n value: t(`subscription.maxDuration.${key}`),\n label: t('subscription.maxDurationLabel')\n },\n {\n key: 'gpu',\n type: 'feature',\n label: t('subscription.gpuLabel')\n },\n {\n key: 'addCredits',\n type: 'feature',\n label: t('subscription.addCreditsLabel')\n }\n ]\n\n if (getTierFeatures(key).customLoRAs) {\n benefits.push({\n key: 'customLoRAs',\n type: 'feature',\n label: t('subscription.customLoRAsLabel')\n })\n }\n\n return benefits\n})\n\nconst { totalCredits, monthlyBonusCredits, prepaidCredits, isLoadingBalance } =\n useSubscriptionCredits()\n\nconst {\n isLoadingSupport,\n handleAddApiCredits,\n handleMessageSupport,\n handleRefresh,\n handleLearnMoreClick\n} = useSubscriptionActions()\n\n// Focus-based polling: refresh balance when user returns from Stripe checkout\nconst PENDING_TOPUP_KEY = 'pending_topup_timestamp'\nconst TOPUP_EXPIRY_MS = 5 * 60 * 1000 // 5 minutes\n\nfunction handleWindowFocus() {\n const timestampStr = localStorage.getItem(PENDING_TOPUP_KEY)\n if (!timestampStr) return\n\n const timestamp = parseInt(timestampStr, 10)\n\n // Clear expired tracking (older than 5 minutes)\n if (Date.now() - timestamp > TOPUP_EXPIRY_MS) {\n localStorage.removeItem(PENDING_TOPUP_KEY)\n return\n }\n\n // Refresh and clear tracking to prevent repeated calls\n void handleRefresh()\n localStorage.removeItem(PENDING_TOPUP_KEY)\n}\n\nonMounted(() => {\n window.addEventListener('focus', handleWindowFocus)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('focus', handleWindowFocus)\n})\n\nconst handleOpenPartnerNodesInfo = () => {\n window.open(\n buildDocsUrl(docsPaths.partnerNodesPricing, { includeLocale: true }),\n '_blank'\n )\n}\n</script>\n\n<style scoped>\n:deep(.bg-comfy-menu-secondary) {\n background-color: transparent;\n}\n</style>\n","<template>\n <TabPanel value=\"PlanCredits\" class=\"subscription-container h-full\">\n <div class=\"flex h-full flex-col gap-6\">\n <div class=\"flex items-center gap-2\">\n <span class=\"text-2xl font-inter font-semibold leading-tight\">\n {{\n isActiveSubscription\n ? $t('subscription.title')\n : $t('subscription.titleUnsubscribed')\n }}\n </span>\n <div class=\"pt-1\">\n <CloudBadge\n reverse-order\n background-color=\"var(--p-dialog-background)\"\n />\n </div>\n </div>\n\n <div class=\"grow overflow-auto\">\n <div class=\"rounded-2xl border border-interface-stroke p-6\">\n <div>\n <div class=\"flex items-center justify-between gap-2\">\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm font-bold text-text-primary\">\n {{ subscriptionTierName }}\n </div>\n <div class=\"flex items-baseline gap-1 font-inter font-semibold\">\n <span class=\"text-2xl\">${{ tierPrice }}</span>\n <span class=\"text-base\">{{\n $t('subscription.perMonth')\n }}</span>\n </div>\n <div\n v-if=\"isActiveSubscription\"\n class=\"text-sm text-text-secondary\"\n >\n <template v-if=\"isCancelled\">\n {{\n $t('subscription.expiresDate', {\n date: formattedEndDate\n })\n }}\n </template>\n <template v-else>\n {{\n $t('subscription.renewsDate', {\n date: formattedRenewalDate\n })\n }}\n </template>\n </div>\n </div>\n\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"secondary\"\n class=\"ml-auto rounded-lg px-4 py-2 text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected\"\n @click=\"\n async () => {\n await authActions.accessBillingPortal()\n }\n \"\n >\n {{ $t('subscription.manageSubscription') }}\n </Button>\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"primary\"\n class=\"rounded-lg px-4 py-2 text-sm font-normal text-text-primary\"\n @click=\"showSubscriptionDialog\"\n >\n {{ $t('subscription.upgradePlan') }}\n </Button>\n\n <SubscribeButton\n v-else\n :label=\"$t('subscription.subscribeNow')\"\n size=\"sm\"\n :fluid=\"false\"\n class=\"text-xs\"\n @subscribed=\"handleRefresh\"\n />\n </div>\n </div>\n\n <div class=\"flex flex-col lg:flex-row gap-6 pt-9\">\n <div class=\"flex flex-col shrink-0\">\n <div class=\"flex flex-col gap-3\">\n <div\n :class=\"\n cn(\n 'relative flex flex-col gap-6 rounded-2xl p-5',\n 'bg-modal-panel-background'\n )\n \"\n >\n <Button\n variant=\"muted-textonly\"\n size=\"icon-sm\"\n class=\"absolute top-4 right-4\"\n :loading=\"isLoadingBalance\"\n @click=\"handleRefresh\"\n >\n <i class=\"pi pi-sync text-text-secondary text-sm\" />\n </Button>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm text-muted\">\n {{ $t('subscription.totalCredits') }}\n </div>\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"8rem\"\n height=\"2rem\"\n />\n <div v-else class=\"text-2xl font-bold\">\n {{ totalCredits }}\n </div>\n </div>\n\n <!-- Credit Breakdown -->\n <table class=\"text-sm text-muted\">\n <tbody>\n <tr>\n <td class=\"pr-4 font-bold text-left align-middle\">\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"5rem\"\n height=\"1rem\"\n />\n <span v-else>{{ includedCreditsDisplay }}</span>\n </td>\n <td class=\"align-middle\" :title=\"creditsRemainingLabel\">\n {{ creditsRemainingLabel }}\n </td>\n </tr>\n <tr>\n <td class=\"pr-4 font-bold text-left align-middle\">\n <Skeleton\n v-if=\"isLoadingBalance\"\n width=\"3rem\"\n height=\"1rem\"\n />\n <span v-else>{{ prepaidCredits }}</span>\n </td>\n <td\n class=\"align-middle\"\n :title=\"$t('subscription.creditsYouveAdded')\"\n >\n {{ $t('subscription.creditsYouveAdded') }}\n </td>\n </tr>\n </tbody>\n </table>\n\n <div class=\"flex items-center justify-between\">\n <a\n :href=\"usageHistoryUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-sm underline text-center text-muted\"\n >\n {{ $t('subscription.viewUsageHistory') }}\n </a>\n <Button\n v-if=\"isActiveSubscription\"\n variant=\"secondary\"\n class=\"p-2 min-h-8 rounded-lg text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected\"\n @click=\"handleAddApiCredits\"\n >\n {{ $t('subscription.addCredits') }}\n </Button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"text-sm text-text-primary\">\n {{ $t('subscription.yourPlanIncludes') }}\n </div>\n\n <div class=\"flex flex-col gap-0\">\n <div\n v-for=\"benefit in tierBenefits\"\n :key=\"benefit.key\"\n class=\"flex items-center gap-2 py-2\"\n >\n <i\n v-if=\"benefit.type === 'feature'\"\n class=\"pi pi-check text-xs text-text-primary\"\n />\n <span\n v-else-if=\"benefit.type === 'metric' && benefit.value\"\n class=\"text-sm font-normal whitespace-nowrap text-text-primary\"\n >\n {{ benefit.value }}\n </span>\n <span class=\"text-sm text-muted\">\n {{ benefit.label }}\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- View More Details - Outside main content -->\n <div class=\"flex items-center gap-2 py-4\">\n <i class=\"pi pi-external-link text-muted\"></i>\n <a\n href=\"https://www.comfy.org/cloud/pricing\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-sm underline hover:opacity-80 text-muted\"\n >\n {{ $t('subscription.viewMoreDetailsPlans') }}\n </a>\n </div>\n </div>\n\n <div\n class=\"flex items-center justify-between border-t border-interface-stroke pt-3\"\n >\n <div class=\"flex gap-2\">\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleLearnMoreClick\"\n >\n <i class=\"pi pi-question-circle text-text-secondary text-xs\" />\n {{ $t('subscription.learnMore') }}\n </Button>\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleOpenPartnerNodesInfo\"\n >\n <i class=\"pi pi-question-circle text-text-secondary text-xs\" />\n {{ $t('subscription.partnerNodesCredits') }}\n </Button>\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n :loading=\"isLoadingSupport\"\n @click=\"handleMessageSupport\"\n >\n <i class=\"pi pi-comment text-text-secondary text-xs\" />\n {{ $t('subscription.messageSupport') }}\n </Button>\n </div>\n\n <Button\n variant=\"muted-textonly\"\n class=\"text-xs text-text-secondary\"\n @click=\"handleInvoiceHistory\"\n >\n {{ $t('subscription.invoiceHistory') }}\n <i class=\"pi pi-external-link text-text-secondary text-xs\" />\n </Button>\n </div>\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\nimport TabPanel from 'primevue/tabpanel'\nimport { computed, onBeforeUnmount, onMounted } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport CloudBadge from '@/components/topbar/CloudBadge.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useExternalLink } from '@/composables/useExternalLink'\nimport { getComfyPlatformBaseUrl } from '@/config/comfyApi'\nimport SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { useSubscriptionActions } from '@/platform/cloud/subscription/composables/useSubscriptionActions'\nimport { useSubscriptionCredits } from '@/platform/cloud/subscription/composables/useSubscriptionCredits'\nimport { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'\nimport {\n DEFAULT_TIER_KEY,\n TIER_TO_KEY,\n getTierCredits,\n getTierFeatures,\n getTierPrice\n} from '@/platform/cloud/subscription/constants/tierPricing'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst { buildDocsUrl, docsPaths } = useExternalLink()\nconst authActions = useFirebaseAuthActions()\nconst { t, n } = useI18n()\n\nconst {\n isActiveSubscription,\n isCancelled,\n formattedRenewalDate,\n formattedEndDate,\n subscriptionTier,\n subscriptionTierName,\n subscriptionStatus,\n isYearlySubscription,\n handleInvoiceHistory\n} = useSubscription()\n\nconst { show: showSubscriptionDialog } = useSubscriptionDialog()\n\nconst tierKey = computed(() => {\n const tier = subscriptionTier.value\n if (!tier) return DEFAULT_TIER_KEY\n return TIER_TO_KEY[tier] ?? DEFAULT_TIER_KEY\n})\nconst tierPrice = computed(() =>\n getTierPrice(tierKey.value, isYearlySubscription.value)\n)\nconst usageHistoryUrl = computed(\n () => `${getComfyPlatformBaseUrl()}/profile/usage`\n)\n\nconst refillsDate = computed(() => {\n if (!subscriptionStatus.value?.renewal_date) return ''\n const date = new Date(subscriptionStatus.value.renewal_date)\n const day = String(date.getDate()).padStart(2, '0')\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const year = String(date.getFullYear()).slice(-2)\n return `${month}/${day}/${year}`\n})\n\nconst creditsRemainingLabel = computed(() =>\n isYearlySubscription.value\n ? t('subscription.creditsRemainingThisYear', {\n date: refillsDate.value\n })\n : t('subscription.creditsRemainingThisMonth', {\n date: refillsDate.value\n })\n)\n\nconst planTotalCredits = computed(() => {\n const credits = getTierCredits(tierKey.value)\n const total = isYearlySubscription.value ? credits * 12 : credits\n return n(total)\n})\n\nconst includedCreditsDisplay = computed(\n () => `${monthlyBonusCredits.value} / ${planTotalCredits.value}`\n)\n\n// Tier benefits for v-for loop\ntype BenefitType = 'metric' | 'feature'\n\ninterface Benefit {\n key: string\n type: BenefitType\n label: string\n value?: string\n}\n\nconst tierBenefits = computed((): Benefit[] => {\n const key = tierKey.value\n\n const benefits: Benefit[] = [\n {\n key: 'maxDuration',\n type: 'metric',\n value: t(`subscription.maxDuration.${key}`),\n label: t('subscription.maxDurationLabel')\n },\n {\n key: 'gpu',\n type: 'feature',\n label: t('subscription.gpuLabel')\n },\n {\n key: 'addCredits',\n type: 'feature',\n label: t('subscription.addCreditsLabel')\n }\n ]\n\n if (getTierFeatures(key).customLoRAs) {\n benefits.push({\n key: 'customLoRAs',\n type: 'feature',\n label: t('subscription.customLoRAsLabel')\n })\n }\n\n return benefits\n})\n\nconst { totalCredits, monthlyBonusCredits, prepaidCredits, isLoadingBalance } =\n useSubscriptionCredits()\n\nconst {\n isLoadingSupport,\n handleAddApiCredits,\n handleMessageSupport,\n handleRefresh,\n handleLearnMoreClick\n} = useSubscriptionActions()\n\n// Focus-based polling: refresh balance when user returns from Stripe checkout\nconst PENDING_TOPUP_KEY = 'pending_topup_timestamp'\nconst TOPUP_EXPIRY_MS = 5 * 60 * 1000 // 5 minutes\n\nfunction handleWindowFocus() {\n const timestampStr = localStorage.getItem(PENDING_TOPUP_KEY)\n if (!timestampStr) return\n\n const timestamp = parseInt(timestampStr, 10)\n\n // Clear expired tracking (older than 5 minutes)\n if (Date.now() - timestamp > TOPUP_EXPIRY_MS) {\n localStorage.removeItem(PENDING_TOPUP_KEY)\n return\n }\n\n // Refresh and clear tracking to prevent repeated calls\n void handleRefresh()\n localStorage.removeItem(PENDING_TOPUP_KEY)\n}\n\nonMounted(() => {\n window.addEventListener('focus', handleWindowFocus)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('focus', handleWindowFocus)\n})\n\nconst handleOpenPartnerNodesInfo = () => {\n window.open(\n buildDocsUrl(docsPaths.partnerNodesPricing, { includeLocale: true }),\n '_blank'\n )\n}\n</script>\n\n<style scoped>\n:deep(.bg-comfy-menu-secondary) {\n background-color: transparent;\n}\n</style>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- var K=Object.defineProperty;var a=(c,i)=>K(c,"name",{value:i,configurable:!0});import{C as Q,et as Z}from"./vendor-primevue-DcMRXJN3.js";import{$o as t,Aa as ee,Ba as e,Do as w,Ha as F,Jo as l,Ka as A,Oo as te,Pa as se,Ua as b,Xo as M,Ya as Y,do as g,oo as re,po as oe,qa as f,wo as V,za as O,zo as D}from"./vendor-other-DlQF6V2E.js";import{n as ae}from"./vendor-vue-D9IUuwPJ.js";import{A as o,Lt as le}from"./api-CUAc7rDA.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import{t as ne}from"./useErrorHandling-CI8_F4yx.js";import{t as z}from"./src-DTrob8OL.js";import{t as C}from"./Button-Do2I1OAA.js";import"./PanelTemplate-BjN5XNg2.js";import{Fr as j,Ir as ie,Mr as ue,Nr as H,Or as ce,ft as de}from"./dialogService-BZ1FmjZL.js";import{t as q}from"./_plugin-vue_export-helper-PHE0iSCb.js";import"./vendor-tiptap-_UqYL7N_.js";import"./vendor-xterm-BU_lcTPR.js";import"./vendor-three-BFcUNSs9.js";import"./markdownRendererUtil-DglHsU8t.js";import"./userStore-BkgQPjq6.js";import{t as pe}from"./subscriptionCheckoutUtil-B_OvUP2T.js";import{t as fe}from"./SubscribeButton-CTOQRkfg.js";import{t as me}from"./CloudBadge-BnLiAHDN.js";var rs=""+new URL("images/cloud-subscription.webm",import.meta.url).href;var ve=["yearly-pro","yearly-creator","yearly-standard","monthly-pro","monthly-creator","monthly-standard"].reduce((c,i,m)=>c.set(i,m),new Map),xe=a((c,i)=>c==="founder"?null:`${i}-${c}`,"toRankedPlanKey");const U=a(({tierKey:c,billingCycle:i})=>{const m=xe(c,i);return m?ve.get(m)??Number.POSITIVE_INFINITY:Number.POSITIVE_INFINITY},"getPlanRank"),be=a(({current:c,target:i})=>{const m=U(c);return U(i)>m},"isPlanDowngrade");var ge={class:"flex flex-col gap-8"},_e={class:"flex justify-center"},ye={class:"flex items-center gap-2"},he={key:0,class:"bg-primary-background text-white text-[11px] px-1 py-0.5 rounded-full flex items-center font-bold"},we={class:"flex flex-col xl:flex-row items-stretch gap-6"},ke={class:"p-8 pb-0 flex flex-col gap-8"},Ce={class:"flex flex-row items-center gap-2 justify-between"},$e={class:"font-inter text-base font-bold leading-normal text-base-foreground"},Pe={key:0,class:"rounded-full bg-base-foreground px-1.5 text-[11px] font-bold uppercase text-base-background h-5 tracking-tight flex items-center"},Te={class:"flex flex-col"},Se={class:"flex flex-col gap-2"},Le={class:"flex flex-row items-baseline gap-2"},Ee={class:"font-inter text-[32px] font-semibold leading-normal text-base-foreground"},Ie={class:"font-inter text-xl leading-normal text-base-foreground"},Re={class:"flex items-center gap-2"},De={class:"text-sm text-muted-foreground"},Ae={class:"flex flex-col gap-4 pb-0 flex-1"},Ne={class:"flex flex-row items-center justify-between"},Be={class:"font-inter text-sm font-normal leading-normal text-foreground"},je={class:"flex flex-row items-center gap-1"},Oe={class:"font-inter text-sm font-bold leading-normal text-base-foreground"},Fe={class:"flex flex-row items-center justify-between"},Me={class:"text-sm font-normal text-foreground"},Ve={class:"font-inter text-sm font-bold leading-normal text-base-foreground"},ze={class:"flex flex-row items-center justify-between"},Ue={class:"text-sm font-normal text-foreground"},Ye={class:"flex flex-row items-center justify-between"},He={class:"text-sm font-normal text-foreground"},qe={class:"flex flex-row items-center justify-between"},We={class:"text-sm font-normal text-foreground"},Xe={key:0,class:"pi pi-check text-xs text-success-foreground"},Ge={key:1,class:"pi pi-times text-xs text-foreground"},Je={class:"flex flex-col gap-2"},Ke={class:"flex flex-row items-start justify-between"},Qe={class:"flex flex-col gap-2"},Ze={class:"text-sm font-normal text-foreground leading-relaxed"},et={class:"flex flex-row items-center gap-2 group pt-2"},tt={class:"font-inter text-sm font-bold leading-normal text-base-foreground"},st={class:"flex flex-col p-8"},rt={class:"flex flex-col gap-2"},ot={class:"text-sm text-base-foreground leading-normal"},at={href:"https://cloud.comfy.org/?template=video_wan2_2_14B_fun_camera",target:"_blank",rel:"noopener noreferrer",class:"text-sm text-azure-600 hover:text-azure-400 no-underline flex gap-1"},lt={class:"underline"},nt=Y({__name:"PricingTable",setup(c){const i=a((s,n)=>n==="yearly"?`${s}-yearly`:s,"getCheckoutTier"),m=[{label:o("subscription.yearly"),value:"yearly"},{label:o("subscription.monthly"),value:"monthly"}],$=[{id:"STANDARD",key:"standard",name:o("subscription.tiers.standard.name"),pricing:j.standard,maxDuration:o("subscription.maxDuration.standard"),customLoRAs:!1,isPopular:!1},{id:"CREATOR",key:"creator",name:o("subscription.tiers.creator.name"),pricing:j.creator,maxDuration:o("subscription.maxDuration.creator"),customLoRAs:!0,isPopular:!0},{id:"PRO",key:"pro",name:o("subscription.tiers.pro.name"),pricing:j.pro,maxDuration:o("subscription.maxDuration.pro"),customLoRAs:!0,isPopular:!1}],{n:P}=ae(),{isActiveSubscription:T,subscriptionTier:S,isYearlySubscription:L}=H(),{accessBillingPortal:E,reportError:I}=ue(),{wrapWithErrorHandlingAsync:k}=ne(),v=D(!1),y=D(null),x=D(),d=D("yearly"),_=O(()=>S.value?ie[S.value]:null),R=O(()=>_.value?{tierKey:_.value,billingCycle:L.value?"yearly":"monthly"}:null),h=a(s=>{if(!_.value)return!1;const n=d.value==="yearly";return _.value===s&&L.value===n},"isCurrentPlan"),N=a(s=>{x.value.toggle(s)},"togglePopover"),B=a(s=>{if(h(s.key))return o("subscription.currentPlan");const n=d.value==="yearly"?o("subscription.tierNameYearly",{name:s.name}):s.name;return T.value?o("subscription.changeTo",{plan:n}):o("subscription.subscribeTo",{plan:n})},"getButtonLabel"),u=a(s=>h(s.key)?"secondary":s.key==="creator"?"primary":"secondary","getButtonSeverity"),p=a(s=>s.key==="creator"?"font-inter text-sm font-bold leading-normal text-base-background":"font-inter text-sm font-bold leading-normal text-primary-foreground","getButtonTextClass"),W=a(s=>s.pricing[d.value],"getPrice"),X=a(s=>s.pricing.yearly*12,"getAnnualTotal"),G=a(s=>s.pricing.credits*(d.value==="yearly"?12:1),"getCreditsDisplay"),J=k(async s=>{if(!(!le||v.value||h(s))){v.value=!0,y.value=s;try{if(T.value){const n=i(s,d.value),r={tierKey:s,billingCycle:d.value};R.value&&be({current:R.value,target:r})?await E():await E(n)}else await pe(s,d.value)}finally{v.value=!1,y.value=null}}},I);return(s,n)=>(g(),b("div",ge,[e("div",_e,[f(l(Q),{modelValue:d.value,"onUpdate:modelValue":n[0]||(n[0]=r=>d.value=r),options:m,"option-label":"label","option-value":"value","allow-empty":!1,unstyled:"",pt:{root:{class:"flex gap-1 bg-secondary-background rounded-lg p-1.5"},pcToggleButton:{root:a(({context:r})=>({class:["w-36 h-8 rounded-md transition-colors cursor-pointer border-none outline-none ring-0 text-sm font-medium flex items-center justify-center",r.active?"bg-base-foreground text-base-background":"bg-transparent text-muted-foreground hover:bg-secondary-background-hover"]}),"root"),label:{class:"flex items-center gap-2 "}}}},{option:w(({option:r})=>[e("div",ye,[e("span",null,t(r.label),1),r.value==="yearly"?(g(),b("div",he," -20% ")):F("",!0)])]),_:1},8,["modelValue","pt"])]),e("div",we,[(g(),b(se,null,oe($,r=>e("div",{key:r.id,class:M(l(z)("flex-1 flex flex-col rounded-2xl border border-border-default bg-base-background shadow-[0_0_12px_rgba(0,0,0,0.1)]",r.isPopular?"border-muted-foreground":""))},[e("div",ke,[e("div",Ce,[e("span",$e,t(r.name),1),r.isPopular?(g(),b("div",Pe,t(l(o)("subscription.mostPopular")),1)):F("",!0)]),e("div",Te,[e("div",Se,[e("div",Le,[e("span",Ee,[te(e("span",{class:"line-through text-2xl text-muted-foreground"}," $"+t(r.pricing.monthly),513),[[ee,d.value==="yearly"]]),A(" $"+t(W(r)),1)]),e("span",Ie,t(l(o)("subscription.usdPerMonth")),1)]),e("div",Re,[e("span",De,t(d.value==="yearly"?l(o)("subscription.billedYearly",{total:`$${X(r)}`}):l(o)("subscription.billedMonthly")),1)])])]),e("div",Ae,[e("div",Ne,[e("span",Be,t(d.value==="yearly"?l(o)("subscription.yearlyCreditsLabel"):l(o)("subscription.monthlyCreditsLabel")),1),e("div",je,[n[1]||(n[1]=e("i",{class:"icon-[lucide--component] text-amber-400 text-sm"},null,-1)),e("span",Oe,t(l(P)(G(r))),1)])]),e("div",Fe,[e("span",Me,t(l(o)("subscription.maxDurationLabel")),1),e("span",Ve,t(r.maxDuration),1)]),e("div",ze,[e("span",Ue,t(l(o)("subscription.gpuLabel")),1),n[2]||(n[2]=e("i",{class:"pi pi-check text-xs text-success-foreground"},null,-1))]),e("div",Ye,[e("span",He,t(l(o)("subscription.addCreditsLabel")),1),n[3]||(n[3]=e("i",{class:"pi pi-check text-xs text-success-foreground"},null,-1))]),e("div",qe,[e("span",We,t(l(o)("subscription.customLoRAsLabel")),1),r.customLoRAs?(g(),b("i",Xe)):(g(),b("i",Ge))]),e("div",Je,[e("div",Ke,[e("div",Qe,[e("span",Ze,t(l(o)("subscription.videoEstimateLabel")),1),e("div",et,[n[4]||(n[4]=e("i",{class:"pi pi-question-circle text-xs text-muted-foreground group-hover:text-base-foreground"},null,-1)),e("span",{class:"text-sm font-normal text-muted-foreground cursor-pointer group-hover:text-base-foreground",onClick:N},t(l(o)("subscription.videoEstimateHelp")),1)])]),e("span",tt," ~"+t(l(P)(r.pricing.videoEstimate)),1)])])])]),e("div",st,[f(C,{variant:u(r),disabled:v.value||h(r.key),loading:y.value===r.key,class:M(l(z)("h-10 w-full",p(r),r.key==="creator"?"bg-base-foreground border-transparent hover:bg-inverted-background-hover":"bg-secondary-background border-transparent hover:bg-secondary-background-hover focus:bg-secondary-background-selected")),onClick:a(()=>l(J)(r.key),"onClick")},{default:w(()=>[A(t(B(r)),1)]),_:2},1032,["variant","disabled","loading","class","onClick"])])],2)),64))]),f(l(Z),{ref_key:"popover",ref:x,"append-to":"body","auto-z-index":!0,"base-z-index":1e3,dismissable:!0,"close-on-escape":!0,unstyled:"",pt:{root:{class:"rounded-lg border border-interface-stroke bg-interface-panel-surface shadow-lg p-4 max-w-xs"}}},{default:w(()=>[e("div",rt,[e("p",ot,t(l(o)("subscription.videoEstimateExplanation")),1),e("a",at,[e("span",lt,t(l(o)("subscription.videoEstimateTryTemplate")),1),n[5]||(n[5]=e("span",{class:"no-underline",innerHTML:"→"},null,-1))])])]),_:1},512)]))}}),it=nt,ut={},ct={class:"flex flex-col items-start gap-0 self-stretch"},dt={class:"flex items-center gap-2 py-2"},pt={class:"text-sm text-text-primary"},ft={class:"flex items-center gap-2 py-2"},mt={class:"text-sm text-text-primary"};function vt(c,i){return g(),b("div",ct,[e("div",dt,[i[0]||(i[0]=e("i",{class:"pi pi-check text-xs text-text-primary"},null,-1)),e("span",pt,t(c.$t("subscription.benefits.benefit1")),1)]),e("div",ft,[i[1]||(i[1]=e("i",{class:"pi pi-check text-xs text-text-primary"},null,-1)),e("span",mt,t(c.$t("subscription.benefits.benefit2")),1)])])}a(vt,"_sfc_render");var xt=q(ut,[["render",vt]]),bt={key:0,class:"relative flex flex-col p-4 pt-8 md:p-16 !overflow-y-auto h-full gap-8"},gt={class:"text-center"},_t={class:"text-xl lg:text-2xl text-muted-foreground m-0"},yt={class:"flex flex-col items-center gap-2"},ht={class:"text-sm text-text-secondary m-0"},wt={class:"flex items-center gap-1.5"},kt={class:"text-sm text-text-secondary"},Ct={key:1,class:"legacy-dialog relative grid h-full grid-cols-5"},$t={class:"col-span-3 flex flex-col justify-between p-8"},Pt={class:"flex flex-col gap-6"},Tt={class:"inline-flex items-center gap-2"},St={class:"text-sm text-text-primary"},Lt={class:"flex items-baseline gap-2"},Et={class:"text-4xl font-bold"},It={class:"text-xl"},Rt={class:"flex flex-col pt-8"},Dt=3e3,At=3,Nt=Y({__name:"SubscriptionRequiredDialogContent",props:{onClose:{type:Function}},emits:["close"],setup(c,{emit:i}){const m=c,$=i,{fetchStatus:P,isActiveSubscription:T,isSubscriptionEnabled:S}=H(),L=new Intl.NumberFormat(navigator.language||"en-US",{style:"currency",currency:"USD",minimumFractionDigits:0,maximumFractionDigits:0}).format(20),E=ce(),I=de(),k=O(()=>S());let v=null,y=0;const x=a(()=>{v&&(clearInterval(v),v=null)},"stopPolling"),d=a(()=>{x(),y=0;const u=a(async()=>{try{await P(),y++,y>=At&&x()}catch(p){console.error("[SubscriptionDialog] Failed to poll subscription status",p),x()}},"poll");u(),v=window.setInterval(()=>{u()},Dt)},"startPolling"),_=a(()=>{k.value&&d()},"handleWindowFocus");V(k,u=>{u?window.addEventListener("focus",_):(window.removeEventListener("focus",_),x())},{immediate:!0}),V(()=>T.value,u=>{u&&k.value&&$("close",!0)});const R=a(()=>{$("close",!0)},"handleSubscribed"),h=a(()=>{x(),m.onClose()},"handleClose"),N=a(async()=>{I?.trackHelpResourceClicked({resource_type:"help_feedback",is_external:!0,source:"subscription"}),await E.execute("Comfy.ContactSupport")},"handleContactUs"),B=a(()=>{I?.trackHelpResourceClicked({resource_type:"docs",is_external:!0,source:"subscription"}),window.open("https://www.comfy.org/cloud/enterprise","_blank")},"handleViewEnterprise");return re(()=>{x(),window.removeEventListener("focus",_)}),(u,p)=>k.value?(g(),b("div",bt,[f(C,{size:"icon",variant:"muted-textonly",class:"rounded-full shrink-0 text-text-secondary hover:bg-white/10 absolute right-2.5 top-2.5","aria-label":u.$t("g.close"),onClick:h},{default:w(()=>p[0]||(p[0]=[e("i",{class:"pi pi-times text-xl"},null,-1)])),_:1},8,["aria-label"]),e("div",gt,[e("h2",_t,t(u.$t("subscription.description")),1)]),f(it,{class:"flex-1"}),e("div",yt,[e("p",ht,t(u.$t("subscription.haveQuestions")),1),e("div",wt,[f(C,{variant:"muted-textonly",class:"h-6 p-1 text-sm text-text-secondary hover:text-base-foreground",onClick:N},{default:w(()=>[A(t(u.$t("subscription.contactUs"))+" ",1),p[1]||(p[1]=e("i",{class:"pi pi-comments"},null,-1))]),_:1}),e("span",kt,t(u.$t("g.or")),1),f(C,{variant:"muted-textonly",class:"h-6 p-1 text-sm text-text-secondary hover:text-base-foreground",onClick:B},{default:w(()=>[A(t(u.$t("subscription.viewEnterprise"))+" ",1),p[2]||(p[2]=e("i",{class:"pi pi-external-link"},null,-1))]),_:1})])])])):(g(),b("div",Ct,[f(C,{size:"icon",variant:"muted-textonly",class:"rounded-full absolute top-2.5 right-2.5 z-10 h-8 w-8 p-0 text-white hover:bg-white/20","aria-label":u.$t("g.close"),onClick:h},{default:w(()=>p[3]||(p[3]=[e("i",{class:"pi pi-times"},null,-1)])),_:1},8,["aria-label"]),p[4]||(p[4]=e("div",{class:"relative col-span-2 flex items-center justify-center overflow-hidden rounded-sm"},[e("video",{autoplay:"",loop:"",muted:"",playsinline:"",class:"h-full min-w-[125%] object-cover p-0",style:{"margin-left":"-20%"}},[e("source",{src:""+new URL("images/cloud-subscription.webm",import.meta.url).href,type:"video/webm"})])],-1)),e("div",$t,[e("div",null,[e("div",Pt,[e("div",Tt,[e("div",St,t(u.$t("subscription.required.title")),1),f(me,{"reverse-order":"","no-padding":"","background-color":"var(--p-dialog-background)","use-subscription":""})]),e("div",Lt,[e("span",Et,t(l(L)),1),e("span",It,t(u.$t("subscription.perMonth")),1)])]),f(xt,{class:"mt-6 text-muted"})]),e("div",Rt,[f(fe,{class:"py-2 px-4 rounded-lg",pt:{root:{style:"background: var(--color-accent-blue, #0B8CE9);"},label:{class:"font-inter font-[700] text-sm"}},onSubscribed:R})])])]))}}),os=q(Nt,[["__scopeId","data-v-80c6bb1c"]]);export{os as default};
1
+ var K=Object.defineProperty;var a=(c,i)=>K(c,"name",{value:i,configurable:!0});import{C as Q,et as Z}from"./vendor-primevue-DcMRXJN3.js";import{$o as t,Aa as ee,Ba as e,Do as w,Ha as F,Jo as l,Ka as A,Oo as te,Pa as se,Ua as b,Xo as M,Ya as Y,do as g,oo as re,po as oe,qa as f,wo as V,za as O,zo as D}from"./vendor-other-DlQF6V2E.js";import{n as ae}from"./vendor-vue-D9IUuwPJ.js";import{A as o,Lt as le}from"./api-Dwq2LQIW.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import{t as ne}from"./useErrorHandling-Cfa5N_7c.js";import{t as z}from"./src-DTrob8OL.js";import{t as C}from"./Button-Do2I1OAA.js";import"./PanelTemplate-BJda9e5J.js";import{Fr as j,Ir as ie,Mr as ue,Nr as H,Or as ce,ft as de}from"./dialogService-YG0RH337.js";import{t as q}from"./_plugin-vue_export-helper-PHE0iSCb.js";import"./vendor-tiptap-_UqYL7N_.js";import"./vendor-xterm-BU_lcTPR.js";import"./vendor-three-BFcUNSs9.js";import"./markdownRendererUtil-DglHsU8t.js";import"./userStore-BAS9m9W6.js";import{t as pe}from"./subscriptionCheckoutUtil-DswSOreM.js";import{t as fe}from"./SubscribeButton-DZBycfCA.js";import{t as me}from"./CloudBadge-B4nmLus2.js";var rs=""+new URL("images/cloud-subscription.webm",import.meta.url).href;var ve=["yearly-pro","yearly-creator","yearly-standard","monthly-pro","monthly-creator","monthly-standard"].reduce((c,i,m)=>c.set(i,m),new Map),xe=a((c,i)=>c==="founder"?null:`${i}-${c}`,"toRankedPlanKey");const U=a(({tierKey:c,billingCycle:i})=>{const m=xe(c,i);return m?ve.get(m)??Number.POSITIVE_INFINITY:Number.POSITIVE_INFINITY},"getPlanRank"),be=a(({current:c,target:i})=>{const m=U(c);return U(i)>m},"isPlanDowngrade");var ge={class:"flex flex-col gap-8"},_e={class:"flex justify-center"},ye={class:"flex items-center gap-2"},he={key:0,class:"bg-primary-background text-white text-[11px] px-1 py-0.5 rounded-full flex items-center font-bold"},we={class:"flex flex-col xl:flex-row items-stretch gap-6"},ke={class:"p-8 pb-0 flex flex-col gap-8"},Ce={class:"flex flex-row items-center gap-2 justify-between"},$e={class:"font-inter text-base font-bold leading-normal text-base-foreground"},Pe={key:0,class:"rounded-full bg-base-foreground px-1.5 text-[11px] font-bold uppercase text-base-background h-5 tracking-tight flex items-center"},Te={class:"flex flex-col"},Se={class:"flex flex-col gap-2"},Le={class:"flex flex-row items-baseline gap-2"},Ee={class:"font-inter text-[32px] font-semibold leading-normal text-base-foreground"},Ie={class:"font-inter text-xl leading-normal text-base-foreground"},Re={class:"flex items-center gap-2"},De={class:"text-sm text-muted-foreground"},Ae={class:"flex flex-col gap-4 pb-0 flex-1"},Ne={class:"flex flex-row items-center justify-between"},Be={class:"font-inter text-sm font-normal leading-normal text-foreground"},je={class:"flex flex-row items-center gap-1"},Oe={class:"font-inter text-sm font-bold leading-normal text-base-foreground"},Fe={class:"flex flex-row items-center justify-between"},Me={class:"text-sm font-normal text-foreground"},Ve={class:"font-inter text-sm font-bold leading-normal text-base-foreground"},ze={class:"flex flex-row items-center justify-between"},Ue={class:"text-sm font-normal text-foreground"},Ye={class:"flex flex-row items-center justify-between"},He={class:"text-sm font-normal text-foreground"},qe={class:"flex flex-row items-center justify-between"},We={class:"text-sm font-normal text-foreground"},Xe={key:0,class:"pi pi-check text-xs text-success-foreground"},Ge={key:1,class:"pi pi-times text-xs text-foreground"},Je={class:"flex flex-col gap-2"},Ke={class:"flex flex-row items-start justify-between"},Qe={class:"flex flex-col gap-2"},Ze={class:"text-sm font-normal text-foreground leading-relaxed"},et={class:"flex flex-row items-center gap-2 group pt-2"},tt={class:"font-inter text-sm font-bold leading-normal text-base-foreground"},st={class:"flex flex-col p-8"},rt={class:"flex flex-col gap-2"},ot={class:"text-sm text-base-foreground leading-normal"},at={href:"https://cloud.comfy.org/?template=video_wan2_2_14B_fun_camera",target:"_blank",rel:"noopener noreferrer",class:"text-sm text-azure-600 hover:text-azure-400 no-underline flex gap-1"},lt={class:"underline"},nt=Y({__name:"PricingTable",setup(c){const i=a((s,n)=>n==="yearly"?`${s}-yearly`:s,"getCheckoutTier"),m=[{label:o("subscription.yearly"),value:"yearly"},{label:o("subscription.monthly"),value:"monthly"}],$=[{id:"STANDARD",key:"standard",name:o("subscription.tiers.standard.name"),pricing:j.standard,maxDuration:o("subscription.maxDuration.standard"),customLoRAs:!1,isPopular:!1},{id:"CREATOR",key:"creator",name:o("subscription.tiers.creator.name"),pricing:j.creator,maxDuration:o("subscription.maxDuration.creator"),customLoRAs:!0,isPopular:!0},{id:"PRO",key:"pro",name:o("subscription.tiers.pro.name"),pricing:j.pro,maxDuration:o("subscription.maxDuration.pro"),customLoRAs:!0,isPopular:!1}],{n:P}=ae(),{isActiveSubscription:T,subscriptionTier:S,isYearlySubscription:L}=H(),{accessBillingPortal:E,reportError:I}=ue(),{wrapWithErrorHandlingAsync:k}=ne(),v=D(!1),y=D(null),x=D(),d=D("yearly"),_=O(()=>S.value?ie[S.value]:null),R=O(()=>_.value?{tierKey:_.value,billingCycle:L.value?"yearly":"monthly"}:null),h=a(s=>{if(!_.value)return!1;const n=d.value==="yearly";return _.value===s&&L.value===n},"isCurrentPlan"),N=a(s=>{x.value.toggle(s)},"togglePopover"),B=a(s=>{if(h(s.key))return o("subscription.currentPlan");const n=d.value==="yearly"?o("subscription.tierNameYearly",{name:s.name}):s.name;return T.value?o("subscription.changeTo",{plan:n}):o("subscription.subscribeTo",{plan:n})},"getButtonLabel"),u=a(s=>h(s.key)?"secondary":s.key==="creator"?"primary":"secondary","getButtonSeverity"),p=a(s=>s.key==="creator"?"font-inter text-sm font-bold leading-normal text-base-background":"font-inter text-sm font-bold leading-normal text-primary-foreground","getButtonTextClass"),W=a(s=>s.pricing[d.value],"getPrice"),X=a(s=>s.pricing.yearly*12,"getAnnualTotal"),G=a(s=>s.pricing.credits*(d.value==="yearly"?12:1),"getCreditsDisplay"),J=k(async s=>{if(!(!le||v.value||h(s))){v.value=!0,y.value=s;try{if(T.value){const n=i(s,d.value),r={tierKey:s,billingCycle:d.value};R.value&&be({current:R.value,target:r})?await E():await E(n)}else await pe(s,d.value)}finally{v.value=!1,y.value=null}}},I);return(s,n)=>(g(),b("div",ge,[e("div",_e,[f(l(Q),{modelValue:d.value,"onUpdate:modelValue":n[0]||(n[0]=r=>d.value=r),options:m,"option-label":"label","option-value":"value","allow-empty":!1,unstyled:"",pt:{root:{class:"flex gap-1 bg-secondary-background rounded-lg p-1.5"},pcToggleButton:{root:a(({context:r})=>({class:["w-36 h-8 rounded-md transition-colors cursor-pointer border-none outline-none ring-0 text-sm font-medium flex items-center justify-center",r.active?"bg-base-foreground text-base-background":"bg-transparent text-muted-foreground hover:bg-secondary-background-hover"]}),"root"),label:{class:"flex items-center gap-2 "}}}},{option:w(({option:r})=>[e("div",ye,[e("span",null,t(r.label),1),r.value==="yearly"?(g(),b("div",he," -20% ")):F("",!0)])]),_:1},8,["modelValue","pt"])]),e("div",we,[(g(),b(se,null,oe($,r=>e("div",{key:r.id,class:M(l(z)("flex-1 flex flex-col rounded-2xl border border-border-default bg-base-background shadow-[0_0_12px_rgba(0,0,0,0.1)]",r.isPopular?"border-muted-foreground":""))},[e("div",ke,[e("div",Ce,[e("span",$e,t(r.name),1),r.isPopular?(g(),b("div",Pe,t(l(o)("subscription.mostPopular")),1)):F("",!0)]),e("div",Te,[e("div",Se,[e("div",Le,[e("span",Ee,[te(e("span",{class:"line-through text-2xl text-muted-foreground"}," $"+t(r.pricing.monthly),513),[[ee,d.value==="yearly"]]),A(" $"+t(W(r)),1)]),e("span",Ie,t(l(o)("subscription.usdPerMonth")),1)]),e("div",Re,[e("span",De,t(d.value==="yearly"?l(o)("subscription.billedYearly",{total:`$${X(r)}`}):l(o)("subscription.billedMonthly")),1)])])]),e("div",Ae,[e("div",Ne,[e("span",Be,t(d.value==="yearly"?l(o)("subscription.yearlyCreditsLabel"):l(o)("subscription.monthlyCreditsLabel")),1),e("div",je,[n[1]||(n[1]=e("i",{class:"icon-[lucide--component] text-amber-400 text-sm"},null,-1)),e("span",Oe,t(l(P)(G(r))),1)])]),e("div",Fe,[e("span",Me,t(l(o)("subscription.maxDurationLabel")),1),e("span",Ve,t(r.maxDuration),1)]),e("div",ze,[e("span",Ue,t(l(o)("subscription.gpuLabel")),1),n[2]||(n[2]=e("i",{class:"pi pi-check text-xs text-success-foreground"},null,-1))]),e("div",Ye,[e("span",He,t(l(o)("subscription.addCreditsLabel")),1),n[3]||(n[3]=e("i",{class:"pi pi-check text-xs text-success-foreground"},null,-1))]),e("div",qe,[e("span",We,t(l(o)("subscription.customLoRAsLabel")),1),r.customLoRAs?(g(),b("i",Xe)):(g(),b("i",Ge))]),e("div",Je,[e("div",Ke,[e("div",Qe,[e("span",Ze,t(l(o)("subscription.videoEstimateLabel")),1),e("div",et,[n[4]||(n[4]=e("i",{class:"pi pi-question-circle text-xs text-muted-foreground group-hover:text-base-foreground"},null,-1)),e("span",{class:"text-sm font-normal text-muted-foreground cursor-pointer group-hover:text-base-foreground",onClick:N},t(l(o)("subscription.videoEstimateHelp")),1)])]),e("span",tt," ~"+t(l(P)(r.pricing.videoEstimate)),1)])])])]),e("div",st,[f(C,{variant:u(r),disabled:v.value||h(r.key),loading:y.value===r.key,class:M(l(z)("h-10 w-full",p(r),r.key==="creator"?"bg-base-foreground border-transparent hover:bg-inverted-background-hover":"bg-secondary-background border-transparent hover:bg-secondary-background-hover focus:bg-secondary-background-selected")),onClick:a(()=>l(J)(r.key),"onClick")},{default:w(()=>[A(t(B(r)),1)]),_:2},1032,["variant","disabled","loading","class","onClick"])])],2)),64))]),f(l(Z),{ref_key:"popover",ref:x,"append-to":"body","auto-z-index":!0,"base-z-index":1e3,dismissable:!0,"close-on-escape":!0,unstyled:"",pt:{root:{class:"rounded-lg border border-interface-stroke bg-interface-panel-surface shadow-lg p-4 max-w-xs"}}},{default:w(()=>[e("div",rt,[e("p",ot,t(l(o)("subscription.videoEstimateExplanation")),1),e("a",at,[e("span",lt,t(l(o)("subscription.videoEstimateTryTemplate")),1),n[5]||(n[5]=e("span",{class:"no-underline",innerHTML:"→"},null,-1))])])]),_:1},512)]))}}),it=nt,ut={},ct={class:"flex flex-col items-start gap-0 self-stretch"},dt={class:"flex items-center gap-2 py-2"},pt={class:"text-sm text-text-primary"},ft={class:"flex items-center gap-2 py-2"},mt={class:"text-sm text-text-primary"};function vt(c,i){return g(),b("div",ct,[e("div",dt,[i[0]||(i[0]=e("i",{class:"pi pi-check text-xs text-text-primary"},null,-1)),e("span",pt,t(c.$t("subscription.benefits.benefit1")),1)]),e("div",ft,[i[1]||(i[1]=e("i",{class:"pi pi-check text-xs text-text-primary"},null,-1)),e("span",mt,t(c.$t("subscription.benefits.benefit2")),1)])])}a(vt,"_sfc_render");var xt=q(ut,[["render",vt]]),bt={key:0,class:"relative flex flex-col p-4 pt-8 md:p-16 !overflow-y-auto h-full gap-8"},gt={class:"text-center"},_t={class:"text-xl lg:text-2xl text-muted-foreground m-0"},yt={class:"flex flex-col items-center gap-2"},ht={class:"text-sm text-text-secondary m-0"},wt={class:"flex items-center gap-1.5"},kt={class:"text-sm text-text-secondary"},Ct={key:1,class:"legacy-dialog relative grid h-full grid-cols-5"},$t={class:"col-span-3 flex flex-col justify-between p-8"},Pt={class:"flex flex-col gap-6"},Tt={class:"inline-flex items-center gap-2"},St={class:"text-sm text-text-primary"},Lt={class:"flex items-baseline gap-2"},Et={class:"text-4xl font-bold"},It={class:"text-xl"},Rt={class:"flex flex-col pt-8"},Dt=3e3,At=3,Nt=Y({__name:"SubscriptionRequiredDialogContent",props:{onClose:{type:Function}},emits:["close"],setup(c,{emit:i}){const m=c,$=i,{fetchStatus:P,isActiveSubscription:T,isSubscriptionEnabled:S}=H(),L=new Intl.NumberFormat(navigator.language||"en-US",{style:"currency",currency:"USD",minimumFractionDigits:0,maximumFractionDigits:0}).format(20),E=ce(),I=de(),k=O(()=>S());let v=null,y=0;const x=a(()=>{v&&(clearInterval(v),v=null)},"stopPolling"),d=a(()=>{x(),y=0;const u=a(async()=>{try{await P(),y++,y>=At&&x()}catch(p){console.error("[SubscriptionDialog] Failed to poll subscription status",p),x()}},"poll");u(),v=window.setInterval(()=>{u()},Dt)},"startPolling"),_=a(()=>{k.value&&d()},"handleWindowFocus");V(k,u=>{u?window.addEventListener("focus",_):(window.removeEventListener("focus",_),x())},{immediate:!0}),V(()=>T.value,u=>{u&&k.value&&$("close",!0)});const R=a(()=>{$("close",!0)},"handleSubscribed"),h=a(()=>{x(),m.onClose()},"handleClose"),N=a(async()=>{I?.trackHelpResourceClicked({resource_type:"help_feedback",is_external:!0,source:"subscription"}),await E.execute("Comfy.ContactSupport")},"handleContactUs"),B=a(()=>{I?.trackHelpResourceClicked({resource_type:"docs",is_external:!0,source:"subscription"}),window.open("https://www.comfy.org/cloud/enterprise","_blank")},"handleViewEnterprise");return re(()=>{x(),window.removeEventListener("focus",_)}),(u,p)=>k.value?(g(),b("div",bt,[f(C,{size:"icon",variant:"muted-textonly",class:"rounded-full shrink-0 text-text-secondary hover:bg-white/10 absolute right-2.5 top-2.5","aria-label":u.$t("g.close"),onClick:h},{default:w(()=>p[0]||(p[0]=[e("i",{class:"pi pi-times text-xl"},null,-1)])),_:1},8,["aria-label"]),e("div",gt,[e("h2",_t,t(u.$t("subscription.description")),1)]),f(it,{class:"flex-1"}),e("div",yt,[e("p",ht,t(u.$t("subscription.haveQuestions")),1),e("div",wt,[f(C,{variant:"muted-textonly",class:"h-6 p-1 text-sm text-text-secondary hover:text-base-foreground",onClick:N},{default:w(()=>[A(t(u.$t("subscription.contactUs"))+" ",1),p[1]||(p[1]=e("i",{class:"pi pi-comments"},null,-1))]),_:1}),e("span",kt,t(u.$t("g.or")),1),f(C,{variant:"muted-textonly",class:"h-6 p-1 text-sm text-text-secondary hover:text-base-foreground",onClick:B},{default:w(()=>[A(t(u.$t("subscription.viewEnterprise"))+" ",1),p[2]||(p[2]=e("i",{class:"pi pi-external-link"},null,-1))]),_:1})])])])):(g(),b("div",Ct,[f(C,{size:"icon",variant:"muted-textonly",class:"rounded-full absolute top-2.5 right-2.5 z-10 h-8 w-8 p-0 text-white hover:bg-white/20","aria-label":u.$t("g.close"),onClick:h},{default:w(()=>p[3]||(p[3]=[e("i",{class:"pi pi-times"},null,-1)])),_:1},8,["aria-label"]),p[4]||(p[4]=e("div",{class:"relative col-span-2 flex items-center justify-center overflow-hidden rounded-sm"},[e("video",{autoplay:"",loop:"",muted:"",playsinline:"",class:"h-full min-w-[125%] object-cover p-0",style:{"margin-left":"-20%"}},[e("source",{src:""+new URL("images/cloud-subscription.webm",import.meta.url).href,type:"video/webm"})])],-1)),e("div",$t,[e("div",null,[e("div",Pt,[e("div",Tt,[e("div",St,t(u.$t("subscription.required.title")),1),f(me,{"reverse-order":"","no-padding":"","background-color":"var(--p-dialog-background)","use-subscription":""})]),e("div",Lt,[e("span",Et,t(l(L)),1),e("span",It,t(u.$t("subscription.perMonth")),1)])]),f(xt,{class:"mt-6 text-muted"})]),e("div",Rt,[f(fe,{class:"py-2 px-4 rounded-lg",pt:{root:{style:"background: var(--color-accent-blue, #0B8CE9);"},label:{class:"font-inter font-[700] text-sm"}},onSubscribed:R})])])]))}}),os=q(Nt,[["__scopeId","data-v-80c6bb1c"]]);export{os as default};
2
2
 
3
- //# sourceMappingURL=SubscriptionRequiredDialogContent--VmT16oc.js.map
3
+ //# sourceMappingURL=SubscriptionRequiredDialogContent-COEF2VQ_.js.map