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":"PanelTemplate-BjN5XNg2.js","names":[],"sources":["../../src/composables/useExternalLink.ts","../../src/stores/systemStatsStore.ts","../../packages/shared-frontend-utils/src/formatUtil.ts","../../src/stores/extensionStore.ts","../../src/components/dialog/content/setting/PanelTemplate.vue","../../src/components/dialog/content/setting/PanelTemplate.vue"],"sourcesContent":["import { computed } from 'vue'\n\nimport { electronAPI, isElectron } from '@/utils/envUtil'\nimport { i18n } from '@/i18n'\n\n/**\n * Composable for building docs.comfy.org URLs with automatic locale and platform detection\n *\n * @example\n * ```ts\n * const { buildDocsUrl } = useExternalLink()\n *\n * // Simple usage\n * const changelogUrl = buildDocsUrl('/changelog', { includeLocale: true })\n * // => 'https://docs.comfy.org/zh-CN/changelog' (if Chinese)\n *\n * // With platform detection\n * const desktopUrl = buildDocsUrl('/installation/desktop', {\n * includeLocale: true,\n * platform: true\n * })\n * // => 'https://docs.comfy.org/zh-CN/installation/desktop/macos' (if Chinese + macOS)\n * ```\n */\nexport function useExternalLink() {\n const locale = computed(() => String(i18n.global.locale.value))\n\n const isChinese = computed(() => {\n return locale.value === 'zh' || locale.value === 'zh-TW'\n })\n\n const platform = computed(() => {\n if (!isElectron()) {\n return null\n }\n\n const electronPlatform = electronAPI().getPlatform()\n return electronPlatform === 'darwin' ? 'macos' : 'windows'\n })\n\n /**\n * Build a docs.comfy.org URL with optional locale and platform\n *\n * @param path - The path after the domain (e.g., '/installation/desktop')\n * @param options - Options for building the URL\n * @param options.includeLocale - Whether to include locale prefix (default: false)\n * @param options.platform - Whether to include platform suffix (default: false)\n * @returns The complete docs URL\n *\n * @example\n * ```ts\n * buildDocsUrl('/changelog') // => 'https://docs.comfy.org/changelog'\n * buildDocsUrl('/changelog', { includeLocale: true }) // => 'https://docs.comfy.org/zh-CN/changelog' (if Chinese)\n * buildDocsUrl('/installation/desktop', { includeLocale: true, platform: true })\n * // => 'https://docs.comfy.org/zh-CN/installation/desktop/macos' (if Chinese + macOS)\n * ```\n */\n const buildDocsUrl = (\n path: string,\n options: {\n includeLocale?: boolean\n platform?: boolean\n } = {}\n ): string => {\n const { includeLocale = false, platform: includePlatform = false } = options\n\n let url = 'https://docs.comfy.org'\n\n if (includeLocale && isChinese.value) {\n url += '/zh-CN'\n }\n\n const normalizedPath = path.startsWith('/') ? path : `/${path}`\n url += normalizedPath\n\n if (includePlatform && platform.value) {\n url = url.endsWith('/') ? url : `${url}/`\n url += platform.value\n }\n\n return url\n }\n\n const staticUrls = {\n // Static external URLs\n discord: 'https://www.comfy.org/discord',\n github: 'https://github.com/comfyanonymous/ComfyUI',\n githubIssues: 'https://github.com/comfyanonymous/ComfyUI/issues',\n githubFrontend: 'https://github.com/Comfy-Org/ComfyUI_frontend',\n githubElectron: 'https://github.com/Comfy-Org/electron',\n forum: 'https://forum.comfy.org/',\n comfyOrg: 'https://www.comfy.org/'\n }\n\n /** Common doc paths for use with buildDocsUrl */\n const docsPaths = {\n partnerNodesPricing: '/tutorials/partner-nodes/pricing'\n }\n\n return {\n buildDocsUrl,\n staticUrls,\n docsPaths\n }\n}\n","import { useAsyncState } from '@vueuse/core'\nimport { defineStore } from 'pinia'\n\nimport { isCloud } from '@/platform/distribution/types'\nimport type { SystemStats } from '@/schemas/apiSchema'\nimport { api } from '@/scripts/api'\nimport { isElectron } from '@/utils/envUtil'\n\nexport const useSystemStatsStore = defineStore('systemStats', () => {\n const fetchSystemStatsData = async () => {\n try {\n return await api.getSystemStats()\n } catch (err) {\n console.error('Error fetching system stats:', err)\n throw err\n }\n }\n\n const {\n state: systemStats,\n isLoading,\n error,\n isReady: isInitialized,\n execute: refetchSystemStats\n } = useAsyncState<SystemStats | null>(\n fetchSystemStatsData,\n null, // initial value\n {\n immediate: true\n }\n )\n\n function getFormFactor(): string {\n if (isCloud) {\n return 'cloud'\n }\n\n if (!systemStats.value?.system?.os) {\n return 'other'\n }\n\n const os = systemStats.value.system.os.toLowerCase()\n const isDesktop = isElectron()\n\n if (isDesktop) {\n if (os.includes('windows')) {\n return 'desktop-windows'\n }\n if (os.includes('darwin') || os.includes('mac')) {\n return 'desktop-mac'\n }\n } else {\n // Git/source installation\n if (os.includes('windows')) {\n return 'git-windows'\n }\n if (os.includes('darwin') || os.includes('mac')) {\n return 'git-mac'\n }\n if (os.includes('linux')) {\n return 'git-linux'\n }\n }\n\n return 'other'\n }\n\n return {\n systemStats,\n isLoading,\n error,\n isInitialized,\n refetchSystemStats,\n getFormFactor\n }\n})\n","import type { operations } from '@comfyorg/registry-types'\n\nexport function formatCamelCase(str: string): string {\n // Check if the string is camel case\n const isCamelCase = /^([A-Z][a-z]*)+$/.test(str)\n\n if (!isCamelCase) {\n return str // Return original string if not camel case\n }\n\n // Split the string into words, keeping acronyms together\n const words = str.split(/(?=[A-Z][a-z])|\\d+/)\n\n // Process each word\n const processedWords = words.map((word) => {\n // If the word is all uppercase and longer than one character, it's likely an acronym\n if (word.length > 1 && word === word.toUpperCase()) {\n return word // Keep acronyms as is\n }\n // For other words, ensure the first letter is capitalized\n return word.charAt(0).toUpperCase() + word.slice(1)\n })\n\n // Join the words with spaces\n return processedWords.join(' ')\n}\n\nexport function appendJsonExt(path: string) {\n if (!path.toLowerCase().endsWith('.json')) {\n path += '.json'\n }\n return path\n}\n\nexport function highlightQuery(text: string, query: string) {\n if (!query) return text\n\n // Escape special regex characters in the query string\n const escapedQuery = query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n const regex = new RegExp(`(${escapedQuery})`, 'gi')\n return text.replace(regex, '<span class=\"highlight\">$1</span>')\n}\n\nexport function formatNumberWithSuffix(\n num: number,\n {\n precision = 1,\n roundToInt = false\n }: { precision?: number; roundToInt?: boolean } = {}\n): string {\n const suffixes = ['', 'k', 'm', 'b', 't']\n const absNum = Math.abs(num)\n\n if (absNum < 1000) {\n return roundToInt ? Math.round(num).toString() : num.toFixed(precision)\n }\n\n const exp = Math.min(Math.floor(Math.log10(absNum) / 3), suffixes.length - 1)\n const formattedNum = (num / Math.pow(1000, exp)).toFixed(precision)\n\n return `${formattedNum}${suffixes[exp]}`\n}\n\nexport function formatSize(value?: number) {\n if (value === null || value === undefined) {\n return '-'\n }\n\n const bytes = value\n if (bytes === 0) return '0 B'\n const k = 1024\n const sizes = ['B', 'KB', 'MB', 'GB']\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`\n}\n\n/**\n * Formats a commit hash by truncating long (40-char) hashes to 7 chars.\n * Returns the original string if not a valid full commit hash.\n */\nexport function formatCommitHash(value: string): string {\n if (/^[a-f0-9]{40}$/i.test(value)) {\n return value.slice(0, 7)\n }\n return value\n}\n\n/**\n * Returns various filename components.\n * Example:\n * - fullFilename: 'file.txt'\n * - filename: 'file'\n * - suffix: 'txt'\n */\nexport function getFilenameDetails(fullFilename: string) {\n if (fullFilename.includes('.')) {\n return {\n filename: fullFilename.split('.').slice(0, -1).join('.'),\n suffix: fullFilename.split('.').pop() ?? null\n }\n } else {\n return { filename: fullFilename, suffix: null }\n }\n}\n\n/**\n * Returns various path components.\n * Example:\n * - path: 'dir/file.txt'\n * - directory: 'dir'\n * - fullFilename: 'file.txt'\n * - filename: 'file'\n * - suffix: 'txt'\n */\nexport function getPathDetails(path: string) {\n const directory = path.split('/').slice(0, -1).join('/')\n const fullFilename = path.split('/').pop() ?? path\n return { directory, fullFilename, ...getFilenameDetails(fullFilename) }\n}\n\n/**\n * Normalizes a string to be used as an i18n key.\n * Replaces dots with underscores.\n */\nexport function normalizeI18nKey(key: string) {\n return typeof key === 'string' ? key.replace(/\\./g, '_') : ''\n}\n\n/**\n * Takes a dynamic prompt in the format {opt1|opt2|{optA|optB}|} and randomly replaces groups. Supports C style comments.\n * @param input The dynamic prompt to process\n * @returns\n */\nexport function processDynamicPrompt(input: string): string {\n /*\n * Strips C-style line and block comments from a string\n */\n function stripComments(str: string) {\n return str.replace(/\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*/g, '')\n }\n\n let i = 0\n let result = ''\n input = stripComments(input)\n\n const handleEscape = () => {\n const nextChar = input[i++]\n return '\\\\' + nextChar\n }\n\n function parseChoiceBlock() {\n // Parse the content inside {}\n const options: string[] = []\n let choice = ''\n let depth = 0\n\n while (i < input.length) {\n const char = input[i++]\n\n if (char === '\\\\') {\n choice += handleEscape()\n continue\n } else if (char === '{') {\n depth++\n } else if (char === '}') {\n if (!depth) break\n depth--\n } else if (char === '|') {\n if (!depth) {\n options.push(choice)\n choice = ''\n continue\n }\n }\n choice += char\n }\n\n options.push(choice)\n\n const chosenOption = options[Math.floor(Math.random() * options.length)]\n return processDynamicPrompt(chosenOption)\n }\n\n while (i < input.length) {\n const char = input[i++]\n if (char === '\\\\') {\n result += handleEscape()\n } else if (char === '{') {\n result += parseChoiceBlock()\n } else {\n result += char\n }\n }\n\n return result.replace(/\\\\([{}|])/g, '$1')\n}\n\nexport function isValidUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parses a filepath into its filename and subfolder components.\n *\n * @example\n * parseFilePath('folder/file.txt') // → { filename: 'file.txt', subfolder: 'folder' }\n * parseFilePath('/folder/file.txt') // → { filename: 'file.txt', subfolder: 'folder' }\n * parseFilePath('file.txt') // → { filename: 'file.txt', subfolder: '' }\n * parseFilePath('folder//file.txt') // → { filename: 'file.txt', subfolder: 'folder' }\n *\n * @param filepath The filepath to parse\n * @returns Object containing filename and subfolder\n */\nexport function parseFilePath(filepath: string): {\n filename: string\n subfolder: string\n} {\n if (!filepath?.trim()) return { filename: '', subfolder: '' }\n\n const normalizedPath = filepath\n .replace(/[\\\\/]+/g, '/') // Normalize path separators\n .replace(/^\\//, '') // Remove leading slash\n .replace(/\\/$/, '') // Remove trailing slash\n\n const lastSlashIndex = normalizedPath.lastIndexOf('/')\n\n if (lastSlashIndex === -1) {\n return {\n filename: normalizedPath,\n subfolder: ''\n }\n }\n\n return {\n filename: normalizedPath.slice(lastSlashIndex + 1),\n subfolder: normalizedPath.slice(0, lastSlashIndex)\n }\n}\n\n// Simple date formatter\nconst parts = {\n d: (d: Date) => d.getDate(),\n M: (d: Date) => d.getMonth() + 1,\n h: (d: Date) => d.getHours(),\n m: (d: Date) => d.getMinutes(),\n s: (d: Date) => d.getSeconds()\n}\nconst format =\n Object.keys(parts)\n .map((k) => k + k + '?')\n .join('|') + '|yyy?y?'\n\nexport function formatDate(text: string, date: Date) {\n return text.replace(new RegExp(format, 'g'), (text: string): string => {\n if (text === 'yy') return (date.getFullYear() + '').substring(2)\n if (text === 'yyyy') return date.getFullYear().toString()\n if (text[0] in parts) {\n const p = parts[text[0] as keyof typeof parts](date)\n return (p + '').padStart(text.length, '0')\n }\n return text\n })\n}\n\n/**\n * Generate a cache key from parameters\n * Sorts the parameters to ensure consistent keys regardless of parameter order\n */\nexport const paramsToCacheKey = (params: unknown): string => {\n if (typeof params === 'string') return params\n if (typeof params === 'object' && params !== null)\n return Object.keys(params)\n .sort((a, b) => a.localeCompare(b))\n .map((key) => `${key}:${params[key as keyof typeof params]}`)\n .join('&')\n\n return String(params)\n}\n\n/**\n * Generates a RFC4122 compliant UUID v4 using the native crypto API when available\n * @returns A properly formatted UUID string\n */\nexport const generateUUID = (): string => {\n // Use native crypto.randomUUID() if available (modern browsers)\n if (\n typeof crypto !== 'undefined' &&\n typeof crypto.randomUUID === 'function'\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback implementation for older browsers\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n const v = c === 'x' ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\n/**\n * Checks if a URL is a Civitai model URL\n * @example\n * isCivitaiModelUrl('https://civitai.com/api/download/models/1234567890') // true\n * isCivitaiModelUrl('https://civitai.com/api/v1/models/1234567890') // true\n * isCivitaiModelUrl('https://civitai.com/api/v1/models-versions/15342') // true\n * isCivitaiModelUrl('https://example.com/model.safetensors') // false\n */\nexport const isCivitaiModelUrl = (url: string): boolean => {\n if (!isValidUrl(url)) return false\n if (!url.includes('civitai.com')) return false\n\n const urlObj = new URL(url)\n const pathname = urlObj.pathname\n\n return (\n /^\\/api\\/download\\/models\\/(\\d+)$/.test(pathname) ||\n /^\\/api\\/v1\\/models\\/(\\d+)$/.test(pathname) ||\n /^\\/api\\/v1\\/models-versions\\/(\\d+)$/.test(pathname)\n )\n}\n\n/**\n * Converts a Hugging Face download URL to a repository page URL\n * @param url The download URL to convert\n * @returns The repository page URL or the original URL if conversion fails\n * @example\n * downloadUrlToHfRepoUrl(\n * 'https://huggingface.co/bfl/FLUX.1/resolve/main/flux1-canny-dev.safetensors?download=true'\n * ) // https://huggingface.co/bfl/FLUX.1\n */\nexport const downloadUrlToHfRepoUrl = (url: string): string => {\n try {\n const urlObj = new URL(url)\n const pathname = urlObj.pathname\n\n // Use regex to match everything before /resolve/ or /blob/\n const regex = /^(.*?)(?:\\/resolve\\/|\\/blob\\/|$)/\n const repoPathMatch = regex.exec(pathname)\n\n // Extract the repository path and remove leading slash if present\n const repoPath = repoPathMatch?.[1]?.replace(/^\\//, '') || ''\n\n return `https://huggingface.co/${repoPath}`\n } catch (error) {\n return url\n }\n}\n\n/**\n * Converts Metronome's integer amount back to a formatted currency string.\n * For USD, converts from cents to dollars.\n * For all other currencies (including custom pricing units), returns the amount as is.\n * This is specific to Metronome's API requirements.\n *\n * @param amount - The amount in Metronome's integer format (cents for USD, base units for others)\n * @param currency - The currency to convert\n * @returns The formatted amount in currency with 2 decimal places for USD\n * @example\n * formatMetronomeCurrency(123, 'usd') // returns \"1.23\" (cents to USD)\n * formatMetronomeCurrency(1000, 'jpy') // returns \"1000\" (yen)\n */\nexport function formatMetronomeCurrency(\n amount: number,\n currency: string\n): string {\n if (currency === 'usd') {\n return (amount / 100).toFixed(2)\n }\n return amount.toString()\n}\n\n/**\n * Converts a USD amount to microdollars (1/1,000,000 of a dollar).\n * This conversion is commonly used in financial systems to avoid floating-point precision issues\n * by representing monetary values as integers.\n *\n * @param usd - The amount in US dollars to convert\n * @returns The amount in microdollars (multiplied by 1,000,000)\n * @example\n * usdToMicros(1.23) // returns 1230000\n */\nexport function usdToMicros(usd: number): number {\n return Math.round(usd * 1_000_000)\n}\n\n/**\n * Converts URLs in a string to HTML links.\n * @param text - The string to convert\n * @returns The string with URLs converted to HTML links\n * @example\n * linkifyHtml('Visit https://example.com for more info') // returns 'Visit <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary-400 hover:underline\">https://example.com</a> for more info'\n */\nexport function linkifyHtml(text: string): string {\n if (!text) return ''\n const urlRegex =\n /(\\b(https?|ftp|file):\\/\\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%?=~_|])|(\\bwww\\.[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%?=~_|])/gi\n return text.replace(urlRegex, (_match, p1, _p2, p3) => {\n const url = p1 || p3\n const href = p3 ? `http://${url}` : url\n return `<a href=\"${href}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary-400 hover:underline\">${url}</a>`\n })\n}\n\n/**\n * Converts newline characters to HTML <br> tags.\n * @param text - The string to convert\n * @returns The string with newline characters converted to <br> tags\n * @example\n * nl2br('Hello\\nWorld') // returns 'Hello<br />World'\n */\nexport function nl2br(text: string): string {\n if (!text) return ''\n return text.replace(/\\n/g, '<br />')\n}\n\n/**\n * Converts a version string to an anchor-safe format by replacing dots with dashes.\n * @param version The version string (e.g., \"1.0.0\", \"2.1.3-beta.1\")\n * @returns The anchor-safe version string (e.g., \"v1-0-0\", \"v2-1-3-beta-1\")\n * @example\n * formatVersionAnchor(\"1.0.0\") // returns \"v1-0-0\"\n * formatVersionAnchor(\"2.1.3-beta.1\") // returns \"v2-1-3-beta-1\"\n */\nexport function formatVersionAnchor(version: string): string {\n return `v${version.replace(/\\./g, '-')}`\n}\n\n/**\n * Supported locale types for the application (from OpenAPI schema)\n */\ntype SupportedLocale = NonNullable<\n operations['getReleaseNotes']['parameters']['query']['locale']\n>\n\n/**\n * Converts a string to a valid locale type with 'en' as default\n * @param locale - The locale string to validate and convert\n * @returns A valid SupportedLocale type, defaults to 'en' if invalid\n * @example\n * stringToLocale('fr') // returns 'fr'\n * stringToLocale('invalid') // returns 'en'\n * stringToLocale('') // returns 'en'\n */\nexport function stringToLocale(locale: string): SupportedLocale {\n const supportedLocales: SupportedLocale[] = [\n 'en',\n 'es',\n 'fr',\n 'ja',\n 'ko',\n 'ru',\n 'zh'\n ]\n return supportedLocales.includes(locale as SupportedLocale)\n ? (locale as SupportedLocale)\n : 'en'\n}\n\nexport function formatDuration(milliseconds: number): string {\n if (!milliseconds || milliseconds < 0) return '0s'\n\n const totalSeconds = Math.floor(milliseconds / 1000)\n const hours = Math.floor(totalSeconds / 3600)\n const minutes = Math.floor((totalSeconds % 3600) / 60)\n const remainingSeconds = Math.floor(totalSeconds % 60)\n\n const parts: string[] = []\n\n if (hours > 0) {\n parts.push(`${hours}h`)\n }\n if (minutes > 0) {\n parts.push(`${minutes}m`)\n }\n if (remainingSeconds > 0 || parts.length === 0) {\n parts.push(`${remainingSeconds}s`)\n }\n\n return parts.join(' ')\n}\n\nconst IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp'] as const\nconst VIDEO_EXTENSIONS = ['mp4', 'webm', 'mov', 'avi'] as const\nconst AUDIO_EXTENSIONS = ['mp3', 'wav', 'ogg', 'flac'] as const\nconst THREE_D_EXTENSIONS = ['obj', 'fbx', 'gltf', 'glb'] as const\n\nconst MEDIA_TYPES = ['image', 'video', 'audio', '3D'] as const\ntype MediaType = (typeof MEDIA_TYPES)[number]\n\n// Type guard helper for checking array membership\ntype ImageExtension = (typeof IMAGE_EXTENSIONS)[number]\ntype VideoExtension = (typeof VIDEO_EXTENSIONS)[number]\ntype AudioExtension = (typeof AUDIO_EXTENSIONS)[number]\ntype ThreeDExtension = (typeof THREE_D_EXTENSIONS)[number]\n\n/**\n * Truncates a filename while preserving the extension\n * @param filename The filename to truncate\n * @param maxLength Maximum length for the filename without extension\n * @returns Truncated filename with extension preserved\n */\nexport function truncateFilename(\n filename: string,\n maxLength: number = 20\n): string {\n if (!filename || filename.length <= maxLength) {\n return filename\n }\n\n const lastDotIndex = filename.lastIndexOf('.')\n const nameWithoutExt =\n lastDotIndex > -1 ? filename.substring(0, lastDotIndex) : filename\n const extension = lastDotIndex > -1 ? filename.substring(lastDotIndex) : ''\n\n // If the name without extension is short enough, return as is\n if (nameWithoutExt.length <= maxLength) {\n return filename\n }\n\n // Calculate how to split the truncation\n const halfLength = Math.floor((maxLength - 3) / 2) // -3 for '...'\n const start = nameWithoutExt.substring(0, halfLength)\n const end = nameWithoutExt.substring(nameWithoutExt.length - halfLength)\n\n return `${start}...${end}${extension}`\n}\n\n/**\n * Determines the media type from a filename's extension (singular form)\n * @param filename The filename to analyze\n * @returns The media type: 'image', 'video', 'audio', or '3D'\n */\nexport function getMediaTypeFromFilename(filename: string): MediaType {\n if (!filename) return 'image'\n const ext = filename.split('.').pop()?.toLowerCase()\n if (!ext) return 'image'\n\n // Type-safe array includes check using type assertion\n if (IMAGE_EXTENSIONS.includes(ext as ImageExtension)) return 'image'\n if (VIDEO_EXTENSIONS.includes(ext as VideoExtension)) return 'video'\n if (AUDIO_EXTENSIONS.includes(ext as AudioExtension)) return 'audio'\n if (THREE_D_EXTENSIONS.includes(ext as ThreeDExtension)) return '3D'\n\n return 'image'\n}\n","import { defineStore } from 'pinia'\nimport { computed, markRaw, ref } from 'vue'\n\nimport type { ComfyExtension } from '@/types/comfy'\n\n/**\n * These extensions are always active, even if they are disabled in the setting.\n */\nconst ALWAYS_ENABLED_EXTENSIONS: readonly string[] = []\n\nconst ALWAYS_DISABLED_EXTENSIONS: readonly string[] = [\n // pysssss.Locking is replaced by pin/unpin in ComfyUI core.\n // https://github.com/Comfy-Org/litegraph.js/pull/117\n 'pysssss.Locking',\n // pysssss.SnapToGrid is replaced by Comfy.Graph.AlwaysSnapToGrid in ComfyUI core.\n // pysssss.SnapToGrid tries to write global app.shiftDown state, which is no longer\n // allowed since v1.3.12.\n // https://github.com/Comfy-Org/ComfyUI_frontend/issues/1176\n 'pysssss.SnapToGrid',\n // Favicon status is implemented in ComfyUI core in v1.20.\n // https://github.com/Comfy-Org/ComfyUI_frontend/pull/3880\n 'pysssss.FaviconStatus',\n 'KJNodes.browserstatus'\n]\n\nexport const useExtensionStore = defineStore('extension', () => {\n // For legacy reasons, the name uniquely identifies an extension\n const extensionByName = ref<Record<string, ComfyExtension>>({})\n const extensions = computed(() => Object.values(extensionByName.value))\n // Not using computed because disable extension requires reloading of the page.\n // Dynamically update this list won't affect extensions that are already loaded.\n const disabledExtensionNames = ref<Set<string>>(new Set())\n\n // Disabled extension names that are currently not in the extension list.\n // If a node pack is disabled in the backend, we shouldn't remove the configuration\n // of the frontend extension disable list, in case the node pack is re-enabled.\n const inactiveDisabledExtensionNames = computed(() => {\n return Array.from(disabledExtensionNames.value).filter(\n (name) => !(name in extensionByName.value)\n )\n })\n\n const isExtensionInstalled = (name: string) => name in extensionByName.value\n\n const isExtensionEnabled = (name: string) =>\n !disabledExtensionNames.value.has(name)\n const enabledExtensions = computed(() => {\n return extensions.value.filter((ext) => isExtensionEnabled(ext.name))\n })\n\n function isExtensionReadOnly(name: string) {\n return (\n ALWAYS_DISABLED_EXTENSIONS.includes(name) ||\n ALWAYS_ENABLED_EXTENSIONS.includes(name)\n )\n }\n\n function registerExtension(extension: ComfyExtension) {\n if (!extension.name) {\n throw new Error(\"Extensions must have a 'name' property.\")\n }\n\n if (extensionByName.value[extension.name]) {\n throw new Error(`Extension named '${extension.name}' already registered.`)\n }\n\n if (disabledExtensionNames.value.has(extension.name)) {\n console.warn(`Extension ${extension.name} is disabled.`)\n }\n\n extensionByName.value[extension.name] = markRaw(extension)\n }\n\n function loadDisabledExtensionNames(names: string[]) {\n disabledExtensionNames.value = new Set(names)\n for (const name of ALWAYS_DISABLED_EXTENSIONS) {\n disabledExtensionNames.value.add(name)\n }\n for (const name of ALWAYS_ENABLED_EXTENSIONS) {\n disabledExtensionNames.value.delete(name)\n }\n }\n\n /**\n * Core extensions are extensions that are defined in the core package.\n * See /extensions/core/index.ts for the list.\n */\n const coreExtensionNames = ref<string[]>([])\n function captureCoreExtensions() {\n coreExtensionNames.value = extensions.value.map((ext) => ext.name)\n }\n\n function isCoreExtension(name: string) {\n return coreExtensionNames.value.includes(name)\n }\n\n const hasThirdPartyExtensions = computed(() => {\n return extensions.value.some((ext) => !isCoreExtension(ext.name))\n })\n\n return {\n extensions,\n enabledExtensions,\n inactiveDisabledExtensionNames,\n isExtensionInstalled,\n isExtensionEnabled,\n isExtensionReadOnly,\n registerExtension,\n loadDisabledExtensionNames,\n captureCoreExtensions,\n isCoreExtension,\n hasThirdPartyExtensions\n }\n})\n","<template>\n <TabPanel :value=\"props.value\" class=\"h-full w-full\" :class=\"props.class\">\n <div class=\"flex h-full w-full flex-col gap-2\">\n <slot name=\"header\" />\n <ScrollPanel class=\"h-0 grow pr-2\">\n <slot />\n </ScrollPanel>\n <slot name=\"footer\" />\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport ScrollPanel from 'primevue/scrollpanel'\nimport TabPanel from 'primevue/tabpanel'\n\nconst props = defineProps<{\n value: string\n class?: string\n}>()\n</script>\n","<template>\n <TabPanel :value=\"props.value\" class=\"h-full w-full\" :class=\"props.class\">\n <div class=\"flex h-full w-full flex-col gap-2\">\n <slot name=\"header\" />\n <ScrollPanel class=\"h-0 grow pr-2\">\n <slot />\n </ScrollPanel>\n <slot name=\"footer\" />\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport ScrollPanel from 'primevue/scrollpanel'\nimport TabPanel from 'primevue/tabpanel'\n\nconst props = defineProps<{\n value: string\n class?: string\n}>()\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PanelTemplate-BJda9e5J.js","names":[],"sources":["../../src/composables/useExternalLink.ts","../../src/stores/systemStatsStore.ts","../../packages/shared-frontend-utils/src/formatUtil.ts","../../src/stores/extensionStore.ts","../../src/components/dialog/content/setting/PanelTemplate.vue","../../src/components/dialog/content/setting/PanelTemplate.vue"],"sourcesContent":["import { computed } from 'vue'\n\nimport { electronAPI, isElectron } from '@/utils/envUtil'\nimport { i18n } from '@/i18n'\n\n/**\n * Composable for building docs.comfy.org URLs with automatic locale and platform detection\n *\n * @example\n * ```ts\n * const { buildDocsUrl } = useExternalLink()\n *\n * // Simple usage\n * const changelogUrl = buildDocsUrl('/changelog', { includeLocale: true })\n * // => 'https://docs.comfy.org/zh-CN/changelog' (if Chinese)\n *\n * // With platform detection\n * const desktopUrl = buildDocsUrl('/installation/desktop', {\n * includeLocale: true,\n * platform: true\n * })\n * // => 'https://docs.comfy.org/zh-CN/installation/desktop/macos' (if Chinese + macOS)\n * ```\n */\nexport function useExternalLink() {\n const locale = computed(() => String(i18n.global.locale.value))\n\n const isChinese = computed(() => {\n return locale.value === 'zh' || locale.value === 'zh-TW'\n })\n\n const platform = computed(() => {\n if (!isElectron()) {\n return null\n }\n\n const electronPlatform = electronAPI().getPlatform()\n return electronPlatform === 'darwin' ? 'macos' : 'windows'\n })\n\n /**\n * Build a docs.comfy.org URL with optional locale and platform\n *\n * @param path - The path after the domain (e.g., '/installation/desktop')\n * @param options - Options for building the URL\n * @param options.includeLocale - Whether to include locale prefix (default: false)\n * @param options.platform - Whether to include platform suffix (default: false)\n * @returns The complete docs URL\n *\n * @example\n * ```ts\n * buildDocsUrl('/changelog') // => 'https://docs.comfy.org/changelog'\n * buildDocsUrl('/changelog', { includeLocale: true }) // => 'https://docs.comfy.org/zh-CN/changelog' (if Chinese)\n * buildDocsUrl('/installation/desktop', { includeLocale: true, platform: true })\n * // => 'https://docs.comfy.org/zh-CN/installation/desktop/macos' (if Chinese + macOS)\n * ```\n */\n const buildDocsUrl = (\n path: string,\n options: {\n includeLocale?: boolean\n platform?: boolean\n } = {}\n ): string => {\n const { includeLocale = false, platform: includePlatform = false } = options\n\n let url = 'https://docs.comfy.org'\n\n if (includeLocale && isChinese.value) {\n url += '/zh-CN'\n }\n\n const normalizedPath = path.startsWith('/') ? path : `/${path}`\n url += normalizedPath\n\n if (includePlatform && platform.value) {\n url = url.endsWith('/') ? url : `${url}/`\n url += platform.value\n }\n\n return url\n }\n\n const staticUrls = {\n // Static external URLs\n discord: 'https://www.comfy.org/discord',\n github: 'https://github.com/comfyanonymous/ComfyUI',\n githubIssues: 'https://github.com/comfyanonymous/ComfyUI/issues',\n githubFrontend: 'https://github.com/Comfy-Org/ComfyUI_frontend',\n githubElectron: 'https://github.com/Comfy-Org/electron',\n forum: 'https://forum.comfy.org/',\n comfyOrg: 'https://www.comfy.org/'\n }\n\n /** Common doc paths for use with buildDocsUrl */\n const docsPaths = {\n partnerNodesPricing: '/tutorials/partner-nodes/pricing'\n }\n\n return {\n buildDocsUrl,\n staticUrls,\n docsPaths\n }\n}\n","import { useAsyncState } from '@vueuse/core'\nimport { defineStore } from 'pinia'\n\nimport { isCloud } from '@/platform/distribution/types'\nimport type { SystemStats } from '@/schemas/apiSchema'\nimport { api } from '@/scripts/api'\nimport { isElectron } from '@/utils/envUtil'\n\nexport const useSystemStatsStore = defineStore('systemStats', () => {\n const fetchSystemStatsData = async () => {\n try {\n return await api.getSystemStats()\n } catch (err) {\n console.error('Error fetching system stats:', err)\n throw err\n }\n }\n\n const {\n state: systemStats,\n isLoading,\n error,\n isReady: isInitialized,\n execute: refetchSystemStats\n } = useAsyncState<SystemStats | null>(\n fetchSystemStatsData,\n null, // initial value\n {\n immediate: true\n }\n )\n\n function getFormFactor(): string {\n if (isCloud) {\n return 'cloud'\n }\n\n if (!systemStats.value?.system?.os) {\n return 'other'\n }\n\n const os = systemStats.value.system.os.toLowerCase()\n const isDesktop = isElectron()\n\n if (isDesktop) {\n if (os.includes('windows')) {\n return 'desktop-windows'\n }\n if (os.includes('darwin') || os.includes('mac')) {\n return 'desktop-mac'\n }\n } else {\n // Git/source installation\n if (os.includes('windows')) {\n return 'git-windows'\n }\n if (os.includes('darwin') || os.includes('mac')) {\n return 'git-mac'\n }\n if (os.includes('linux')) {\n return 'git-linux'\n }\n }\n\n return 'other'\n }\n\n return {\n systemStats,\n isLoading,\n error,\n isInitialized,\n refetchSystemStats,\n getFormFactor\n }\n})\n","import type { operations } from '@comfyorg/registry-types'\n\nexport function formatCamelCase(str: string): string {\n // Check if the string is camel case\n const isCamelCase = /^([A-Z][a-z]*)+$/.test(str)\n\n if (!isCamelCase) {\n return str // Return original string if not camel case\n }\n\n // Split the string into words, keeping acronyms together\n const words = str.split(/(?=[A-Z][a-z])|\\d+/)\n\n // Process each word\n const processedWords = words.map((word) => {\n // If the word is all uppercase and longer than one character, it's likely an acronym\n if (word.length > 1 && word === word.toUpperCase()) {\n return word // Keep acronyms as is\n }\n // For other words, ensure the first letter is capitalized\n return word.charAt(0).toUpperCase() + word.slice(1)\n })\n\n // Join the words with spaces\n return processedWords.join(' ')\n}\n\nexport function appendJsonExt(path: string) {\n if (!path.toLowerCase().endsWith('.json')) {\n path += '.json'\n }\n return path\n}\n\nexport function highlightQuery(text: string, query: string) {\n if (!query) return text\n\n // Escape special regex characters in the query string\n const escapedQuery = query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n const regex = new RegExp(`(${escapedQuery})`, 'gi')\n return text.replace(regex, '<span class=\"highlight\">$1</span>')\n}\n\nexport function formatNumberWithSuffix(\n num: number,\n {\n precision = 1,\n roundToInt = false\n }: { precision?: number; roundToInt?: boolean } = {}\n): string {\n const suffixes = ['', 'k', 'm', 'b', 't']\n const absNum = Math.abs(num)\n\n if (absNum < 1000) {\n return roundToInt ? Math.round(num).toString() : num.toFixed(precision)\n }\n\n const exp = Math.min(Math.floor(Math.log10(absNum) / 3), suffixes.length - 1)\n const formattedNum = (num / Math.pow(1000, exp)).toFixed(precision)\n\n return `${formattedNum}${suffixes[exp]}`\n}\n\nexport function formatSize(value?: number) {\n if (value === null || value === undefined) {\n return '-'\n }\n\n const bytes = value\n if (bytes === 0) return '0 B'\n const k = 1024\n const sizes = ['B', 'KB', 'MB', 'GB']\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`\n}\n\n/**\n * Formats a commit hash by truncating long (40-char) hashes to 7 chars.\n * Returns the original string if not a valid full commit hash.\n */\nexport function formatCommitHash(value: string): string {\n if (/^[a-f0-9]{40}$/i.test(value)) {\n return value.slice(0, 7)\n }\n return value\n}\n\n/**\n * Returns various filename components.\n * Example:\n * - fullFilename: 'file.txt'\n * - filename: 'file'\n * - suffix: 'txt'\n */\nexport function getFilenameDetails(fullFilename: string) {\n if (fullFilename.includes('.')) {\n return {\n filename: fullFilename.split('.').slice(0, -1).join('.'),\n suffix: fullFilename.split('.').pop() ?? null\n }\n } else {\n return { filename: fullFilename, suffix: null }\n }\n}\n\n/**\n * Returns various path components.\n * Example:\n * - path: 'dir/file.txt'\n * - directory: 'dir'\n * - fullFilename: 'file.txt'\n * - filename: 'file'\n * - suffix: 'txt'\n */\nexport function getPathDetails(path: string) {\n const directory = path.split('/').slice(0, -1).join('/')\n const fullFilename = path.split('/').pop() ?? path\n return { directory, fullFilename, ...getFilenameDetails(fullFilename) }\n}\n\n/**\n * Normalizes a string to be used as an i18n key.\n * Replaces dots with underscores.\n */\nexport function normalizeI18nKey(key: string) {\n return typeof key === 'string' ? key.replace(/\\./g, '_') : ''\n}\n\n/**\n * Takes a dynamic prompt in the format {opt1|opt2|{optA|optB}|} and randomly replaces groups. Supports C style comments.\n * @param input The dynamic prompt to process\n * @returns\n */\nexport function processDynamicPrompt(input: string): string {\n /*\n * Strips C-style line and block comments from a string\n */\n function stripComments(str: string) {\n return str.replace(/\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*/g, '')\n }\n\n let i = 0\n let result = ''\n input = stripComments(input)\n\n const handleEscape = () => {\n const nextChar = input[i++]\n return '\\\\' + nextChar\n }\n\n function parseChoiceBlock() {\n // Parse the content inside {}\n const options: string[] = []\n let choice = ''\n let depth = 0\n\n while (i < input.length) {\n const char = input[i++]\n\n if (char === '\\\\') {\n choice += handleEscape()\n continue\n } else if (char === '{') {\n depth++\n } else if (char === '}') {\n if (!depth) break\n depth--\n } else if (char === '|') {\n if (!depth) {\n options.push(choice)\n choice = ''\n continue\n }\n }\n choice += char\n }\n\n options.push(choice)\n\n const chosenOption = options[Math.floor(Math.random() * options.length)]\n return processDynamicPrompt(chosenOption)\n }\n\n while (i < input.length) {\n const char = input[i++]\n if (char === '\\\\') {\n result += handleEscape()\n } else if (char === '{') {\n result += parseChoiceBlock()\n } else {\n result += char\n }\n }\n\n return result.replace(/\\\\([{}|])/g, '$1')\n}\n\nexport function isValidUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Parses a filepath into its filename and subfolder components.\n *\n * @example\n * parseFilePath('folder/file.txt') // → { filename: 'file.txt', subfolder: 'folder' }\n * parseFilePath('/folder/file.txt') // → { filename: 'file.txt', subfolder: 'folder' }\n * parseFilePath('file.txt') // → { filename: 'file.txt', subfolder: '' }\n * parseFilePath('folder//file.txt') // → { filename: 'file.txt', subfolder: 'folder' }\n *\n * @param filepath The filepath to parse\n * @returns Object containing filename and subfolder\n */\nexport function parseFilePath(filepath: string): {\n filename: string\n subfolder: string\n} {\n if (!filepath?.trim()) return { filename: '', subfolder: '' }\n\n const normalizedPath = filepath\n .replace(/[\\\\/]+/g, '/') // Normalize path separators\n .replace(/^\\//, '') // Remove leading slash\n .replace(/\\/$/, '') // Remove trailing slash\n\n const lastSlashIndex = normalizedPath.lastIndexOf('/')\n\n if (lastSlashIndex === -1) {\n return {\n filename: normalizedPath,\n subfolder: ''\n }\n }\n\n return {\n filename: normalizedPath.slice(lastSlashIndex + 1),\n subfolder: normalizedPath.slice(0, lastSlashIndex)\n }\n}\n\n// Simple date formatter\nconst parts = {\n d: (d: Date) => d.getDate(),\n M: (d: Date) => d.getMonth() + 1,\n h: (d: Date) => d.getHours(),\n m: (d: Date) => d.getMinutes(),\n s: (d: Date) => d.getSeconds()\n}\nconst format =\n Object.keys(parts)\n .map((k) => k + k + '?')\n .join('|') + '|yyy?y?'\n\nexport function formatDate(text: string, date: Date) {\n return text.replace(new RegExp(format, 'g'), (text: string): string => {\n if (text === 'yy') return (date.getFullYear() + '').substring(2)\n if (text === 'yyyy') return date.getFullYear().toString()\n if (text[0] in parts) {\n const p = parts[text[0] as keyof typeof parts](date)\n return (p + '').padStart(text.length, '0')\n }\n return text\n })\n}\n\n/**\n * Generate a cache key from parameters\n * Sorts the parameters to ensure consistent keys regardless of parameter order\n */\nexport const paramsToCacheKey = (params: unknown): string => {\n if (typeof params === 'string') return params\n if (typeof params === 'object' && params !== null)\n return Object.keys(params)\n .sort((a, b) => a.localeCompare(b))\n .map((key) => `${key}:${params[key as keyof typeof params]}`)\n .join('&')\n\n return String(params)\n}\n\n/**\n * Generates a RFC4122 compliant UUID v4 using the native crypto API when available\n * @returns A properly formatted UUID string\n */\nexport const generateUUID = (): string => {\n // Use native crypto.randomUUID() if available (modern browsers)\n if (\n typeof crypto !== 'undefined' &&\n typeof crypto.randomUUID === 'function'\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback implementation for older browsers\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n const v = c === 'x' ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\n/**\n * Checks if a URL is a Civitai model URL\n * @example\n * isCivitaiModelUrl('https://civitai.com/api/download/models/1234567890') // true\n * isCivitaiModelUrl('https://civitai.com/api/v1/models/1234567890') // true\n * isCivitaiModelUrl('https://civitai.com/api/v1/models-versions/15342') // true\n * isCivitaiModelUrl('https://example.com/model.safetensors') // false\n */\nexport const isCivitaiModelUrl = (url: string): boolean => {\n if (!isValidUrl(url)) return false\n if (!url.includes('civitai.com')) return false\n\n const urlObj = new URL(url)\n const pathname = urlObj.pathname\n\n return (\n /^\\/api\\/download\\/models\\/(\\d+)$/.test(pathname) ||\n /^\\/api\\/v1\\/models\\/(\\d+)$/.test(pathname) ||\n /^\\/api\\/v1\\/models-versions\\/(\\d+)$/.test(pathname)\n )\n}\n\n/**\n * Converts a Hugging Face download URL to a repository page URL\n * @param url The download URL to convert\n * @returns The repository page URL or the original URL if conversion fails\n * @example\n * downloadUrlToHfRepoUrl(\n * 'https://huggingface.co/bfl/FLUX.1/resolve/main/flux1-canny-dev.safetensors?download=true'\n * ) // https://huggingface.co/bfl/FLUX.1\n */\nexport const downloadUrlToHfRepoUrl = (url: string): string => {\n try {\n const urlObj = new URL(url)\n const pathname = urlObj.pathname\n\n // Use regex to match everything before /resolve/ or /blob/\n const regex = /^(.*?)(?:\\/resolve\\/|\\/blob\\/|$)/\n const repoPathMatch = regex.exec(pathname)\n\n // Extract the repository path and remove leading slash if present\n const repoPath = repoPathMatch?.[1]?.replace(/^\\//, '') || ''\n\n return `https://huggingface.co/${repoPath}`\n } catch (error) {\n return url\n }\n}\n\n/**\n * Converts Metronome's integer amount back to a formatted currency string.\n * For USD, converts from cents to dollars.\n * For all other currencies (including custom pricing units), returns the amount as is.\n * This is specific to Metronome's API requirements.\n *\n * @param amount - The amount in Metronome's integer format (cents for USD, base units for others)\n * @param currency - The currency to convert\n * @returns The formatted amount in currency with 2 decimal places for USD\n * @example\n * formatMetronomeCurrency(123, 'usd') // returns \"1.23\" (cents to USD)\n * formatMetronomeCurrency(1000, 'jpy') // returns \"1000\" (yen)\n */\nexport function formatMetronomeCurrency(\n amount: number,\n currency: string\n): string {\n if (currency === 'usd') {\n return (amount / 100).toFixed(2)\n }\n return amount.toString()\n}\n\n/**\n * Converts a USD amount to microdollars (1/1,000,000 of a dollar).\n * This conversion is commonly used in financial systems to avoid floating-point precision issues\n * by representing monetary values as integers.\n *\n * @param usd - The amount in US dollars to convert\n * @returns The amount in microdollars (multiplied by 1,000,000)\n * @example\n * usdToMicros(1.23) // returns 1230000\n */\nexport function usdToMicros(usd: number): number {\n return Math.round(usd * 1_000_000)\n}\n\n/**\n * Converts URLs in a string to HTML links.\n * @param text - The string to convert\n * @returns The string with URLs converted to HTML links\n * @example\n * linkifyHtml('Visit https://example.com for more info') // returns 'Visit <a href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary-400 hover:underline\">https://example.com</a> for more info'\n */\nexport function linkifyHtml(text: string): string {\n if (!text) return ''\n const urlRegex =\n /(\\b(https?|ftp|file):\\/\\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%?=~_|])|(\\bwww\\.[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%?=~_|])/gi\n return text.replace(urlRegex, (_match, p1, _p2, p3) => {\n const url = p1 || p3\n const href = p3 ? `http://${url}` : url\n return `<a href=\"${href}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary-400 hover:underline\">${url}</a>`\n })\n}\n\n/**\n * Converts newline characters to HTML <br> tags.\n * @param text - The string to convert\n * @returns The string with newline characters converted to <br> tags\n * @example\n * nl2br('Hello\\nWorld') // returns 'Hello<br />World'\n */\nexport function nl2br(text: string): string {\n if (!text) return ''\n return text.replace(/\\n/g, '<br />')\n}\n\n/**\n * Converts a version string to an anchor-safe format by replacing dots with dashes.\n * @param version The version string (e.g., \"1.0.0\", \"2.1.3-beta.1\")\n * @returns The anchor-safe version string (e.g., \"v1-0-0\", \"v2-1-3-beta-1\")\n * @example\n * formatVersionAnchor(\"1.0.0\") // returns \"v1-0-0\"\n * formatVersionAnchor(\"2.1.3-beta.1\") // returns \"v2-1-3-beta-1\"\n */\nexport function formatVersionAnchor(version: string): string {\n return `v${version.replace(/\\./g, '-')}`\n}\n\n/**\n * Supported locale types for the application (from OpenAPI schema)\n */\ntype SupportedLocale = NonNullable<\n operations['getReleaseNotes']['parameters']['query']['locale']\n>\n\n/**\n * Converts a string to a valid locale type with 'en' as default\n * @param locale - The locale string to validate and convert\n * @returns A valid SupportedLocale type, defaults to 'en' if invalid\n * @example\n * stringToLocale('fr') // returns 'fr'\n * stringToLocale('invalid') // returns 'en'\n * stringToLocale('') // returns 'en'\n */\nexport function stringToLocale(locale: string): SupportedLocale {\n const supportedLocales: SupportedLocale[] = [\n 'en',\n 'es',\n 'fr',\n 'ja',\n 'ko',\n 'ru',\n 'zh'\n ]\n return supportedLocales.includes(locale as SupportedLocale)\n ? (locale as SupportedLocale)\n : 'en'\n}\n\nexport function formatDuration(milliseconds: number): string {\n if (!milliseconds || milliseconds < 0) return '0s'\n\n const totalSeconds = Math.floor(milliseconds / 1000)\n const hours = Math.floor(totalSeconds / 3600)\n const minutes = Math.floor((totalSeconds % 3600) / 60)\n const remainingSeconds = Math.floor(totalSeconds % 60)\n\n const parts: string[] = []\n\n if (hours > 0) {\n parts.push(`${hours}h`)\n }\n if (minutes > 0) {\n parts.push(`${minutes}m`)\n }\n if (remainingSeconds > 0 || parts.length === 0) {\n parts.push(`${remainingSeconds}s`)\n }\n\n return parts.join(' ')\n}\n\nconst IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp'] as const\nconst VIDEO_EXTENSIONS = ['mp4', 'webm', 'mov', 'avi'] as const\nconst AUDIO_EXTENSIONS = ['mp3', 'wav', 'ogg', 'flac'] as const\nconst THREE_D_EXTENSIONS = ['obj', 'fbx', 'gltf', 'glb'] as const\n\nconst MEDIA_TYPES = ['image', 'video', 'audio', '3D'] as const\ntype MediaType = (typeof MEDIA_TYPES)[number]\n\n// Type guard helper for checking array membership\ntype ImageExtension = (typeof IMAGE_EXTENSIONS)[number]\ntype VideoExtension = (typeof VIDEO_EXTENSIONS)[number]\ntype AudioExtension = (typeof AUDIO_EXTENSIONS)[number]\ntype ThreeDExtension = (typeof THREE_D_EXTENSIONS)[number]\n\n/**\n * Truncates a filename while preserving the extension\n * @param filename The filename to truncate\n * @param maxLength Maximum length for the filename without extension\n * @returns Truncated filename with extension preserved\n */\nexport function truncateFilename(\n filename: string,\n maxLength: number = 20\n): string {\n if (!filename || filename.length <= maxLength) {\n return filename\n }\n\n const lastDotIndex = filename.lastIndexOf('.')\n const nameWithoutExt =\n lastDotIndex > -1 ? filename.substring(0, lastDotIndex) : filename\n const extension = lastDotIndex > -1 ? filename.substring(lastDotIndex) : ''\n\n // If the name without extension is short enough, return as is\n if (nameWithoutExt.length <= maxLength) {\n return filename\n }\n\n // Calculate how to split the truncation\n const halfLength = Math.floor((maxLength - 3) / 2) // -3 for '...'\n const start = nameWithoutExt.substring(0, halfLength)\n const end = nameWithoutExt.substring(nameWithoutExt.length - halfLength)\n\n return `${start}...${end}${extension}`\n}\n\n/**\n * Determines the media type from a filename's extension (singular form)\n * @param filename The filename to analyze\n * @returns The media type: 'image', 'video', 'audio', or '3D'\n */\nexport function getMediaTypeFromFilename(filename: string): MediaType {\n if (!filename) return 'image'\n const ext = filename.split('.').pop()?.toLowerCase()\n if (!ext) return 'image'\n\n // Type-safe array includes check using type assertion\n if (IMAGE_EXTENSIONS.includes(ext as ImageExtension)) return 'image'\n if (VIDEO_EXTENSIONS.includes(ext as VideoExtension)) return 'video'\n if (AUDIO_EXTENSIONS.includes(ext as AudioExtension)) return 'audio'\n if (THREE_D_EXTENSIONS.includes(ext as ThreeDExtension)) return '3D'\n\n return 'image'\n}\n","import { defineStore } from 'pinia'\nimport { computed, markRaw, ref } from 'vue'\n\nimport type { ComfyExtension } from '@/types/comfy'\n\n/**\n * These extensions are always active, even if they are disabled in the setting.\n */\nconst ALWAYS_ENABLED_EXTENSIONS: readonly string[] = []\n\nconst ALWAYS_DISABLED_EXTENSIONS: readonly string[] = [\n // pysssss.Locking is replaced by pin/unpin in ComfyUI core.\n // https://github.com/Comfy-Org/litegraph.js/pull/117\n 'pysssss.Locking',\n // pysssss.SnapToGrid is replaced by Comfy.Graph.AlwaysSnapToGrid in ComfyUI core.\n // pysssss.SnapToGrid tries to write global app.shiftDown state, which is no longer\n // allowed since v1.3.12.\n // https://github.com/Comfy-Org/ComfyUI_frontend/issues/1176\n 'pysssss.SnapToGrid',\n // Favicon status is implemented in ComfyUI core in v1.20.\n // https://github.com/Comfy-Org/ComfyUI_frontend/pull/3880\n 'pysssss.FaviconStatus',\n 'KJNodes.browserstatus'\n]\n\nexport const useExtensionStore = defineStore('extension', () => {\n // For legacy reasons, the name uniquely identifies an extension\n const extensionByName = ref<Record<string, ComfyExtension>>({})\n const extensions = computed(() => Object.values(extensionByName.value))\n // Not using computed because disable extension requires reloading of the page.\n // Dynamically update this list won't affect extensions that are already loaded.\n const disabledExtensionNames = ref<Set<string>>(new Set())\n\n // Disabled extension names that are currently not in the extension list.\n // If a node pack is disabled in the backend, we shouldn't remove the configuration\n // of the frontend extension disable list, in case the node pack is re-enabled.\n const inactiveDisabledExtensionNames = computed(() => {\n return Array.from(disabledExtensionNames.value).filter(\n (name) => !(name in extensionByName.value)\n )\n })\n\n const isExtensionInstalled = (name: string) => name in extensionByName.value\n\n const isExtensionEnabled = (name: string) =>\n !disabledExtensionNames.value.has(name)\n const enabledExtensions = computed(() => {\n return extensions.value.filter((ext) => isExtensionEnabled(ext.name))\n })\n\n function isExtensionReadOnly(name: string) {\n return (\n ALWAYS_DISABLED_EXTENSIONS.includes(name) ||\n ALWAYS_ENABLED_EXTENSIONS.includes(name)\n )\n }\n\n function registerExtension(extension: ComfyExtension) {\n if (!extension.name) {\n throw new Error(\"Extensions must have a 'name' property.\")\n }\n\n if (extensionByName.value[extension.name]) {\n throw new Error(`Extension named '${extension.name}' already registered.`)\n }\n\n if (disabledExtensionNames.value.has(extension.name)) {\n console.warn(`Extension ${extension.name} is disabled.`)\n }\n\n extensionByName.value[extension.name] = markRaw(extension)\n }\n\n function loadDisabledExtensionNames(names: string[]) {\n disabledExtensionNames.value = new Set(names)\n for (const name of ALWAYS_DISABLED_EXTENSIONS) {\n disabledExtensionNames.value.add(name)\n }\n for (const name of ALWAYS_ENABLED_EXTENSIONS) {\n disabledExtensionNames.value.delete(name)\n }\n }\n\n /**\n * Core extensions are extensions that are defined in the core package.\n * See /extensions/core/index.ts for the list.\n */\n const coreExtensionNames = ref<string[]>([])\n function captureCoreExtensions() {\n coreExtensionNames.value = extensions.value.map((ext) => ext.name)\n }\n\n function isCoreExtension(name: string) {\n return coreExtensionNames.value.includes(name)\n }\n\n const hasThirdPartyExtensions = computed(() => {\n return extensions.value.some((ext) => !isCoreExtension(ext.name))\n })\n\n return {\n extensions,\n enabledExtensions,\n inactiveDisabledExtensionNames,\n isExtensionInstalled,\n isExtensionEnabled,\n isExtensionReadOnly,\n registerExtension,\n loadDisabledExtensionNames,\n captureCoreExtensions,\n isCoreExtension,\n hasThirdPartyExtensions\n }\n})\n","<template>\n <TabPanel :value=\"props.value\" class=\"h-full w-full\" :class=\"props.class\">\n <div class=\"flex h-full w-full flex-col gap-2\">\n <slot name=\"header\" />\n <ScrollPanel class=\"h-0 grow pr-2\">\n <slot />\n </ScrollPanel>\n <slot name=\"footer\" />\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport ScrollPanel from 'primevue/scrollpanel'\nimport TabPanel from 'primevue/tabpanel'\n\nconst props = defineProps<{\n value: string\n class?: string\n}>()\n</script>\n","<template>\n <TabPanel :value=\"props.value\" class=\"h-full w-full\" :class=\"props.class\">\n <div class=\"flex h-full w-full flex-col gap-2\">\n <slot name=\"header\" />\n <ScrollPanel class=\"h-0 grow pr-2\">\n <slot />\n </ScrollPanel>\n <slot name=\"footer\" />\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport ScrollPanel from 'primevue/scrollpanel'\nimport TabPanel from 'primevue/tabpanel'\n\nconst props = defineProps<{\n value: string\n class?: string\n}>()\n</script>\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- var R=Object.defineProperty;var l=(h,f)=>R(h,"name",{value:f,configurable:!0});import{X as z,ut as V}from"./vendor-primevue-DcMRXJN3.js";import{$o as o,Ba as r,Do as s,Ha as C,Jo as n,Ka as x,Pa as y,Ua as u,Va as m,Ya as F,do as t,oo as E,po as _,qa as v,wo as T}from"./vendor-other-DlQF6V2E.js";import{m as H,n as J}from"./vendor-vue-D9IUuwPJ.js";import{It as K,zt as O}from"./api-CUAc7rDA.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import"./useErrorHandling-CI8_F4yx.js";import{t as S}from"./Button-Do2I1OAA.js";import{t as X}from"./PanelTemplate-BjN5XNg2.js";import{r as Y,sn as G,yr as M}from"./dialogService-BZ1FmjZL.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 Q}from"./serverConfigStore-60S1CDhK.js";var W={class:"flex flex-col gap-2"},Z={class:"flex justify-end gap-2"},ee={class:"flex items-center justify-between"},te=F({__name:"ServerConfigPanel",setup(h){const f=M(),k=Q(),$=K(),{serverConfigsByCategory:B,serverConfigValues:b,launchArgs:A,commandLineArgs:c,modifiedConfigs:g}=H(k);let w=!1;const I=l(()=>{k.revertChanges()},"revertChanges"),P=l(async()=>{w=!0,await O().restartApp()},"restartApp");T(A,async e=>{await f.set("Comfy.Server.LaunchArgs",e)}),T(b,async e=>{await f.set("Comfy.Server.ServerConfigValues",e)});const{copyToClipboard:L}=G(),N=l(async()=>{await L(c.value)},"copyCommandLineArgs"),{t:d}=J();E(()=>{w||g.value.length!==0&&$.add({severity:"warn",summary:d("serverConfig.restartRequiredToastSummary"),detail:d("serverConfig.restartRequiredToastDetail"),life:1e4})});const U=l(e=>({...e,name:d(`serverConfigItems.${e.id}.name`,e.name),tooltip:e.tooltip?d(`serverConfigItems.${e.id}.tooltip`,e.tooltip):void 0}),"translateItem");return(e,p)=>(t(),m(X,{value:"Server-Config",class:"server-config-panel"},{header:s(()=>[r("div",W,[n(g).length>0?(t(),m(n(V),{key:0,severity:"info","pt:text":"w-full"},{default:s(()=>[r("p",null,o(e.$t("serverConfig.modifiedConfigs")),1),r("ul",null,[(t(!0),u(y,null,_(n(g),a=>(t(),u("li",{key:a.id},o(a.name)+": "+o(a.initialValue)+" → "+o(a.value),1))),128))]),r("div",Z,[v(S,{variant:"secondary",onClick:I},{default:s(()=>[x(o(e.$t("serverConfig.revertChanges")),1)]),_:1}),v(S,{variant:"destructive",onClick:P},{default:s(()=>[x(o(e.$t("serverConfig.restart")),1)]),_:1})])]),_:1})):C("",!0),n(c)?(t(),m(n(V),{key:1,severity:"secondary","pt:text":"w-full"},{icon:s(()=>p[0]||(p[0]=[r("i",{class:"icon-[lucide--terminal] text-xl font-bold"},null,-1)])),default:s(()=>[r("div",ee,[r("p",null,o(n(c)),1),v(S,{size:"icon",variant:"muted-textonly",onClick:N},{default:s(()=>p[1]||(p[1]=[r("i",{class:"pi pi-clipboard"},null,-1)])),_:1})])]),_:1})):C("",!0)])]),default:s(()=>[(t(!0),u(y,null,_(Object.entries(n(B)),([a,j],q)=>(t(),u("div",{key:a},[q>0?(t(),m(n(z),{key:0})):C("",!0),r("h3",null,o(e.$t(`serverConfigCategories.${a}`,a)),1),(t(!0),u(y,null,_(j,i=>(t(),u("div",{key:i.name,class:"mb-4"},[v(Y,{id:i.id,"form-value":i.value,"onUpdate:formValue":l(D=>i.value=D,"onUpdate:formValue"),item:U(i),"label-class":{"text-highlight":i.initialValue!==i.value}},null,8,["id","form-value","onUpdate:formValue","item","label-class"])]))),128))]))),128))]),_:1}))}}),_e=te;export{_e as default};
1
+ var R=Object.defineProperty;var l=(h,f)=>R(h,"name",{value:f,configurable:!0});import{X as z,ut as V}from"./vendor-primevue-DcMRXJN3.js";import{$o as o,Ba as r,Do as s,Ha as C,Jo as n,Ka as x,Pa as y,Ua as u,Va as m,Ya as F,do as t,oo as E,po as _,qa as v,wo as T}from"./vendor-other-DlQF6V2E.js";import{m as H,n as J}from"./vendor-vue-D9IUuwPJ.js";import{It as K,zt as O}from"./api-Dwq2LQIW.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import"./useErrorHandling-Cfa5N_7c.js";import{t as S}from"./Button-Do2I1OAA.js";import{t as X}from"./PanelTemplate-BJda9e5J.js";import{r as Y,sn as G,yr as M}from"./dialogService-YG0RH337.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 Q}from"./serverConfigStore-60S1CDhK.js";var W={class:"flex flex-col gap-2"},Z={class:"flex justify-end gap-2"},ee={class:"flex items-center justify-between"},te=F({__name:"ServerConfigPanel",setup(h){const f=M(),k=Q(),$=K(),{serverConfigsByCategory:B,serverConfigValues:b,launchArgs:A,commandLineArgs:c,modifiedConfigs:g}=H(k);let w=!1;const I=l(()=>{k.revertChanges()},"revertChanges"),P=l(async()=>{w=!0,await O().restartApp()},"restartApp");T(A,async e=>{await f.set("Comfy.Server.LaunchArgs",e)}),T(b,async e=>{await f.set("Comfy.Server.ServerConfigValues",e)});const{copyToClipboard:L}=G(),N=l(async()=>{await L(c.value)},"copyCommandLineArgs"),{t:d}=J();E(()=>{w||g.value.length!==0&&$.add({severity:"warn",summary:d("serverConfig.restartRequiredToastSummary"),detail:d("serverConfig.restartRequiredToastDetail"),life:1e4})});const U=l(e=>({...e,name:d(`serverConfigItems.${e.id}.name`,e.name),tooltip:e.tooltip?d(`serverConfigItems.${e.id}.tooltip`,e.tooltip):void 0}),"translateItem");return(e,p)=>(t(),m(X,{value:"Server-Config",class:"server-config-panel"},{header:s(()=>[r("div",W,[n(g).length>0?(t(),m(n(V),{key:0,severity:"info","pt:text":"w-full"},{default:s(()=>[r("p",null,o(e.$t("serverConfig.modifiedConfigs")),1),r("ul",null,[(t(!0),u(y,null,_(n(g),a=>(t(),u("li",{key:a.id},o(a.name)+": "+o(a.initialValue)+" → "+o(a.value),1))),128))]),r("div",Z,[v(S,{variant:"secondary",onClick:I},{default:s(()=>[x(o(e.$t("serverConfig.revertChanges")),1)]),_:1}),v(S,{variant:"destructive",onClick:P},{default:s(()=>[x(o(e.$t("serverConfig.restart")),1)]),_:1})])]),_:1})):C("",!0),n(c)?(t(),m(n(V),{key:1,severity:"secondary","pt:text":"w-full"},{icon:s(()=>p[0]||(p[0]=[r("i",{class:"icon-[lucide--terminal] text-xl font-bold"},null,-1)])),default:s(()=>[r("div",ee,[r("p",null,o(n(c)),1),v(S,{size:"icon",variant:"muted-textonly",onClick:N},{default:s(()=>p[1]||(p[1]=[r("i",{class:"pi pi-clipboard"},null,-1)])),_:1})])]),_:1})):C("",!0)])]),default:s(()=>[(t(!0),u(y,null,_(Object.entries(n(B)),([a,j],q)=>(t(),u("div",{key:a},[q>0?(t(),m(n(z),{key:0})):C("",!0),r("h3",null,o(e.$t(`serverConfigCategories.${a}`,a)),1),(t(!0),u(y,null,_(j,i=>(t(),u("div",{key:i.name,class:"mb-4"},[v(Y,{id:i.id,"form-value":i.value,"onUpdate:formValue":l(D=>i.value=D,"onUpdate:formValue"),item:U(i),"label-class":{"text-highlight":i.initialValue!==i.value}},null,8,["id","form-value","onUpdate:formValue","item","label-class"])]))),128))]))),128))]),_:1}))}}),_e=te;export{_e as default};
2
2
 
3
- //# sourceMappingURL=ServerConfigPanel-CxovH9Qk.js.map
3
+ //# sourceMappingURL=ServerConfigPanel-VsC6xlZJ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ServerConfigPanel-CxovH9Qk.js","names":[],"sources":["../../src/platform/settings/components/ServerConfigPanel.vue","../../src/platform/settings/components/ServerConfigPanel.vue"],"sourcesContent":["<template>\n <PanelTemplate value=\"Server-Config\" class=\"server-config-panel\">\n <template #header>\n <div class=\"flex flex-col gap-2\">\n <Message\n v-if=\"modifiedConfigs.length > 0\"\n severity=\"info\"\n pt:text=\"w-full\"\n >\n <p>\n {{ $t('serverConfig.modifiedConfigs') }}\n </p>\n <ul>\n <li v-for=\"config in modifiedConfigs\" :key=\"config.id\">\n {{ config.name }}: {{ config.initialValue }} → {{ config.value }}\n </li>\n </ul>\n <div class=\"flex justify-end gap-2\">\n <Button variant=\"secondary\" @click=\"revertChanges\">\n {{ $t('serverConfig.revertChanges') }}\n </Button>\n <Button variant=\"destructive\" @click=\"restartApp\">\n {{ $t('serverConfig.restart') }}\n </Button>\n </div>\n </Message>\n <Message v-if=\"commandLineArgs\" severity=\"secondary\" pt:text=\"w-full\">\n <template #icon>\n <i class=\"icon-[lucide--terminal] text-xl font-bold\" />\n </template>\n <div class=\"flex items-center justify-between\">\n <p>{{ commandLineArgs }}</p>\n <Button\n size=\"icon\"\n variant=\"muted-textonly\"\n @click=\"copyCommandLineArgs\"\n >\n <i class=\"pi pi-clipboard\" />\n </Button>\n </div>\n </Message>\n </div>\n </template>\n <div\n v-for=\"([label, items], i) in Object.entries(serverConfigsByCategory)\"\n :key=\"label\"\n >\n <Divider v-if=\"i > 0\" />\n <h3>{{ $t(`serverConfigCategories.${label}`, label) }}</h3>\n <div v-for=\"item in items\" :key=\"item.name\" class=\"mb-4\">\n <FormItem\n :id=\"item.id\"\n v-model:form-value=\"item.value\"\n :item=\"translateItem(item)\"\n :label-class=\"{\n 'text-highlight': item.initialValue !== item.value\n }\"\n />\n </div>\n </div>\n </PanelTemplate>\n</template>\n\n<script setup lang=\"ts\">\nimport { storeToRefs } from 'pinia'\nimport Divider from 'primevue/divider'\nimport Message from 'primevue/message'\nimport { onBeforeUnmount, watch } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport FormItem from '@/components/common/FormItem.vue'\nimport PanelTemplate from '@/components/dialog/content/setting/PanelTemplate.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useCopyToClipboard } from '@/composables/useCopyToClipboard'\nimport type { ServerConfig } from '@/constants/serverConfig'\nimport { useSettingStore } from '@/platform/settings/settingStore'\nimport type { FormItem as FormItemType } from '@/platform/settings/types'\nimport { useToastStore } from '@/platform/updates/common/toastStore'\nimport { useServerConfigStore } from '@/stores/serverConfigStore'\nimport { electronAPI } from '@/utils/envUtil'\n\nconst settingStore = useSettingStore()\nconst serverConfigStore = useServerConfigStore()\nconst toastStore = useToastStore()\nconst {\n serverConfigsByCategory,\n serverConfigValues,\n launchArgs,\n commandLineArgs,\n modifiedConfigs\n} = storeToRefs(serverConfigStore)\n\nlet restartTriggered = false\n\nconst revertChanges = () => {\n serverConfigStore.revertChanges()\n}\n\nconst restartApp = async () => {\n restartTriggered = true\n await electronAPI().restartApp()\n}\n\nwatch(launchArgs, async (newVal) => {\n await settingStore.set('Comfy.Server.LaunchArgs', newVal)\n})\n\nwatch(serverConfigValues, async (newVal) => {\n await settingStore.set('Comfy.Server.ServerConfigValues', newVal)\n})\n\nconst { copyToClipboard } = useCopyToClipboard()\nconst copyCommandLineArgs = async () => {\n await copyToClipboard(commandLineArgs.value)\n}\n\nconst { t } = useI18n()\n\nonBeforeUnmount(() => {\n if (restartTriggered) {\n return\n }\n\n if (modifiedConfigs.value.length === 0) {\n return\n }\n\n toastStore.add({\n severity: 'warn',\n summary: t('serverConfig.restartRequiredToastSummary'),\n detail: t('serverConfig.restartRequiredToastDetail'),\n life: 10_000\n })\n})\n\nconst translateItem = (item: ServerConfig<any>): FormItemType => {\n return {\n ...item,\n name: t(`serverConfigItems.${item.id}.name`, item.name),\n tooltip: item.tooltip\n ? t(`serverConfigItems.${item.id}.tooltip`, item.tooltip)\n : undefined\n }\n}\n</script>\n","<template>\n <PanelTemplate value=\"Server-Config\" class=\"server-config-panel\">\n <template #header>\n <div class=\"flex flex-col gap-2\">\n <Message\n v-if=\"modifiedConfigs.length > 0\"\n severity=\"info\"\n pt:text=\"w-full\"\n >\n <p>\n {{ $t('serverConfig.modifiedConfigs') }}\n </p>\n <ul>\n <li v-for=\"config in modifiedConfigs\" :key=\"config.id\">\n {{ config.name }}: {{ config.initialValue }} → {{ config.value }}\n </li>\n </ul>\n <div class=\"flex justify-end gap-2\">\n <Button variant=\"secondary\" @click=\"revertChanges\">\n {{ $t('serverConfig.revertChanges') }}\n </Button>\n <Button variant=\"destructive\" @click=\"restartApp\">\n {{ $t('serverConfig.restart') }}\n </Button>\n </div>\n </Message>\n <Message v-if=\"commandLineArgs\" severity=\"secondary\" pt:text=\"w-full\">\n <template #icon>\n <i class=\"icon-[lucide--terminal] text-xl font-bold\" />\n </template>\n <div class=\"flex items-center justify-between\">\n <p>{{ commandLineArgs }}</p>\n <Button\n size=\"icon\"\n variant=\"muted-textonly\"\n @click=\"copyCommandLineArgs\"\n >\n <i class=\"pi pi-clipboard\" />\n </Button>\n </div>\n </Message>\n </div>\n </template>\n <div\n v-for=\"([label, items], i) in Object.entries(serverConfigsByCategory)\"\n :key=\"label\"\n >\n <Divider v-if=\"i > 0\" />\n <h3>{{ $t(`serverConfigCategories.${label}`, label) }}</h3>\n <div v-for=\"item in items\" :key=\"item.name\" class=\"mb-4\">\n <FormItem\n :id=\"item.id\"\n v-model:form-value=\"item.value\"\n :item=\"translateItem(item)\"\n :label-class=\"{\n 'text-highlight': item.initialValue !== item.value\n }\"\n />\n </div>\n </div>\n </PanelTemplate>\n</template>\n\n<script setup lang=\"ts\">\nimport { storeToRefs } from 'pinia'\nimport Divider from 'primevue/divider'\nimport Message from 'primevue/message'\nimport { onBeforeUnmount, watch } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport FormItem from '@/components/common/FormItem.vue'\nimport PanelTemplate from '@/components/dialog/content/setting/PanelTemplate.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useCopyToClipboard } from '@/composables/useCopyToClipboard'\nimport type { ServerConfig } from '@/constants/serverConfig'\nimport { useSettingStore } from '@/platform/settings/settingStore'\nimport type { FormItem as FormItemType } from '@/platform/settings/types'\nimport { useToastStore } from '@/platform/updates/common/toastStore'\nimport { useServerConfigStore } from '@/stores/serverConfigStore'\nimport { electronAPI } from '@/utils/envUtil'\n\nconst settingStore = useSettingStore()\nconst serverConfigStore = useServerConfigStore()\nconst toastStore = useToastStore()\nconst {\n serverConfigsByCategory,\n serverConfigValues,\n launchArgs,\n commandLineArgs,\n modifiedConfigs\n} = storeToRefs(serverConfigStore)\n\nlet restartTriggered = false\n\nconst revertChanges = () => {\n serverConfigStore.revertChanges()\n}\n\nconst restartApp = async () => {\n restartTriggered = true\n await electronAPI().restartApp()\n}\n\nwatch(launchArgs, async (newVal) => {\n await settingStore.set('Comfy.Server.LaunchArgs', newVal)\n})\n\nwatch(serverConfigValues, async (newVal) => {\n await settingStore.set('Comfy.Server.ServerConfigValues', newVal)\n})\n\nconst { copyToClipboard } = useCopyToClipboard()\nconst copyCommandLineArgs = async () => {\n await copyToClipboard(commandLineArgs.value)\n}\n\nconst { t } = useI18n()\n\nonBeforeUnmount(() => {\n if (restartTriggered) {\n return\n }\n\n if (modifiedConfigs.value.length === 0) {\n return\n }\n\n toastStore.add({\n severity: 'warn',\n summary: t('serverConfig.restartRequiredToastSummary'),\n detail: t('serverConfig.restartRequiredToastDetail'),\n life: 10_000\n })\n})\n\nconst translateItem = (item: ServerConfig<any>): FormItemType => {\n return {\n ...item,\n name: t(`serverConfigItems.${item.id}.name`, item.name),\n tooltip: item.tooltip\n ? t(`serverConfigItems.${item.id}.tooltip`, item.tooltip)\n : undefined\n }\n}\n</script>\n"],"mappings":"whCCiFA,MAAM,EAAe,EAAA,EACf,EAAoB,EAAA,EACpB,EAAa,EAAA,EACb,CACJ,wBAAA,EACA,mBAAA,EACA,WAAA,EACA,gBAAA,EACA,gBAAA,CAAA,EACE,EAAY,CAAA,EAEhB,IAAI,EAAmB,GAEvB,MAAM,EAAA,EAAA,IAAsB,CAC1B,EAAkB,cAAA,GADd,iBAIA,EAAa,EAAA,SAAY,CAC7B,EAAmB,GACnB,MAAM,EAAA,EAAc,WAAA,GAFH,cAKnB,EAAM,EAAY,MAAO,GAAW,CAClC,MAAM,EAAa,IAAI,0BAA2B,CAAA,IAGpD,EAAM,EAAoB,MAAO,GAAW,CAC1C,MAAM,EAAa,IAAI,kCAAmC,CAAA,IAG5D,KAAM,CAAE,gBAAA,CAAA,EAAoB,EAAA,EACtB,EAAsB,EAAA,SAAY,CACtC,MAAM,EAAgB,EAAgB,KAAA,GADZ,uBAItB,CAAE,EAAA,CAAA,EAAM,EAAA,EAEd,EAAA,IAAsB,CAChB,GAIA,EAAgB,MAAM,SAAW,GAIrC,EAAW,IAAI,CACb,SAAU,OACV,QAAS,EAAE,0CAAA,EACX,OAAQ,EAAE,yCAAA,EACV,KAAM,IACP,IAGH,MAAM,EAAA,EAAiB,IACd,CACL,GAAG,EACH,KAAM,EAAE,qBAAqB,EAAK,EAAA,QAAW,EAAK,IAAA,EAClD,QAAS,EAAK,QACV,EAAE,qBAAqB,EAAK,EAAA,WAAc,EAAK,OAAA,EAC/C,SANF"}
1
+ {"version":3,"file":"ServerConfigPanel-VsC6xlZJ.js","names":[],"sources":["../../src/platform/settings/components/ServerConfigPanel.vue","../../src/platform/settings/components/ServerConfigPanel.vue"],"sourcesContent":["<template>\n <PanelTemplate value=\"Server-Config\" class=\"server-config-panel\">\n <template #header>\n <div class=\"flex flex-col gap-2\">\n <Message\n v-if=\"modifiedConfigs.length > 0\"\n severity=\"info\"\n pt:text=\"w-full\"\n >\n <p>\n {{ $t('serverConfig.modifiedConfigs') }}\n </p>\n <ul>\n <li v-for=\"config in modifiedConfigs\" :key=\"config.id\">\n {{ config.name }}: {{ config.initialValue }} → {{ config.value }}\n </li>\n </ul>\n <div class=\"flex justify-end gap-2\">\n <Button variant=\"secondary\" @click=\"revertChanges\">\n {{ $t('serverConfig.revertChanges') }}\n </Button>\n <Button variant=\"destructive\" @click=\"restartApp\">\n {{ $t('serverConfig.restart') }}\n </Button>\n </div>\n </Message>\n <Message v-if=\"commandLineArgs\" severity=\"secondary\" pt:text=\"w-full\">\n <template #icon>\n <i class=\"icon-[lucide--terminal] text-xl font-bold\" />\n </template>\n <div class=\"flex items-center justify-between\">\n <p>{{ commandLineArgs }}</p>\n <Button\n size=\"icon\"\n variant=\"muted-textonly\"\n @click=\"copyCommandLineArgs\"\n >\n <i class=\"pi pi-clipboard\" />\n </Button>\n </div>\n </Message>\n </div>\n </template>\n <div\n v-for=\"([label, items], i) in Object.entries(serverConfigsByCategory)\"\n :key=\"label\"\n >\n <Divider v-if=\"i > 0\" />\n <h3>{{ $t(`serverConfigCategories.${label}`, label) }}</h3>\n <div v-for=\"item in items\" :key=\"item.name\" class=\"mb-4\">\n <FormItem\n :id=\"item.id\"\n v-model:form-value=\"item.value\"\n :item=\"translateItem(item)\"\n :label-class=\"{\n 'text-highlight': item.initialValue !== item.value\n }\"\n />\n </div>\n </div>\n </PanelTemplate>\n</template>\n\n<script setup lang=\"ts\">\nimport { storeToRefs } from 'pinia'\nimport Divider from 'primevue/divider'\nimport Message from 'primevue/message'\nimport { onBeforeUnmount, watch } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport FormItem from '@/components/common/FormItem.vue'\nimport PanelTemplate from '@/components/dialog/content/setting/PanelTemplate.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useCopyToClipboard } from '@/composables/useCopyToClipboard'\nimport type { ServerConfig } from '@/constants/serverConfig'\nimport { useSettingStore } from '@/platform/settings/settingStore'\nimport type { FormItem as FormItemType } from '@/platform/settings/types'\nimport { useToastStore } from '@/platform/updates/common/toastStore'\nimport { useServerConfigStore } from '@/stores/serverConfigStore'\nimport { electronAPI } from '@/utils/envUtil'\n\nconst settingStore = useSettingStore()\nconst serverConfigStore = useServerConfigStore()\nconst toastStore = useToastStore()\nconst {\n serverConfigsByCategory,\n serverConfigValues,\n launchArgs,\n commandLineArgs,\n modifiedConfigs\n} = storeToRefs(serverConfigStore)\n\nlet restartTriggered = false\n\nconst revertChanges = () => {\n serverConfigStore.revertChanges()\n}\n\nconst restartApp = async () => {\n restartTriggered = true\n await electronAPI().restartApp()\n}\n\nwatch(launchArgs, async (newVal) => {\n await settingStore.set('Comfy.Server.LaunchArgs', newVal)\n})\n\nwatch(serverConfigValues, async (newVal) => {\n await settingStore.set('Comfy.Server.ServerConfigValues', newVal)\n})\n\nconst { copyToClipboard } = useCopyToClipboard()\nconst copyCommandLineArgs = async () => {\n await copyToClipboard(commandLineArgs.value)\n}\n\nconst { t } = useI18n()\n\nonBeforeUnmount(() => {\n if (restartTriggered) {\n return\n }\n\n if (modifiedConfigs.value.length === 0) {\n return\n }\n\n toastStore.add({\n severity: 'warn',\n summary: t('serverConfig.restartRequiredToastSummary'),\n detail: t('serverConfig.restartRequiredToastDetail'),\n life: 10_000\n })\n})\n\nconst translateItem = (item: ServerConfig<any>): FormItemType => {\n return {\n ...item,\n name: t(`serverConfigItems.${item.id}.name`, item.name),\n tooltip: item.tooltip\n ? t(`serverConfigItems.${item.id}.tooltip`, item.tooltip)\n : undefined\n }\n}\n</script>\n","<template>\n <PanelTemplate value=\"Server-Config\" class=\"server-config-panel\">\n <template #header>\n <div class=\"flex flex-col gap-2\">\n <Message\n v-if=\"modifiedConfigs.length > 0\"\n severity=\"info\"\n pt:text=\"w-full\"\n >\n <p>\n {{ $t('serverConfig.modifiedConfigs') }}\n </p>\n <ul>\n <li v-for=\"config in modifiedConfigs\" :key=\"config.id\">\n {{ config.name }}: {{ config.initialValue }} → {{ config.value }}\n </li>\n </ul>\n <div class=\"flex justify-end gap-2\">\n <Button variant=\"secondary\" @click=\"revertChanges\">\n {{ $t('serverConfig.revertChanges') }}\n </Button>\n <Button variant=\"destructive\" @click=\"restartApp\">\n {{ $t('serverConfig.restart') }}\n </Button>\n </div>\n </Message>\n <Message v-if=\"commandLineArgs\" severity=\"secondary\" pt:text=\"w-full\">\n <template #icon>\n <i class=\"icon-[lucide--terminal] text-xl font-bold\" />\n </template>\n <div class=\"flex items-center justify-between\">\n <p>{{ commandLineArgs }}</p>\n <Button\n size=\"icon\"\n variant=\"muted-textonly\"\n @click=\"copyCommandLineArgs\"\n >\n <i class=\"pi pi-clipboard\" />\n </Button>\n </div>\n </Message>\n </div>\n </template>\n <div\n v-for=\"([label, items], i) in Object.entries(serverConfigsByCategory)\"\n :key=\"label\"\n >\n <Divider v-if=\"i > 0\" />\n <h3>{{ $t(`serverConfigCategories.${label}`, label) }}</h3>\n <div v-for=\"item in items\" :key=\"item.name\" class=\"mb-4\">\n <FormItem\n :id=\"item.id\"\n v-model:form-value=\"item.value\"\n :item=\"translateItem(item)\"\n :label-class=\"{\n 'text-highlight': item.initialValue !== item.value\n }\"\n />\n </div>\n </div>\n </PanelTemplate>\n</template>\n\n<script setup lang=\"ts\">\nimport { storeToRefs } from 'pinia'\nimport Divider from 'primevue/divider'\nimport Message from 'primevue/message'\nimport { onBeforeUnmount, watch } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport FormItem from '@/components/common/FormItem.vue'\nimport PanelTemplate from '@/components/dialog/content/setting/PanelTemplate.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useCopyToClipboard } from '@/composables/useCopyToClipboard'\nimport type { ServerConfig } from '@/constants/serverConfig'\nimport { useSettingStore } from '@/platform/settings/settingStore'\nimport type { FormItem as FormItemType } from '@/platform/settings/types'\nimport { useToastStore } from '@/platform/updates/common/toastStore'\nimport { useServerConfigStore } from '@/stores/serverConfigStore'\nimport { electronAPI } from '@/utils/envUtil'\n\nconst settingStore = useSettingStore()\nconst serverConfigStore = useServerConfigStore()\nconst toastStore = useToastStore()\nconst {\n serverConfigsByCategory,\n serverConfigValues,\n launchArgs,\n commandLineArgs,\n modifiedConfigs\n} = storeToRefs(serverConfigStore)\n\nlet restartTriggered = false\n\nconst revertChanges = () => {\n serverConfigStore.revertChanges()\n}\n\nconst restartApp = async () => {\n restartTriggered = true\n await electronAPI().restartApp()\n}\n\nwatch(launchArgs, async (newVal) => {\n await settingStore.set('Comfy.Server.LaunchArgs', newVal)\n})\n\nwatch(serverConfigValues, async (newVal) => {\n await settingStore.set('Comfy.Server.ServerConfigValues', newVal)\n})\n\nconst { copyToClipboard } = useCopyToClipboard()\nconst copyCommandLineArgs = async () => {\n await copyToClipboard(commandLineArgs.value)\n}\n\nconst { t } = useI18n()\n\nonBeforeUnmount(() => {\n if (restartTriggered) {\n return\n }\n\n if (modifiedConfigs.value.length === 0) {\n return\n }\n\n toastStore.add({\n severity: 'warn',\n summary: t('serverConfig.restartRequiredToastSummary'),\n detail: t('serverConfig.restartRequiredToastDetail'),\n life: 10_000\n })\n})\n\nconst translateItem = (item: ServerConfig<any>): FormItemType => {\n return {\n ...item,\n name: t(`serverConfigItems.${item.id}.name`, item.name),\n tooltip: item.tooltip\n ? t(`serverConfigItems.${item.id}.tooltip`, item.tooltip)\n : undefined\n }\n}\n</script>\n"],"mappings":"whCCiFA,MAAM,EAAe,EAAA,EACf,EAAoB,EAAA,EACpB,EAAa,EAAA,EACb,CACJ,wBAAA,EACA,mBAAA,EACA,WAAA,EACA,gBAAA,EACA,gBAAA,CAAA,EACE,EAAY,CAAA,EAEhB,IAAI,EAAmB,GAEvB,MAAM,EAAA,EAAA,IAAsB,CAC1B,EAAkB,cAAA,GADd,iBAIA,EAAa,EAAA,SAAY,CAC7B,EAAmB,GACnB,MAAM,EAAA,EAAc,WAAA,GAFH,cAKnB,EAAM,EAAY,MAAO,GAAW,CAClC,MAAM,EAAa,IAAI,0BAA2B,CAAA,IAGpD,EAAM,EAAoB,MAAO,GAAW,CAC1C,MAAM,EAAa,IAAI,kCAAmC,CAAA,IAG5D,KAAM,CAAE,gBAAA,CAAA,EAAoB,EAAA,EACtB,EAAsB,EAAA,SAAY,CACtC,MAAM,EAAgB,EAAgB,KAAA,GADZ,uBAItB,CAAE,EAAA,CAAA,EAAM,EAAA,EAEd,EAAA,IAAsB,CAChB,GAIA,EAAgB,MAAM,SAAW,GAIrC,EAAW,IAAI,CACb,SAAU,OACV,QAAS,EAAE,0CAAA,EACX,OAAQ,EAAE,yCAAA,EACV,KAAM,IACP,IAGH,MAAM,EAAA,EAAiB,IACd,CACL,GAAG,EACH,KAAM,EAAE,qBAAqB,EAAK,EAAA,QAAW,EAAK,IAAA,EAClD,QAAS,EAAK,QACV,EAAE,qBAAqB,EAAK,EAAA,WAAc,EAAK,OAAA,EAC/C,SANF"}
@@ -1,3 +1,3 @@
1
- var I=Object.defineProperty;var g=(x,n)=>I(x,"name",{value:n,configurable:!0});import{et as T}from"./vendor-primevue-DcMRXJN3.js";import{$o as l,Ba as y,Do as z,Ha as u,Jo as P,Ka as E,Oo as U,Qo as S,Ua as t,Va as q,Xo as s,Ya as V,do as a,go as R,oo as X,qa as L,wo as $,za as d,zo as B}from"./vendor-other-DlQF6V2E.js";import{Lt as M}from"./api-CUAc7rDA.js";import{t as D}from"./src-DTrob8OL.js";import{t as j}from"./Button-Do2I1OAA.js";import{Nr as H,ft as N}from"./dialogService-BZ1FmjZL.js";var J={class:"flex max-w-xs min-w-40 flex-col gap-2 p-3"},K={class:"text-sm font-inter"},Q={key:1,class:"text-xs"},Y={class:"flex max-w-xs min-w-40 flex-col gap-2 p-3"},F={class:"text-sm font-inter"},G={key:1,class:"text-xs"},O="cursor-pointer transition-opacity hover:opacity-80",W=V({__name:"TopbarBadge",props:{badge:{},displayMode:{default:"full"},reverseOrder:{type:Boolean,default:!1},noPadding:{type:Boolean,default:!1},backgroundColor:{default:"var(--comfy-menu-bg)"}},setup(x){const n=x,v=B(),h=g(e=>{v.value?.toggle(e)},"togglePopover"),c=d(()=>n.badge.variant??"info"),m=d(()=>({backgroundColor:n.backgroundColor})),b=d(()=>{switch(c.value){case"error":return"bg-danger-100 text-white";case"warning":return"bg-gold-600 text-black";case"info":default:return"bg-white text-black"}}),w=d(()=>{switch(c.value){case"error":return"text-danger-100";case"warning":return"text-warning-background";case"info":default:return"text-text-primary"}}),i=d(()=>w.value),o=d(()=>{if(n.badge.icon)return n.badge.icon;switch(c.value){case"error":return"pi pi-exclamation-circle";case"warning":return"icon-[lucide--triangle-alert]";case"info":default:return}}),f=d(()=>{switch(c.value){case"error":return"bg-danger-100";case"warning":return"bg-gold-600";case"info":default:return"bg-slate-100"}}),p=d(()=>({root:{class:D("absolute z-50")},content:{class:D("mt-1 rounded-lg","bg-base-background","text-base-foreground","shadow-lg","border border-border-default")}}));return(e,_)=>{const C=R("tooltip");return e.displayMode==="icon-only"?(a(),t("div",{key:0,class:s(["relative inline-flex h-full shrink-0 items-center justify-center px-2",O]),style:S(m.value),onClick:h},[o.value?(a(),t("i",{key:0,class:s(["shrink-0 text-base",o.value,i.value])},null,2)):e.badge.label?(a(),t("div",{key:1,class:s(["shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):(a(),t("div",{key:2,class:s(["size-2 shrink-0 rounded-full",f.value])},null,2)),L(P(T),{ref_key:"popover",ref:v,"append-to":"body","auto-z-index":!0,"base-z-index":1e3,dismissable:!0,"close-on-escape":!0,unstyled:"",pt:p.value},{default:z(()=>[y("div",J,[e.badge.label?(a(),t("div",{key:0,class:s(["w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):u("",!0),y("div",K,l(e.badge.text),1),e.badge.tooltip?(a(),t("div",Q,l(e.badge.tooltip),1)):u("",!0)])]),_:1},8,["pt"])],4)):e.displayMode==="compact"?(a(),t("div",{key:1,class:"relative inline-flex h-full",style:S(m.value)},[y("div",{class:s(["flex h-full shrink-0 items-center gap-2 whitespace-nowrap",[{"flex-row-reverse":e.reverseOrder},e.noPadding?"":"px-3",O]]),onClick:h},[o.value?(a(),t("i",{key:0,class:s(["shrink-0 text-base",o.value,i.value])},null,2)):u("",!0),e.badge.label?(a(),t("div",{key:1,class:s(["shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):u("",!0)],2),L(P(T),{ref_key:"popover",ref:v,"append-to":"body","auto-z-index":!0,"base-z-index":1e3,dismissable:!0,"close-on-escape":!0,unstyled:"",pt:p.value},{default:z(()=>[y("div",Y,[e.badge.label?(a(),t("div",{key:0,class:s(["w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):u("",!0),y("div",F,l(e.badge.text),1),e.badge.tooltip?(a(),t("div",G,l(e.badge.tooltip),1)):u("",!0)])]),_:1},8,["pt"])],4)):U((a(),t("div",{key:2,class:s(["flex h-full shrink-0 items-center gap-2 whitespace-nowrap",[{"flex-row-reverse":e.reverseOrder},e.noPadding?"":"px-3"]]),style:S(m.value)},[o.value?(a(),t("i",{key:0,class:s(["shrink-0 text-base",o.value,i.value])},null,2)):u("",!0),e.badge.label?(a(),t("div",{key:1,class:s(["shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):u("",!0),y("div",{class:s(["font-inter text-sm",w.value])},l(e.badge.text),3)],6)),[[C,e.badge.tooltip]])}}}),ue=W,Z=3e3,ee=300*1e3,ae=V({__name:"SubscribeButton",props:{label:{},size:{default:"lg"},variant:{default:"default"},fluid:{type:Boolean,default:!0}},emits:["subscribed"],setup(x,{emit:n}){const v=n,{subscribe:h,isActiveSubscription:c,fetchStatus:m,showSubscriptionDialog:b}=H(),w=N(),i=B(!1),o=B(!1);let f=null;const p=B(!1),e=g(()=>{o.value=!0,i.value=!0;const r=Date.now(),k=g(async()=>{try{if(Date.now()-r>ee){_();return}await m(),c.value&&(_(),w?.trackMonthlySubscriptionSucceeded(),v("subscribed"))}catch(A){console.error("[SubscribeButton] Error polling subscription status:",A)}},"poll");k(),f=window.setInterval(k,Z)},"startPollingSubscriptionStatus"),_=g(()=>{f&&(clearInterval(f),f=null),o.value=!1,i.value=!1},"stopPolling");$([p,c],([r,k])=>{M&&r&&k&&(v("subscribed"),p.value=!1)});const C=g(async()=>{if(M){N()?.trackSubscription("subscribe_clicked"),p.value=!0,b();return}i.value=!0;try{await h(),e()}catch(r){console.error("[SubscribeButton] Error initiating subscription:",r),i.value=!1}},"handleSubscribe");return X(()=>{_(),p.value=!1}),(r,k)=>(a(),q(j,{size:r.size,loading:i.value,disabled:o.value,variant:"primary",style:S(r.variant==="gradient"?{background:"var(--color-subscription-button-gradient)",color:"var(--color-white)"}:void 0),class:s(P(D)("font-bold",r.fluid&&"w-full")),onClick:C},{default:z(()=>[E(l(r.label||r.$t("subscription.required.subscribe")),1)]),_:1},8,["size","loading","disabled","style","class"]))}}),de=ae;export{ue as n,de as t};
1
+ var I=Object.defineProperty;var g=(x,n)=>I(x,"name",{value:n,configurable:!0});import{et as T}from"./vendor-primevue-DcMRXJN3.js";import{$o as l,Ba as y,Do as z,Ha as u,Jo as P,Ka as E,Oo as U,Qo as S,Ua as t,Va as q,Xo as s,Ya as V,do as a,go as R,oo as X,qa as L,wo as $,za as d,zo as B}from"./vendor-other-DlQF6V2E.js";import{Lt as M}from"./api-Dwq2LQIW.js";import{t as D}from"./src-DTrob8OL.js";import{t as j}from"./Button-Do2I1OAA.js";import{Nr as H,ft as N}from"./dialogService-YG0RH337.js";var J={class:"flex max-w-xs min-w-40 flex-col gap-2 p-3"},K={class:"text-sm font-inter"},Q={key:1,class:"text-xs"},Y={class:"flex max-w-xs min-w-40 flex-col gap-2 p-3"},F={class:"text-sm font-inter"},G={key:1,class:"text-xs"},O="cursor-pointer transition-opacity hover:opacity-80",W=V({__name:"TopbarBadge",props:{badge:{},displayMode:{default:"full"},reverseOrder:{type:Boolean,default:!1},noPadding:{type:Boolean,default:!1},backgroundColor:{default:"var(--comfy-menu-bg)"}},setup(x){const n=x,v=B(),h=g(e=>{v.value?.toggle(e)},"togglePopover"),c=d(()=>n.badge.variant??"info"),m=d(()=>({backgroundColor:n.backgroundColor})),b=d(()=>{switch(c.value){case"error":return"bg-danger-100 text-white";case"warning":return"bg-gold-600 text-black";case"info":default:return"bg-white text-black"}}),w=d(()=>{switch(c.value){case"error":return"text-danger-100";case"warning":return"text-warning-background";case"info":default:return"text-text-primary"}}),i=d(()=>w.value),o=d(()=>{if(n.badge.icon)return n.badge.icon;switch(c.value){case"error":return"pi pi-exclamation-circle";case"warning":return"icon-[lucide--triangle-alert]";case"info":default:return}}),f=d(()=>{switch(c.value){case"error":return"bg-danger-100";case"warning":return"bg-gold-600";case"info":default:return"bg-slate-100"}}),p=d(()=>({root:{class:D("absolute z-50")},content:{class:D("mt-1 rounded-lg","bg-base-background","text-base-foreground","shadow-lg","border border-border-default")}}));return(e,_)=>{const C=R("tooltip");return e.displayMode==="icon-only"?(a(),t("div",{key:0,class:s(["relative inline-flex h-full shrink-0 items-center justify-center px-2",O]),style:S(m.value),onClick:h},[o.value?(a(),t("i",{key:0,class:s(["shrink-0 text-base",o.value,i.value])},null,2)):e.badge.label?(a(),t("div",{key:1,class:s(["shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):(a(),t("div",{key:2,class:s(["size-2 shrink-0 rounded-full",f.value])},null,2)),L(P(T),{ref_key:"popover",ref:v,"append-to":"body","auto-z-index":!0,"base-z-index":1e3,dismissable:!0,"close-on-escape":!0,unstyled:"",pt:p.value},{default:z(()=>[y("div",J,[e.badge.label?(a(),t("div",{key:0,class:s(["w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):u("",!0),y("div",K,l(e.badge.text),1),e.badge.tooltip?(a(),t("div",Q,l(e.badge.tooltip),1)):u("",!0)])]),_:1},8,["pt"])],4)):e.displayMode==="compact"?(a(),t("div",{key:1,class:"relative inline-flex h-full",style:S(m.value)},[y("div",{class:s(["flex h-full shrink-0 items-center gap-2 whitespace-nowrap",[{"flex-row-reverse":e.reverseOrder},e.noPadding?"":"px-3",O]]),onClick:h},[o.value?(a(),t("i",{key:0,class:s(["shrink-0 text-base",o.value,i.value])},null,2)):u("",!0),e.badge.label?(a(),t("div",{key:1,class:s(["shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):u("",!0)],2),L(P(T),{ref_key:"popover",ref:v,"append-to":"body","auto-z-index":!0,"base-z-index":1e3,dismissable:!0,"close-on-escape":!0,unstyled:"",pt:p.value},{default:z(()=>[y("div",Y,[e.badge.label?(a(),t("div",{key:0,class:s(["w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):u("",!0),y("div",F,l(e.badge.text),1),e.badge.tooltip?(a(),t("div",G,l(e.badge.tooltip),1)):u("",!0)])]),_:1},8,["pt"])],4)):U((a(),t("div",{key:2,class:s(["flex h-full shrink-0 items-center gap-2 whitespace-nowrap",[{"flex-row-reverse":e.reverseOrder},e.noPadding?"":"px-3"]]),style:S(m.value)},[o.value?(a(),t("i",{key:0,class:s(["shrink-0 text-base",o.value,i.value])},null,2)):u("",!0),e.badge.label?(a(),t("div",{key:1,class:s(["shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold",b.value])},l(e.badge.label),3)):u("",!0),y("div",{class:s(["font-inter text-sm",w.value])},l(e.badge.text),3)],6)),[[C,e.badge.tooltip]])}}}),ue=W,Z=3e3,ee=300*1e3,ae=V({__name:"SubscribeButton",props:{label:{},size:{default:"lg"},variant:{default:"default"},fluid:{type:Boolean,default:!0}},emits:["subscribed"],setup(x,{emit:n}){const v=n,{subscribe:h,isActiveSubscription:c,fetchStatus:m,showSubscriptionDialog:b}=H(),w=N(),i=B(!1),o=B(!1);let f=null;const p=B(!1),e=g(()=>{o.value=!0,i.value=!0;const r=Date.now(),k=g(async()=>{try{if(Date.now()-r>ee){_();return}await m(),c.value&&(_(),w?.trackMonthlySubscriptionSucceeded(),v("subscribed"))}catch(A){console.error("[SubscribeButton] Error polling subscription status:",A)}},"poll");k(),f=window.setInterval(k,Z)},"startPollingSubscriptionStatus"),_=g(()=>{f&&(clearInterval(f),f=null),o.value=!1,i.value=!1},"stopPolling");$([p,c],([r,k])=>{M&&r&&k&&(v("subscribed"),p.value=!1)});const C=g(async()=>{if(M){N()?.trackSubscription("subscribe_clicked"),p.value=!0,b();return}i.value=!0;try{await h(),e()}catch(r){console.error("[SubscribeButton] Error initiating subscription:",r),i.value=!1}},"handleSubscribe");return X(()=>{_(),p.value=!1}),(r,k)=>(a(),q(j,{size:r.size,loading:i.value,disabled:o.value,variant:"primary",style:S(r.variant==="gradient"?{background:"var(--color-subscription-button-gradient)",color:"var(--color-white)"}:void 0),class:s(P(D)("font-bold",r.fluid&&"w-full")),onClick:C},{default:z(()=>[E(l(r.label||r.$t("subscription.required.subscribe")),1)]),_:1},8,["size","loading","disabled","style","class"]))}}),de=ae;export{ue as n,de as t};
2
2
 
3
- //# sourceMappingURL=SubscribeButton-CTOQRkfg.js.map
3
+ //# sourceMappingURL=SubscribeButton-DZBycfCA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SubscribeButton-CTOQRkfg.js","names":[],"sources":["../../src/components/topbar/TopbarBadge.vue","../../src/components/topbar/TopbarBadge.vue","../../src/platform/cloud/subscription/components/SubscribeButton.vue","../../src/platform/cloud/subscription/components/SubscribeButton.vue"],"sourcesContent":["<template>\n <!-- Icon-only mode with Popover -->\n <div\n v-if=\"displayMode === 'icon-only'\"\n class=\"relative inline-flex h-full shrink-0 items-center justify-center px-2\"\n :class=\"clickableClasses\"\n :style=\"menuBackgroundStyle\"\n @click=\"togglePopover\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-else-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div v-else class=\"size-2 shrink-0 rounded-full\" :class=\"dotClasses\" />\n <Popover\n ref=\"popover\"\n append-to=\"body\"\n :auto-z-index=\"true\"\n :base-z-index=\"1000\"\n :dismissable=\"true\"\n :close-on-escape=\"true\"\n unstyled\n :pt=\"popoverPt\"\n >\n <div class=\"flex max-w-xs min-w-40 flex-col gap-2 p-3\">\n <div\n v-if=\"badge.label\"\n class=\"w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"text-sm font-inter\">{{ badge.text }}</div>\n <div v-if=\"badge.tooltip\" class=\"text-xs\">\n {{ badge.tooltip }}\n </div>\n </div>\n </Popover>\n </div>\n\n <!-- Compact mode: Icon + Label only with Popover -->\n <div\n v-else-if=\"displayMode === 'compact'\"\n class=\"relative inline-flex h-full\"\n :style=\"menuBackgroundStyle\"\n >\n <div\n class=\"flex h-full shrink-0 items-center gap-2 whitespace-nowrap\"\n :class=\"[\n { 'flex-row-reverse': reverseOrder },\n noPadding ? '' : 'px-3',\n clickableClasses\n ]\"\n @click=\"togglePopover\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n </div>\n <Popover\n ref=\"popover\"\n append-to=\"body\"\n :auto-z-index=\"true\"\n :base-z-index=\"1000\"\n :dismissable=\"true\"\n :close-on-escape=\"true\"\n unstyled\n :pt=\"popoverPt\"\n >\n <div class=\"flex max-w-xs min-w-40 flex-col gap-2 p-3\">\n <div\n v-if=\"badge.label\"\n class=\"w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"text-sm font-inter\">{{ badge.text }}</div>\n <div v-if=\"badge.tooltip\" class=\"text-xs\">\n {{ badge.tooltip }}\n </div>\n </div>\n </Popover>\n </div>\n\n <!-- Full mode: Icon + Label + Text -->\n <div\n v-else\n v-tooltip=\"badge.tooltip\"\n class=\"flex h-full shrink-0 items-center gap-2 whitespace-nowrap\"\n :class=\"[{ 'flex-row-reverse': reverseOrder }, noPadding ? '' : 'px-3']\"\n :style=\"menuBackgroundStyle\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"font-inter text-sm\" :class=\"textClasses\">\n {{ badge.text }}\n </div>\n </div>\n</template>\n<script setup lang=\"ts\">\nimport Popover from 'primevue/popover'\nimport { computed, ref } from 'vue'\n\nimport type { TopbarBadge } from '@/types/comfy'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst props = withDefaults(\n defineProps<{\n badge: TopbarBadge\n displayMode?: 'full' | 'compact' | 'icon-only'\n reverseOrder?: boolean\n noPadding?: boolean\n backgroundColor?: string\n }>(),\n {\n displayMode: 'full',\n reverseOrder: false,\n noPadding: false,\n backgroundColor: 'var(--comfy-menu-bg)'\n }\n)\n\nconst popover = ref<InstanceType<typeof Popover>>()\n\nconst togglePopover = (event: Event) => {\n popover.value?.toggle(event)\n}\n\nconst variant = computed(() => props.badge.variant ?? 'info')\n\nconst menuBackgroundStyle = computed(() => ({\n backgroundColor: props.backgroundColor\n}))\n\nconst labelClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'bg-danger-100 text-white'\n case 'warning':\n return 'bg-gold-600 text-black'\n case 'info':\n default:\n return 'bg-white text-black'\n }\n})\n\nconst textClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'text-danger-100'\n case 'warning':\n return 'text-warning-background'\n case 'info':\n default:\n return 'text-text-primary'\n }\n})\n\nconst iconColorClass = computed(() => textClasses.value)\n\nconst iconClass = computed(() => {\n if (props.badge.icon) {\n return props.badge.icon\n }\n switch (variant.value) {\n case 'error':\n return 'pi pi-exclamation-circle'\n case 'warning':\n return 'icon-[lucide--triangle-alert]'\n case 'info':\n default:\n return undefined\n }\n})\n\nconst clickableClasses = 'cursor-pointer transition-opacity hover:opacity-80'\n\nconst dotClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'bg-danger-100'\n case 'warning':\n return 'bg-gold-600'\n case 'info':\n default:\n return 'bg-slate-100'\n }\n})\n\nconst popoverPt = computed(() => ({\n root: {\n class: cn('absolute z-50')\n },\n content: {\n class: cn(\n 'mt-1 rounded-lg',\n 'bg-base-background',\n 'text-base-foreground',\n 'shadow-lg',\n 'border border-border-default'\n )\n }\n}))\n</script>\n","<template>\n <!-- Icon-only mode with Popover -->\n <div\n v-if=\"displayMode === 'icon-only'\"\n class=\"relative inline-flex h-full shrink-0 items-center justify-center px-2\"\n :class=\"clickableClasses\"\n :style=\"menuBackgroundStyle\"\n @click=\"togglePopover\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-else-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div v-else class=\"size-2 shrink-0 rounded-full\" :class=\"dotClasses\" />\n <Popover\n ref=\"popover\"\n append-to=\"body\"\n :auto-z-index=\"true\"\n :base-z-index=\"1000\"\n :dismissable=\"true\"\n :close-on-escape=\"true\"\n unstyled\n :pt=\"popoverPt\"\n >\n <div class=\"flex max-w-xs min-w-40 flex-col gap-2 p-3\">\n <div\n v-if=\"badge.label\"\n class=\"w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"text-sm font-inter\">{{ badge.text }}</div>\n <div v-if=\"badge.tooltip\" class=\"text-xs\">\n {{ badge.tooltip }}\n </div>\n </div>\n </Popover>\n </div>\n\n <!-- Compact mode: Icon + Label only with Popover -->\n <div\n v-else-if=\"displayMode === 'compact'\"\n class=\"relative inline-flex h-full\"\n :style=\"menuBackgroundStyle\"\n >\n <div\n class=\"flex h-full shrink-0 items-center gap-2 whitespace-nowrap\"\n :class=\"[\n { 'flex-row-reverse': reverseOrder },\n noPadding ? '' : 'px-3',\n clickableClasses\n ]\"\n @click=\"togglePopover\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n </div>\n <Popover\n ref=\"popover\"\n append-to=\"body\"\n :auto-z-index=\"true\"\n :base-z-index=\"1000\"\n :dismissable=\"true\"\n :close-on-escape=\"true\"\n unstyled\n :pt=\"popoverPt\"\n >\n <div class=\"flex max-w-xs min-w-40 flex-col gap-2 p-3\">\n <div\n v-if=\"badge.label\"\n class=\"w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"text-sm font-inter\">{{ badge.text }}</div>\n <div v-if=\"badge.tooltip\" class=\"text-xs\">\n {{ badge.tooltip }}\n </div>\n </div>\n </Popover>\n </div>\n\n <!-- Full mode: Icon + Label + Text -->\n <div\n v-else\n v-tooltip=\"badge.tooltip\"\n class=\"flex h-full shrink-0 items-center gap-2 whitespace-nowrap\"\n :class=\"[{ 'flex-row-reverse': reverseOrder }, noPadding ? '' : 'px-3']\"\n :style=\"menuBackgroundStyle\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"font-inter text-sm\" :class=\"textClasses\">\n {{ badge.text }}\n </div>\n </div>\n</template>\n<script setup lang=\"ts\">\nimport Popover from 'primevue/popover'\nimport { computed, ref } from 'vue'\n\nimport type { TopbarBadge } from '@/types/comfy'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst props = withDefaults(\n defineProps<{\n badge: TopbarBadge\n displayMode?: 'full' | 'compact' | 'icon-only'\n reverseOrder?: boolean\n noPadding?: boolean\n backgroundColor?: string\n }>(),\n {\n displayMode: 'full',\n reverseOrder: false,\n noPadding: false,\n backgroundColor: 'var(--comfy-menu-bg)'\n }\n)\n\nconst popover = ref<InstanceType<typeof Popover>>()\n\nconst togglePopover = (event: Event) => {\n popover.value?.toggle(event)\n}\n\nconst variant = computed(() => props.badge.variant ?? 'info')\n\nconst menuBackgroundStyle = computed(() => ({\n backgroundColor: props.backgroundColor\n}))\n\nconst labelClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'bg-danger-100 text-white'\n case 'warning':\n return 'bg-gold-600 text-black'\n case 'info':\n default:\n return 'bg-white text-black'\n }\n})\n\nconst textClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'text-danger-100'\n case 'warning':\n return 'text-warning-background'\n case 'info':\n default:\n return 'text-text-primary'\n }\n})\n\nconst iconColorClass = computed(() => textClasses.value)\n\nconst iconClass = computed(() => {\n if (props.badge.icon) {\n return props.badge.icon\n }\n switch (variant.value) {\n case 'error':\n return 'pi pi-exclamation-circle'\n case 'warning':\n return 'icon-[lucide--triangle-alert]'\n case 'info':\n default:\n return undefined\n }\n})\n\nconst clickableClasses = 'cursor-pointer transition-opacity hover:opacity-80'\n\nconst dotClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'bg-danger-100'\n case 'warning':\n return 'bg-gold-600'\n case 'info':\n default:\n return 'bg-slate-100'\n }\n})\n\nconst popoverPt = computed(() => ({\n root: {\n class: cn('absolute z-50')\n },\n content: {\n class: cn(\n 'mt-1 rounded-lg',\n 'bg-base-background',\n 'text-base-foreground',\n 'shadow-lg',\n 'border border-border-default'\n )\n }\n}))\n</script>\n","<template>\n <Button\n :size\n :loading=\"isLoading\"\n :disabled=\"isPolling\"\n variant=\"primary\"\n :style=\"\n variant === 'gradient'\n ? {\n background: 'var(--color-subscription-button-gradient)',\n color: 'var(--color-white)'\n }\n : undefined\n \"\n :class=\"cn('font-bold', fluid && 'w-full')\"\n @click=\"handleSubscribe\"\n >\n {{ label || $t('subscription.required.subscribe') }}\n </Button>\n</template>\n\n<script setup lang=\"ts\">\nimport { onBeforeUnmount, ref, watch } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n size = 'lg',\n fluid = true,\n variant = 'default',\n label\n} = defineProps<{\n label?: string\n size?: 'sm' | 'lg'\n variant?: 'default' | 'gradient'\n fluid?: boolean\n}>()\n\nconst emit = defineEmits<{\n subscribed: []\n}>()\n\nconst { subscribe, isActiveSubscription, fetchStatus, showSubscriptionDialog } =\n useSubscription()\n\nconst telemetry = useTelemetry()\n\nconst isLoading = ref(false)\nconst isPolling = ref(false)\nlet pollInterval: number | null = null\nconst isAwaitingStripeSubscription = ref(false)\n\nconst POLL_INTERVAL_MS = 3000 // Poll every 3 seconds\nconst MAX_POLL_DURATION_MS = 5 * 60 * 1000 // Stop polling after 5 minutes\n\nconst startPollingSubscriptionStatus = () => {\n isPolling.value = true\n isLoading.value = true\n\n const startTime = Date.now()\n\n const poll = async () => {\n try {\n if (Date.now() - startTime > MAX_POLL_DURATION_MS) {\n stopPolling()\n return\n }\n\n await fetchStatus()\n\n if (isActiveSubscription.value) {\n stopPolling()\n telemetry?.trackMonthlySubscriptionSucceeded()\n emit('subscribed')\n }\n } catch (error) {\n console.error(\n '[SubscribeButton] Error polling subscription status:',\n error\n )\n }\n }\n\n void poll()\n pollInterval = window.setInterval(poll, POLL_INTERVAL_MS)\n}\n\nconst stopPolling = () => {\n if (pollInterval) {\n clearInterval(pollInterval)\n pollInterval = null\n }\n isPolling.value = false\n isLoading.value = false\n}\n\nwatch(\n [isAwaitingStripeSubscription, isActiveSubscription],\n ([awaiting, isActive]) => {\n if (isCloud && awaiting && isActive) {\n emit('subscribed')\n isAwaitingStripeSubscription.value = false\n }\n }\n)\n\nconst handleSubscribe = async () => {\n if (isCloud) {\n useTelemetry()?.trackSubscription('subscribe_clicked')\n isAwaitingStripeSubscription.value = true\n showSubscriptionDialog()\n return\n }\n\n isLoading.value = true\n try {\n await subscribe()\n\n startPollingSubscriptionStatus()\n } catch (error) {\n console.error('[SubscribeButton] Error initiating subscription:', error)\n isLoading.value = false\n }\n}\n\nonBeforeUnmount(() => {\n stopPolling()\n isAwaitingStripeSubscription.value = false\n})\n</script>\n","<template>\n <Button\n :size\n :loading=\"isLoading\"\n :disabled=\"isPolling\"\n variant=\"primary\"\n :style=\"\n variant === 'gradient'\n ? {\n background: 'var(--color-subscription-button-gradient)',\n color: 'var(--color-white)'\n }\n : undefined\n \"\n :class=\"cn('font-bold', fluid && 'w-full')\"\n @click=\"handleSubscribe\"\n >\n {{ label || $t('subscription.required.subscribe') }}\n </Button>\n</template>\n\n<script setup lang=\"ts\">\nimport { onBeforeUnmount, ref, watch } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n size = 'lg',\n fluid = true,\n variant = 'default',\n label\n} = defineProps<{\n label?: string\n size?: 'sm' | 'lg'\n variant?: 'default' | 'gradient'\n fluid?: boolean\n}>()\n\nconst emit = defineEmits<{\n subscribed: []\n}>()\n\nconst { subscribe, isActiveSubscription, fetchStatus, showSubscriptionDialog } =\n useSubscription()\n\nconst telemetry = useTelemetry()\n\nconst isLoading = ref(false)\nconst isPolling = ref(false)\nlet pollInterval: number | null = null\nconst isAwaitingStripeSubscription = ref(false)\n\nconst POLL_INTERVAL_MS = 3000 // Poll every 3 seconds\nconst MAX_POLL_DURATION_MS = 5 * 60 * 1000 // Stop polling after 5 minutes\n\nconst startPollingSubscriptionStatus = () => {\n isPolling.value = true\n isLoading.value = true\n\n const startTime = Date.now()\n\n const poll = async () => {\n try {\n if (Date.now() - startTime > MAX_POLL_DURATION_MS) {\n stopPolling()\n return\n }\n\n await fetchStatus()\n\n if (isActiveSubscription.value) {\n stopPolling()\n telemetry?.trackMonthlySubscriptionSucceeded()\n emit('subscribed')\n }\n } catch (error) {\n console.error(\n '[SubscribeButton] Error polling subscription status:',\n error\n )\n }\n }\n\n void poll()\n pollInterval = window.setInterval(poll, POLL_INTERVAL_MS)\n}\n\nconst stopPolling = () => {\n if (pollInterval) {\n clearInterval(pollInterval)\n pollInterval = null\n }\n isPolling.value = false\n isLoading.value = false\n}\n\nwatch(\n [isAwaitingStripeSubscription, isActiveSubscription],\n ([awaiting, isActive]) => {\n if (isCloud && awaiting && isActive) {\n emit('subscribed')\n isAwaitingStripeSubscription.value = false\n }\n }\n)\n\nconst handleSubscribe = async () => {\n if (isCloud) {\n useTelemetry()?.trackSubscription('subscribe_clicked')\n isAwaitingStripeSubscription.value = true\n showSubscriptionDialog()\n return\n }\n\n isLoading.value = true\n try {\n await subscribe()\n\n startPollingSubscriptionStatus()\n } catch (error) {\n console.error('[SubscribeButton] Error initiating subscription:', error)\n isLoading.value = false\n }\n}\n\nonBeforeUnmount(() => {\n stopPolling()\n isAwaitingStripeSubscription.value = false\n})\n</script>\n"],"mappings":"mtBCwMM,EAAmB,kQArEzB,MAAM,EAAQ,EAgBR,EAAU,EAAA,EAEV,EAAA,EAAiB,GAAiB,CACtC,EAAQ,OAAO,OAAO,CAAA,GADlB,iBAIA,EAAU,EAAA,IAAe,EAAM,MAAM,SAAW,MAAA,EAEhD,EAAsB,EAAA,KAAgB,CAC1C,gBAAiB,EAAM,eAAA,EACxB,EAEK,EAAe,EAAA,IAAe,CAClC,OAAQ,EAAQ,MAAhB,CACE,IAAK,QACH,MAAO,2BACT,IAAK,UACH,MAAO,yBACT,IAAK,OACL,QACE,MAAO,yBAIP,EAAc,EAAA,IAAe,CACjC,OAAQ,EAAQ,MAAhB,CACE,IAAK,QACH,MAAO,kBACT,IAAK,UACH,MAAO,0BACT,IAAK,OACL,QACE,MAAO,uBAIP,EAAiB,EAAA,IAAe,EAAY,KAAA,EAE5C,EAAY,EAAA,IAAe,CAC/B,GAAI,EAAM,MAAM,KACd,OAAO,EAAM,MAAM,KAErB,OAAQ,EAAQ,MAAhB,CACE,IAAK,QACH,MAAO,2BACT,IAAK,UACH,MAAO,gCACT,IAAK,OACL,QACE,UAMA,EAAa,EAAA,IAAe,CAChC,OAAQ,EAAQ,MAAhB,CACE,IAAK,QACH,MAAO,gBACT,IAAK,UACH,MAAO,cACT,IAAK,OACL,QACE,MAAO,kBAIP,EAAY,EAAA,KAAgB,CAChC,KAAM,CACJ,MAAO,EAAG,eAAA,CAAe,EAE3B,QAAS,CACP,MAAO,EACL,kBACA,qBACA,uBACA,YACA,8BAAA,CACF,GAEH,s1EE3KK,EAAmB,IACnB,GAAuB,IAAS,2KAftC,MAAM,EAAO,EAIP,CAAE,UAAA,EAAW,qBAAA,EAAsB,YAAA,EAAa,uBAAA,CAAA,EACpD,EAAA,EAEI,EAAY,EAAA,EAEZ,EAAY,EAAI,EAAA,EAChB,EAAY,EAAI,EAAA,EACtB,IAAI,EAA8B,KAClC,MAAM,EAA+B,EAAI,EAAA,EAKnC,EAAA,EAAA,IAAuC,CAC3C,EAAU,MAAQ,GAClB,EAAU,MAAQ,GAElB,MAAM,EAAY,KAAK,IAAA,EAEjB,EAAO,EAAA,SAAY,CACvB,GAAI,CACF,GAAI,KAAK,IAAA,EAAQ,EAAY,GAAsB,CACjD,EAAA,EACA,OAGF,MAAM,EAAA,EAEF,EAAqB,QACvB,EAAA,EACA,GAAW,kCAAA,EACX,EAAK,YAAA,SAEA,EAAO,CACd,QAAQ,MACN,uDACA,CAAA,IAjBO,QAsBR,EAAA,EACL,EAAe,OAAO,YAAY,EAAM,CAAA,GA7BpC,kCAgCA,EAAA,EAAA,IAAoB,CACpB,IACF,cAAc,CAAA,EACd,EAAe,MAEjB,EAAU,MAAQ,GAClB,EAAU,MAAQ,IANd,eASN,EACE,CAAC,EAA8B,CAAA,EAAqB,CACnD,CAAC,EAAU,CAAA,IAAc,CACpB,GAAW,GAAY,IACzB,EAAK,YAAA,EACL,EAA6B,MAAQ,MAK3C,MAAM,EAAkB,EAAA,SAAY,CAClC,GAAI,EAAS,CACX,EAAA,GAAgB,kBAAkB,mBAAA,EAClC,EAA6B,MAAQ,GACrC,EAAA,EACA,OAGF,EAAU,MAAQ,GAClB,GAAI,CACF,MAAM,EAAA,EAEN,EAAA,QACO,EAAO,CACd,QAAQ,MAAM,mDAAoD,CAAA,EAClE,EAAU,MAAQ,KAfE,mBAmBxB,OAAA,EAAA,IAAsB,CACpB,EAAA,EACA,EAA6B,MAAQ"}
1
+ {"version":3,"file":"SubscribeButton-DZBycfCA.js","names":[],"sources":["../../src/components/topbar/TopbarBadge.vue","../../src/components/topbar/TopbarBadge.vue","../../src/platform/cloud/subscription/components/SubscribeButton.vue","../../src/platform/cloud/subscription/components/SubscribeButton.vue"],"sourcesContent":["<template>\n <!-- Icon-only mode with Popover -->\n <div\n v-if=\"displayMode === 'icon-only'\"\n class=\"relative inline-flex h-full shrink-0 items-center justify-center px-2\"\n :class=\"clickableClasses\"\n :style=\"menuBackgroundStyle\"\n @click=\"togglePopover\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-else-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div v-else class=\"size-2 shrink-0 rounded-full\" :class=\"dotClasses\" />\n <Popover\n ref=\"popover\"\n append-to=\"body\"\n :auto-z-index=\"true\"\n :base-z-index=\"1000\"\n :dismissable=\"true\"\n :close-on-escape=\"true\"\n unstyled\n :pt=\"popoverPt\"\n >\n <div class=\"flex max-w-xs min-w-40 flex-col gap-2 p-3\">\n <div\n v-if=\"badge.label\"\n class=\"w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"text-sm font-inter\">{{ badge.text }}</div>\n <div v-if=\"badge.tooltip\" class=\"text-xs\">\n {{ badge.tooltip }}\n </div>\n </div>\n </Popover>\n </div>\n\n <!-- Compact mode: Icon + Label only with Popover -->\n <div\n v-else-if=\"displayMode === 'compact'\"\n class=\"relative inline-flex h-full\"\n :style=\"menuBackgroundStyle\"\n >\n <div\n class=\"flex h-full shrink-0 items-center gap-2 whitespace-nowrap\"\n :class=\"[\n { 'flex-row-reverse': reverseOrder },\n noPadding ? '' : 'px-3',\n clickableClasses\n ]\"\n @click=\"togglePopover\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n </div>\n <Popover\n ref=\"popover\"\n append-to=\"body\"\n :auto-z-index=\"true\"\n :base-z-index=\"1000\"\n :dismissable=\"true\"\n :close-on-escape=\"true\"\n unstyled\n :pt=\"popoverPt\"\n >\n <div class=\"flex max-w-xs min-w-40 flex-col gap-2 p-3\">\n <div\n v-if=\"badge.label\"\n class=\"w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"text-sm font-inter\">{{ badge.text }}</div>\n <div v-if=\"badge.tooltip\" class=\"text-xs\">\n {{ badge.tooltip }}\n </div>\n </div>\n </Popover>\n </div>\n\n <!-- Full mode: Icon + Label + Text -->\n <div\n v-else\n v-tooltip=\"badge.tooltip\"\n class=\"flex h-full shrink-0 items-center gap-2 whitespace-nowrap\"\n :class=\"[{ 'flex-row-reverse': reverseOrder }, noPadding ? '' : 'px-3']\"\n :style=\"menuBackgroundStyle\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"font-inter text-sm\" :class=\"textClasses\">\n {{ badge.text }}\n </div>\n </div>\n</template>\n<script setup lang=\"ts\">\nimport Popover from 'primevue/popover'\nimport { computed, ref } from 'vue'\n\nimport type { TopbarBadge } from '@/types/comfy'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst props = withDefaults(\n defineProps<{\n badge: TopbarBadge\n displayMode?: 'full' | 'compact' | 'icon-only'\n reverseOrder?: boolean\n noPadding?: boolean\n backgroundColor?: string\n }>(),\n {\n displayMode: 'full',\n reverseOrder: false,\n noPadding: false,\n backgroundColor: 'var(--comfy-menu-bg)'\n }\n)\n\nconst popover = ref<InstanceType<typeof Popover>>()\n\nconst togglePopover = (event: Event) => {\n popover.value?.toggle(event)\n}\n\nconst variant = computed(() => props.badge.variant ?? 'info')\n\nconst menuBackgroundStyle = computed(() => ({\n backgroundColor: props.backgroundColor\n}))\n\nconst labelClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'bg-danger-100 text-white'\n case 'warning':\n return 'bg-gold-600 text-black'\n case 'info':\n default:\n return 'bg-white text-black'\n }\n})\n\nconst textClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'text-danger-100'\n case 'warning':\n return 'text-warning-background'\n case 'info':\n default:\n return 'text-text-primary'\n }\n})\n\nconst iconColorClass = computed(() => textClasses.value)\n\nconst iconClass = computed(() => {\n if (props.badge.icon) {\n return props.badge.icon\n }\n switch (variant.value) {\n case 'error':\n return 'pi pi-exclamation-circle'\n case 'warning':\n return 'icon-[lucide--triangle-alert]'\n case 'info':\n default:\n return undefined\n }\n})\n\nconst clickableClasses = 'cursor-pointer transition-opacity hover:opacity-80'\n\nconst dotClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'bg-danger-100'\n case 'warning':\n return 'bg-gold-600'\n case 'info':\n default:\n return 'bg-slate-100'\n }\n})\n\nconst popoverPt = computed(() => ({\n root: {\n class: cn('absolute z-50')\n },\n content: {\n class: cn(\n 'mt-1 rounded-lg',\n 'bg-base-background',\n 'text-base-foreground',\n 'shadow-lg',\n 'border border-border-default'\n )\n }\n}))\n</script>\n","<template>\n <!-- Icon-only mode with Popover -->\n <div\n v-if=\"displayMode === 'icon-only'\"\n class=\"relative inline-flex h-full shrink-0 items-center justify-center px-2\"\n :class=\"clickableClasses\"\n :style=\"menuBackgroundStyle\"\n @click=\"togglePopover\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-else-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div v-else class=\"size-2 shrink-0 rounded-full\" :class=\"dotClasses\" />\n <Popover\n ref=\"popover\"\n append-to=\"body\"\n :auto-z-index=\"true\"\n :base-z-index=\"1000\"\n :dismissable=\"true\"\n :close-on-escape=\"true\"\n unstyled\n :pt=\"popoverPt\"\n >\n <div class=\"flex max-w-xs min-w-40 flex-col gap-2 p-3\">\n <div\n v-if=\"badge.label\"\n class=\"w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"text-sm font-inter\">{{ badge.text }}</div>\n <div v-if=\"badge.tooltip\" class=\"text-xs\">\n {{ badge.tooltip }}\n </div>\n </div>\n </Popover>\n </div>\n\n <!-- Compact mode: Icon + Label only with Popover -->\n <div\n v-else-if=\"displayMode === 'compact'\"\n class=\"relative inline-flex h-full\"\n :style=\"menuBackgroundStyle\"\n >\n <div\n class=\"flex h-full shrink-0 items-center gap-2 whitespace-nowrap\"\n :class=\"[\n { 'flex-row-reverse': reverseOrder },\n noPadding ? '' : 'px-3',\n clickableClasses\n ]\"\n @click=\"togglePopover\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n </div>\n <Popover\n ref=\"popover\"\n append-to=\"body\"\n :auto-z-index=\"true\"\n :base-z-index=\"1000\"\n :dismissable=\"true\"\n :close-on-escape=\"true\"\n unstyled\n :pt=\"popoverPt\"\n >\n <div class=\"flex max-w-xs min-w-40 flex-col gap-2 p-3\">\n <div\n v-if=\"badge.label\"\n class=\"w-fit rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"text-sm font-inter\">{{ badge.text }}</div>\n <div v-if=\"badge.tooltip\" class=\"text-xs\">\n {{ badge.tooltip }}\n </div>\n </div>\n </Popover>\n </div>\n\n <!-- Full mode: Icon + Label + Text -->\n <div\n v-else\n v-tooltip=\"badge.tooltip\"\n class=\"flex h-full shrink-0 items-center gap-2 whitespace-nowrap\"\n :class=\"[{ 'flex-row-reverse': reverseOrder }, noPadding ? '' : 'px-3']\"\n :style=\"menuBackgroundStyle\"\n >\n <i\n v-if=\"iconClass\"\n :class=\"['shrink-0 text-base', iconClass, iconColorClass]\"\n />\n <div\n v-if=\"badge.label\"\n class=\"shrink-0 rounded-full px-1.5 py-0.5 text-xxxs font-semibold\"\n :class=\"labelClasses\"\n >\n {{ badge.label }}\n </div>\n <div class=\"font-inter text-sm\" :class=\"textClasses\">\n {{ badge.text }}\n </div>\n </div>\n</template>\n<script setup lang=\"ts\">\nimport Popover from 'primevue/popover'\nimport { computed, ref } from 'vue'\n\nimport type { TopbarBadge } from '@/types/comfy'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst props = withDefaults(\n defineProps<{\n badge: TopbarBadge\n displayMode?: 'full' | 'compact' | 'icon-only'\n reverseOrder?: boolean\n noPadding?: boolean\n backgroundColor?: string\n }>(),\n {\n displayMode: 'full',\n reverseOrder: false,\n noPadding: false,\n backgroundColor: 'var(--comfy-menu-bg)'\n }\n)\n\nconst popover = ref<InstanceType<typeof Popover>>()\n\nconst togglePopover = (event: Event) => {\n popover.value?.toggle(event)\n}\n\nconst variant = computed(() => props.badge.variant ?? 'info')\n\nconst menuBackgroundStyle = computed(() => ({\n backgroundColor: props.backgroundColor\n}))\n\nconst labelClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'bg-danger-100 text-white'\n case 'warning':\n return 'bg-gold-600 text-black'\n case 'info':\n default:\n return 'bg-white text-black'\n }\n})\n\nconst textClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'text-danger-100'\n case 'warning':\n return 'text-warning-background'\n case 'info':\n default:\n return 'text-text-primary'\n }\n})\n\nconst iconColorClass = computed(() => textClasses.value)\n\nconst iconClass = computed(() => {\n if (props.badge.icon) {\n return props.badge.icon\n }\n switch (variant.value) {\n case 'error':\n return 'pi pi-exclamation-circle'\n case 'warning':\n return 'icon-[lucide--triangle-alert]'\n case 'info':\n default:\n return undefined\n }\n})\n\nconst clickableClasses = 'cursor-pointer transition-opacity hover:opacity-80'\n\nconst dotClasses = computed(() => {\n switch (variant.value) {\n case 'error':\n return 'bg-danger-100'\n case 'warning':\n return 'bg-gold-600'\n case 'info':\n default:\n return 'bg-slate-100'\n }\n})\n\nconst popoverPt = computed(() => ({\n root: {\n class: cn('absolute z-50')\n },\n content: {\n class: cn(\n 'mt-1 rounded-lg',\n 'bg-base-background',\n 'text-base-foreground',\n 'shadow-lg',\n 'border border-border-default'\n )\n }\n}))\n</script>\n","<template>\n <Button\n :size\n :loading=\"isLoading\"\n :disabled=\"isPolling\"\n variant=\"primary\"\n :style=\"\n variant === 'gradient'\n ? {\n background: 'var(--color-subscription-button-gradient)',\n color: 'var(--color-white)'\n }\n : undefined\n \"\n :class=\"cn('font-bold', fluid && 'w-full')\"\n @click=\"handleSubscribe\"\n >\n {{ label || $t('subscription.required.subscribe') }}\n </Button>\n</template>\n\n<script setup lang=\"ts\">\nimport { onBeforeUnmount, ref, watch } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n size = 'lg',\n fluid = true,\n variant = 'default',\n label\n} = defineProps<{\n label?: string\n size?: 'sm' | 'lg'\n variant?: 'default' | 'gradient'\n fluid?: boolean\n}>()\n\nconst emit = defineEmits<{\n subscribed: []\n}>()\n\nconst { subscribe, isActiveSubscription, fetchStatus, showSubscriptionDialog } =\n useSubscription()\n\nconst telemetry = useTelemetry()\n\nconst isLoading = ref(false)\nconst isPolling = ref(false)\nlet pollInterval: number | null = null\nconst isAwaitingStripeSubscription = ref(false)\n\nconst POLL_INTERVAL_MS = 3000 // Poll every 3 seconds\nconst MAX_POLL_DURATION_MS = 5 * 60 * 1000 // Stop polling after 5 minutes\n\nconst startPollingSubscriptionStatus = () => {\n isPolling.value = true\n isLoading.value = true\n\n const startTime = Date.now()\n\n const poll = async () => {\n try {\n if (Date.now() - startTime > MAX_POLL_DURATION_MS) {\n stopPolling()\n return\n }\n\n await fetchStatus()\n\n if (isActiveSubscription.value) {\n stopPolling()\n telemetry?.trackMonthlySubscriptionSucceeded()\n emit('subscribed')\n }\n } catch (error) {\n console.error(\n '[SubscribeButton] Error polling subscription status:',\n error\n )\n }\n }\n\n void poll()\n pollInterval = window.setInterval(poll, POLL_INTERVAL_MS)\n}\n\nconst stopPolling = () => {\n if (pollInterval) {\n clearInterval(pollInterval)\n pollInterval = null\n }\n isPolling.value = false\n isLoading.value = false\n}\n\nwatch(\n [isAwaitingStripeSubscription, isActiveSubscription],\n ([awaiting, isActive]) => {\n if (isCloud && awaiting && isActive) {\n emit('subscribed')\n isAwaitingStripeSubscription.value = false\n }\n }\n)\n\nconst handleSubscribe = async () => {\n if (isCloud) {\n useTelemetry()?.trackSubscription('subscribe_clicked')\n isAwaitingStripeSubscription.value = true\n showSubscriptionDialog()\n return\n }\n\n isLoading.value = true\n try {\n await subscribe()\n\n startPollingSubscriptionStatus()\n } catch (error) {\n console.error('[SubscribeButton] Error initiating subscription:', error)\n isLoading.value = false\n }\n}\n\nonBeforeUnmount(() => {\n stopPolling()\n isAwaitingStripeSubscription.value = false\n})\n</script>\n","<template>\n <Button\n :size\n :loading=\"isLoading\"\n :disabled=\"isPolling\"\n variant=\"primary\"\n :style=\"\n variant === 'gradient'\n ? {\n background: 'var(--color-subscription-button-gradient)',\n color: 'var(--color-white)'\n }\n : undefined\n \"\n :class=\"cn('font-bold', fluid && 'w-full')\"\n @click=\"handleSubscribe\"\n >\n {{ label || $t('subscription.required.subscribe') }}\n </Button>\n</template>\n\n<script setup lang=\"ts\">\nimport { onBeforeUnmount, ref, watch } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n size = 'lg',\n fluid = true,\n variant = 'default',\n label\n} = defineProps<{\n label?: string\n size?: 'sm' | 'lg'\n variant?: 'default' | 'gradient'\n fluid?: boolean\n}>()\n\nconst emit = defineEmits<{\n subscribed: []\n}>()\n\nconst { subscribe, isActiveSubscription, fetchStatus, showSubscriptionDialog } =\n useSubscription()\n\nconst telemetry = useTelemetry()\n\nconst isLoading = ref(false)\nconst isPolling = ref(false)\nlet pollInterval: number | null = null\nconst isAwaitingStripeSubscription = ref(false)\n\nconst POLL_INTERVAL_MS = 3000 // Poll every 3 seconds\nconst MAX_POLL_DURATION_MS = 5 * 60 * 1000 // Stop polling after 5 minutes\n\nconst startPollingSubscriptionStatus = () => {\n isPolling.value = true\n isLoading.value = true\n\n const startTime = Date.now()\n\n const poll = async () => {\n try {\n if (Date.now() - startTime > MAX_POLL_DURATION_MS) {\n stopPolling()\n return\n }\n\n await fetchStatus()\n\n if (isActiveSubscription.value) {\n stopPolling()\n telemetry?.trackMonthlySubscriptionSucceeded()\n emit('subscribed')\n }\n } catch (error) {\n console.error(\n '[SubscribeButton] Error polling subscription status:',\n error\n )\n }\n }\n\n void poll()\n pollInterval = window.setInterval(poll, POLL_INTERVAL_MS)\n}\n\nconst stopPolling = () => {\n if (pollInterval) {\n clearInterval(pollInterval)\n pollInterval = null\n }\n isPolling.value = false\n isLoading.value = false\n}\n\nwatch(\n [isAwaitingStripeSubscription, isActiveSubscription],\n ([awaiting, isActive]) => {\n if (isCloud && awaiting && isActive) {\n emit('subscribed')\n isAwaitingStripeSubscription.value = false\n }\n }\n)\n\nconst handleSubscribe = async () => {\n if (isCloud) {\n useTelemetry()?.trackSubscription('subscribe_clicked')\n isAwaitingStripeSubscription.value = true\n showSubscriptionDialog()\n return\n }\n\n isLoading.value = true\n try {\n await subscribe()\n\n startPollingSubscriptionStatus()\n } catch (error) {\n console.error('[SubscribeButton] Error initiating subscription:', error)\n isLoading.value = false\n }\n}\n\nonBeforeUnmount(() => {\n stopPolling()\n isAwaitingStripeSubscription.value = false\n})\n</script>\n"],"mappings":"mtBCwMM,EAAmB,kQArEzB,MAAM,EAAQ,EAgBR,EAAU,EAAA,EAEV,EAAA,EAAiB,GAAiB,CACtC,EAAQ,OAAO,OAAO,CAAA,GADlB,iBAIA,EAAU,EAAA,IAAe,EAAM,MAAM,SAAW,MAAA,EAEhD,EAAsB,EAAA,KAAgB,CAC1C,gBAAiB,EAAM,eAAA,EACxB,EAEK,EAAe,EAAA,IAAe,CAClC,OAAQ,EAAQ,MAAhB,CACE,IAAK,QACH,MAAO,2BACT,IAAK,UACH,MAAO,yBACT,IAAK,OACL,QACE,MAAO,yBAIP,EAAc,EAAA,IAAe,CACjC,OAAQ,EAAQ,MAAhB,CACE,IAAK,QACH,MAAO,kBACT,IAAK,UACH,MAAO,0BACT,IAAK,OACL,QACE,MAAO,uBAIP,EAAiB,EAAA,IAAe,EAAY,KAAA,EAE5C,EAAY,EAAA,IAAe,CAC/B,GAAI,EAAM,MAAM,KACd,OAAO,EAAM,MAAM,KAErB,OAAQ,EAAQ,MAAhB,CACE,IAAK,QACH,MAAO,2BACT,IAAK,UACH,MAAO,gCACT,IAAK,OACL,QACE,UAMA,EAAa,EAAA,IAAe,CAChC,OAAQ,EAAQ,MAAhB,CACE,IAAK,QACH,MAAO,gBACT,IAAK,UACH,MAAO,cACT,IAAK,OACL,QACE,MAAO,kBAIP,EAAY,EAAA,KAAgB,CAChC,KAAM,CACJ,MAAO,EAAG,eAAA,CAAe,EAE3B,QAAS,CACP,MAAO,EACL,kBACA,qBACA,uBACA,YACA,8BAAA,CACF,GAEH,s1EE3KK,EAAmB,IACnB,GAAuB,IAAS,2KAftC,MAAM,EAAO,EAIP,CAAE,UAAA,EAAW,qBAAA,EAAsB,YAAA,EAAa,uBAAA,CAAA,EACpD,EAAA,EAEI,EAAY,EAAA,EAEZ,EAAY,EAAI,EAAA,EAChB,EAAY,EAAI,EAAA,EACtB,IAAI,EAA8B,KAClC,MAAM,EAA+B,EAAI,EAAA,EAKnC,EAAA,EAAA,IAAuC,CAC3C,EAAU,MAAQ,GAClB,EAAU,MAAQ,GAElB,MAAM,EAAY,KAAK,IAAA,EAEjB,EAAO,EAAA,SAAY,CACvB,GAAI,CACF,GAAI,KAAK,IAAA,EAAQ,EAAY,GAAsB,CACjD,EAAA,EACA,OAGF,MAAM,EAAA,EAEF,EAAqB,QACvB,EAAA,EACA,GAAW,kCAAA,EACX,EAAK,YAAA,SAEA,EAAO,CACd,QAAQ,MACN,uDACA,CAAA,IAjBO,QAsBR,EAAA,EACL,EAAe,OAAO,YAAY,EAAM,CAAA,GA7BpC,kCAgCA,EAAA,EAAA,IAAoB,CACpB,IACF,cAAc,CAAA,EACd,EAAe,MAEjB,EAAU,MAAQ,GAClB,EAAU,MAAQ,IANd,eASN,EACE,CAAC,EAA8B,CAAA,EAAqB,CACnD,CAAC,EAAU,CAAA,IAAc,CACpB,GAAW,GAAY,IACzB,EAAK,YAAA,EACL,EAA6B,MAAQ,MAK3C,MAAM,EAAkB,EAAA,SAAY,CAClC,GAAI,EAAS,CACX,EAAA,GAAgB,kBAAkB,mBAAA,EAClC,EAA6B,MAAQ,GACrC,EAAA,EACA,OAGF,EAAU,MAAQ,GAClB,GAAI,CACF,MAAM,EAAA,EAEN,EAAA,QACO,EAAO,CACd,QAAQ,MAAM,mDAAoD,CAAA,EAClE,EAAU,MAAQ,KAfE,mBAmBxB,OAAA,EAAA,IAAsB,CACpB,EAAA,EACA,EAA6B,MAAQ"}
@@ -1,3 +1,3 @@
1
- var l=Object.defineProperty;var r=(o,t)=>l(o,"name",{value:t,configurable:!0});import{$o as b,Ba as p,Do as d,Ka as _,Oo as m,Va as v,Ya as f,_i as T,do as g,fi as k,go as w,za as R}from"./vendor-other-DlQF6V2E.js";import{n as B}from"./vendor-vue-D9IUuwPJ.js";import{Lt as S}from"./api-CUAc7rDA.js";import{t as h}from"./Button-Do2I1OAA.js";import{Nr as y,ft as D}from"./dialogService-BZ1FmjZL.js";var C=f({__name:"SubscribeToRun",setup(o){const{t}=B(),e=T(k).greaterOrEqual("md"),a=R(()=>e.value?t("subscription.subscribeToRunFull"):t("subscription.subscribeToRun")),{showSubscriptionDialog:i}=y(),u=r(()=>{S&&D()?.trackRunButton({subscribe_to_run:!0}),i()},"handleSubscribeToRun");return(n,s)=>{const c=w("tooltip");return m((g(),v(h,{class:"subscribe-to-run-button whitespace-nowrap",variant:"primary",size:"sm",style:{background:"var(--color-subscription-button-gradient)",color:"var(--color-white)",borderColor:"transparent"},"data-testid":"subscribe-to-run-button",onClick:u},{default:d(()=>[s[0]||(s[0]=p("i",{class:"pi pi-lock"},null,-1)),_(" "+b(a.value),1)]),_:1})),[[c,{value:n.$t("subscription.subscribeToRunFull"),showDelay:600},void 0,{bottom:!0}]])}}}),F=C;export{F as t};
1
+ var l=Object.defineProperty;var r=(o,t)=>l(o,"name",{value:t,configurable:!0});import{$o as b,Ba as p,Do as d,Ka as _,Oo as m,Va as v,Ya as f,_i as T,do as g,fi as k,go as w,za as R}from"./vendor-other-DlQF6V2E.js";import{n as B}from"./vendor-vue-D9IUuwPJ.js";import{Lt as S}from"./api-Dwq2LQIW.js";import{t as h}from"./Button-Do2I1OAA.js";import{Nr as y,ft as D}from"./dialogService-YG0RH337.js";var C=f({__name:"SubscribeToRun",setup(o){const{t}=B(),e=T(k).greaterOrEqual("md"),a=R(()=>e.value?t("subscription.subscribeToRunFull"):t("subscription.subscribeToRun")),{showSubscriptionDialog:i}=y(),u=r(()=>{S&&D()?.trackRunButton({subscribe_to_run:!0}),i()},"handleSubscribeToRun");return(n,s)=>{const c=w("tooltip");return m((g(),v(h,{class:"subscribe-to-run-button whitespace-nowrap",variant:"primary",size:"sm",style:{background:"var(--color-subscription-button-gradient)",color:"var(--color-white)",borderColor:"transparent"},"data-testid":"subscribe-to-run-button",onClick:u},{default:d(()=>[s[0]||(s[0]=p("i",{class:"pi pi-lock"},null,-1)),_(" "+b(a.value),1)]),_:1})),[[c,{value:n.$t("subscription.subscribeToRunFull"),showDelay:600},void 0,{bottom:!0}]])}}}),F=C;export{F as t};
2
2
 
3
- //# sourceMappingURL=SubscribeToRun-DnXzV8y0.js.map
3
+ //# sourceMappingURL=SubscribeToRun-4YolxBOL.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SubscribeToRun-DnXzV8y0.js","names":[],"sources":["../../src/platform/cloud/subscription/components/SubscribeToRun.vue","../../src/platform/cloud/subscription/components/SubscribeToRun.vue"],"sourcesContent":["<template>\n <Button\n v-tooltip.bottom=\"{\n value: $t('subscription.subscribeToRunFull'),\n showDelay: 600\n }\"\n class=\"subscribe-to-run-button whitespace-nowrap\"\n variant=\"primary\"\n size=\"sm\"\n :style=\"{\n background: 'var(--color-subscription-button-gradient)',\n color: 'var(--color-white)',\n borderColor: 'transparent'\n }\"\n data-testid=\"subscribe-to-run-button\"\n @click=\"handleSubscribeToRun\"\n >\n <i class=\"pi pi-lock\" />\n {{ buttonLabel }}\n </Button>\n</template>\n\n<script setup lang=\"ts\">\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core'\nimport { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\n\nconst { t } = useI18n()\nconst breakpoints = useBreakpoints(breakpointsTailwind)\nconst isMdOrLarger = breakpoints.greaterOrEqual('md')\n\nconst buttonLabel = computed(() =>\n isMdOrLarger.value\n ? t('subscription.subscribeToRunFull')\n : t('subscription.subscribeToRun')\n)\n\nconst { showSubscriptionDialog } = useSubscription()\n\nconst handleSubscribeToRun = () => {\n if (isCloud) {\n useTelemetry()?.trackRunButton({ subscribe_to_run: true })\n }\n\n showSubscriptionDialog()\n}\n</script>\n","<template>\n <Button\n v-tooltip.bottom=\"{\n value: $t('subscription.subscribeToRunFull'),\n showDelay: 600\n }\"\n class=\"subscribe-to-run-button whitespace-nowrap\"\n variant=\"primary\"\n size=\"sm\"\n :style=\"{\n background: 'var(--color-subscription-button-gradient)',\n color: 'var(--color-white)',\n borderColor: 'transparent'\n }\"\n data-testid=\"subscribe-to-run-button\"\n @click=\"handleSubscribeToRun\"\n >\n <i class=\"pi pi-lock\" />\n {{ buttonLabel }}\n </Button>\n</template>\n\n<script setup lang=\"ts\">\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core'\nimport { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\n\nconst { t } = useI18n()\nconst breakpoints = useBreakpoints(breakpointsTailwind)\nconst isMdOrLarger = breakpoints.greaterOrEqual('md')\n\nconst buttonLabel = computed(() =>\n isMdOrLarger.value\n ? t('subscription.subscribeToRunFull')\n : t('subscription.subscribeToRun')\n)\n\nconst { showSubscriptionDialog } = useSubscription()\n\nconst handleSubscribeToRun = () => {\n if (isCloud) {\n useTelemetry()?.trackRunButton({ subscribe_to_run: true })\n }\n\n showSubscriptionDialog()\n}\n</script>\n"],"mappings":"ubCgCA,KAAM,CAAE,CAAA,EAAM,EAAA,EAER,EADc,EAAe,CAAA,EACF,eAAe,IAAA,EAE1C,EAAc,EAAA,IAClB,EAAa,MACT,EAAE,iCAAA,EACF,EAAE,6BAAA,CAA6B,EAG/B,CAAE,uBAAA,CAAA,EAA2B,EAAA,EAE7B,EAAA,EAAA,IAA6B,CAC7B,GACF,EAAA,GAAgB,eAAe,CAAE,iBAAkB,EAAA,CAAM,EAG3D,EAAA,GALI"}
1
+ {"version":3,"file":"SubscribeToRun-4YolxBOL.js","names":[],"sources":["../../src/platform/cloud/subscription/components/SubscribeToRun.vue","../../src/platform/cloud/subscription/components/SubscribeToRun.vue"],"sourcesContent":["<template>\n <Button\n v-tooltip.bottom=\"{\n value: $t('subscription.subscribeToRunFull'),\n showDelay: 600\n }\"\n class=\"subscribe-to-run-button whitespace-nowrap\"\n variant=\"primary\"\n size=\"sm\"\n :style=\"{\n background: 'var(--color-subscription-button-gradient)',\n color: 'var(--color-white)',\n borderColor: 'transparent'\n }\"\n data-testid=\"subscribe-to-run-button\"\n @click=\"handleSubscribeToRun\"\n >\n <i class=\"pi pi-lock\" />\n {{ buttonLabel }}\n </Button>\n</template>\n\n<script setup lang=\"ts\">\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core'\nimport { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\n\nconst { t } = useI18n()\nconst breakpoints = useBreakpoints(breakpointsTailwind)\nconst isMdOrLarger = breakpoints.greaterOrEqual('md')\n\nconst buttonLabel = computed(() =>\n isMdOrLarger.value\n ? t('subscription.subscribeToRunFull')\n : t('subscription.subscribeToRun')\n)\n\nconst { showSubscriptionDialog } = useSubscription()\n\nconst handleSubscribeToRun = () => {\n if (isCloud) {\n useTelemetry()?.trackRunButton({ subscribe_to_run: true })\n }\n\n showSubscriptionDialog()\n}\n</script>\n","<template>\n <Button\n v-tooltip.bottom=\"{\n value: $t('subscription.subscribeToRunFull'),\n showDelay: 600\n }\"\n class=\"subscribe-to-run-button whitespace-nowrap\"\n variant=\"primary\"\n size=\"sm\"\n :style=\"{\n background: 'var(--color-subscription-button-gradient)',\n color: 'var(--color-white)',\n borderColor: 'transparent'\n }\"\n data-testid=\"subscribe-to-run-button\"\n @click=\"handleSubscribeToRun\"\n >\n <i class=\"pi pi-lock\" />\n {{ buttonLabel }}\n </Button>\n</template>\n\n<script setup lang=\"ts\">\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core'\nimport { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { isCloud } from '@/platform/distribution/types'\nimport { useTelemetry } from '@/platform/telemetry'\n\nconst { t } = useI18n()\nconst breakpoints = useBreakpoints(breakpointsTailwind)\nconst isMdOrLarger = breakpoints.greaterOrEqual('md')\n\nconst buttonLabel = computed(() =>\n isMdOrLarger.value\n ? t('subscription.subscribeToRunFull')\n : t('subscription.subscribeToRun')\n)\n\nconst { showSubscriptionDialog } = useSubscription()\n\nconst handleSubscribeToRun = () => {\n if (isCloud) {\n useTelemetry()?.trackRunButton({ subscribe_to_run: true })\n }\n\n showSubscriptionDialog()\n}\n</script>\n"],"mappings":"ubCgCA,KAAM,CAAE,CAAA,EAAM,EAAA,EAER,EADc,EAAe,CAAA,EACF,eAAe,IAAA,EAE1C,EAAc,EAAA,IAClB,EAAa,MACT,EAAE,iCAAA,EACF,EAAE,6BAAA,CAA6B,EAG/B,CAAE,uBAAA,CAAA,EAA2B,EAAA,EAE7B,EAAA,EAAA,IAA6B,CAC7B,GACF,EAAA,GAAgB,eAAe,CAAE,iBAAkB,EAAA,CAAM,EAG3D,EAAA,GALI"}
@@ -1,3 +1,3 @@
1
- var re=Object.defineProperty;var n=(d,v)=>re(d,"name",{value:v,configurable:!0});import{f as ie,lt as A}from"./vendor-primevue-DcMRXJN3.js";import{$o as a,Ba as e,Do as u,Ha as w,Jo as s,Ka as p,Pa as B,Ua as l,Va as m,Xo as oe,Ya as ne,co as U,do as i,oo as le,po as de,qa as g,za as o,zo as ce}from"./vendor-other-DlQF6V2E.js";import{n as F}from"./vendor-vue-D9IUuwPJ.js";import{Lt as ue}from"./api-CUAc7rDA.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import"./useErrorHandling-CI8_F4yx.js";import{t as pe}from"./src-DTrob8OL.js";import{t as f}from"./Button-Do2I1OAA.js";import{k as me}from"./PanelTemplate-BjN5XNg2.js";import{Ir as fe,Lr as ve,Mr as Y,Nr as z,Or as xe,Pr as _e,Rr as be,Ur as ge,Vr as ye,ft as he,o as ke,t as Ce,zr as Se}from"./dialogService-BZ1FmjZL.js";import{t as we}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 $e}from"./SubscribeButton-CTOQRkfg.js";import{t as De}from"./CloudBadge-BnLiAHDN.js";import{t as Le}from"./useSubscriptionDialog-Chxkdny5.js";function Pe(){const d=Ce(),v=Y(),x=xe(),y=he(),{fetchStatus:c}=z(),h=ce(!1);U(()=>{C()});const _=n(()=>{d.showTopUpCreditsDialog()},"handleAddApiCredits"),$=n(async()=>{try{h.value=!0,ue&&y?.trackHelpResourceClicked({resource_type:"help_feedback",is_external:!0,source:"subscription"}),await x.execute("Comfy.ContactSupport")}catch(k){console.error("[useSubscriptionActions] Error contacting support:",k)}finally{h.value=!1}},"handleMessageSupport"),C=n(async()=>{try{await Promise.all([v.fetchBalance(),c()])}catch(k){console.error("[useSubscriptionActions] Error refreshing data:",k)}},"handleRefresh");return{isLoadingSupport:h,handleAddApiCredits:_,handleMessageSupport:$,handleRefresh:C,handleLearnMoreClick:n(()=>{window.open("https://docs.comfy.org/get_started/cloud","_blank")},"handleLearnMoreClick")}}n(Pe,"useSubscriptionActions");function Ae(){const d=ye(),{locale:v}=F(),x=n(y=>ke({cents:y??0,locale:v.value,numberOptions:{minimumFractionDigits:0,maximumFractionDigits:0}}),"formatBalance");return{totalCredits:o(()=>x(d.balance?.amount_micros)),monthlyBonusCredits:o(()=>x(d.balance?.cloud_credit_balance_micros)),prepaidCredits:o(()=>x(d.balance?.prepaid_balance_micros)),isLoadingBalance:o(()=>d.isFetchingBalance)}}n(Ae,"useSubscriptionCredits");var Be={class:"flex h-full flex-col gap-6"},Te={class:"flex items-center gap-2"},Ee={class:"text-2xl font-inter font-semibold leading-tight"},Ie={class:"pt-1"},Re={class:"grow overflow-auto"},Me={class:"rounded-2xl border border-interface-stroke p-6"},Ne={class:"flex items-center justify-between gap-2"},Ue={class:"flex flex-col gap-2"},Fe={class:"text-sm font-bold text-text-primary"},Ye={class:"flex items-baseline gap-1 font-inter font-semibold"},ze={class:"text-2xl"},He={class:"text-base"},Oe={key:0,class:"text-sm text-text-secondary"},Ve={class:"flex flex-col lg:flex-row gap-6 pt-9"},Ke={class:"flex flex-col shrink-0"},je={class:"flex flex-col gap-3"},qe={class:"flex flex-col gap-2"},Xe={class:"text-sm text-muted"},Ge={key:1,class:"text-2xl font-bold"},Je={class:"text-sm text-muted"},We={class:"pr-4 font-bold text-left align-middle"},Qe={key:1},Ze=["title"],et={class:"pr-4 font-bold text-left align-middle"},tt={key:1},st=["title"],at={class:"flex items-center justify-between"},rt=["href"],it={class:"flex flex-col gap-2"},ot={class:"text-sm text-text-primary"},nt={class:"flex flex-col gap-0"},lt={key:0,class:"pi pi-check text-xs text-text-primary"},dt={key:1,class:"text-sm font-normal whitespace-nowrap text-text-primary"},ct={class:"text-sm text-muted"},ut={class:"flex items-center gap-2 py-4"},pt={href:"https://www.comfy.org/cloud/pricing",target:"_blank",rel:"noopener noreferrer",class:"text-sm underline hover:opacity-80 text-muted"},mt={class:"flex items-center justify-between border-t border-interface-stroke pt-3"},ft={class:"flex gap-2"},T="pending_topup_timestamp",vt=300*1e3,xt=ne({__name:"SubscriptionPanel",setup(d){const{buildDocsUrl:v,docsPaths:x}=me(),y=Y(),{t:c,n:h}=F(),{isActiveSubscription:_,isCancelled:$,formattedRenewalDate:C,formattedEndDate:E,subscriptionTier:k,subscriptionTierName:H,subscriptionStatus:I,isYearlySubscription:D,handleInvoiceHistory:O}=z(),{show:V}=Le(),L=o(()=>{const t=k.value;return t?fe[t]??"standard":_e}),K=o(()=>Se(L.value,D.value)),j=o(()=>`${ge()}/profile/usage`),R=o(()=>{if(!I.value?.renewal_date)return"";const t=new Date(I.value.renewal_date),r=String(t.getDate()).padStart(2,"0");return`${String(t.getMonth()+1).padStart(2,"0")}/${r}/${String(t.getFullYear()).slice(-2)}`}),M=o(()=>D.value?c("subscription.creditsRemainingThisYear",{date:R.value}):c("subscription.creditsRemainingThisMonth",{date:R.value})),q=o(()=>{const t=ve(L.value);return h(D.value?t*12:t)}),X=o(()=>`${W.value} / ${q.value}`),G=o(()=>{const t=L.value,r=[{key:"maxDuration",type:"metric",value:c(`subscription.maxDuration.${t}`),label:c("subscription.maxDurationLabel")},{key:"gpu",type:"feature",label:c("subscription.gpuLabel")},{key:"addCredits",type:"feature",label:c("subscription.addCreditsLabel")}];return be(t).customLoRAs&&r.push({key:"customLoRAs",type:"feature",label:c("subscription.customLoRAsLabel")}),r}),{totalCredits:J,monthlyBonusCredits:W,prepaidCredits:Q,isLoadingBalance:S}=Ae(),{isLoadingSupport:Z,handleAddApiCredits:ee,handleMessageSupport:te,handleRefresh:P,handleLearnMoreClick:se}=Pe();function N(){const t=localStorage.getItem(T);if(!t)return;const r=parseInt(t,10);if(Date.now()-r>vt){localStorage.removeItem(T);return}P(),localStorage.removeItem(T)}n(N,"handleWindowFocus"),U(()=>{window.addEventListener("focus",N)}),le(()=>{window.removeEventListener("focus",N)});const ae=n(()=>{window.open(v(x.partnerNodesPricing,{includeLocale:!0}),"_blank")},"handleOpenPartnerNodesInfo");return(t,r)=>(i(),m(s(ie),{value:"PlanCredits",class:"subscription-container h-full"},{default:u(()=>[e("div",Be,[e("div",Te,[e("span",Ee,a(s(_)?t.$t("subscription.title"):t.$t("subscription.titleUnsubscribed")),1),e("div",Ie,[g(De,{"reverse-order":"","background-color":"var(--p-dialog-background)"})])]),e("div",Re,[e("div",Me,[e("div",null,[e("div",Ne,[e("div",Ue,[e("div",Fe,a(s(H)),1),e("div",Ye,[e("span",ze,"$"+a(K.value),1),e("span",He,a(t.$t("subscription.perMonth")),1)]),s(_)?(i(),l("div",Oe,[s($)?(i(),l(B,{key:0},[p(a(t.$t("subscription.expiresDate",{date:s(E)})),1)],64)):(i(),l(B,{key:1},[p(a(t.$t("subscription.renewsDate",{date:s(C)})),1)],64))])):w("",!0)]),s(_)?(i(),m(f,{key:0,variant:"secondary",class:"ml-auto rounded-lg px-4 py-2 text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected",onClick:r[0]||(r[0]=async()=>{await s(y).accessBillingPortal()})},{default:u(()=>[p(a(t.$t("subscription.manageSubscription")),1)]),_:1})):w("",!0),s(_)?(i(),m(f,{key:1,variant:"primary",class:"rounded-lg px-4 py-2 text-sm font-normal text-text-primary",onClick:s(V)},{default:u(()=>[p(a(t.$t("subscription.upgradePlan")),1)]),_:1},8,["onClick"])):(i(),m($e,{key:2,label:t.$t("subscription.subscribeNow"),size:"sm",fluid:!1,class:"text-xs",onSubscribed:s(P)},null,8,["label","onSubscribed"]))])]),e("div",Ve,[e("div",Ke,[e("div",je,[e("div",{class:oe(s(pe)("relative flex flex-col gap-6 rounded-2xl p-5","bg-modal-panel-background"))},[g(f,{variant:"muted-textonly",size:"icon-sm",class:"absolute top-4 right-4",loading:s(S),onClick:s(P)},{default:u(()=>r[1]||(r[1]=[e("i",{class:"pi pi-sync text-text-secondary text-sm"},null,-1)])),_:1},8,["loading","onClick"]),e("div",qe,[e("div",Xe,a(t.$t("subscription.totalCredits")),1),s(S)?(i(),m(s(A),{key:0,width:"8rem",height:"2rem"})):(i(),l("div",Ge,a(s(J)),1))]),e("table",Je,[e("tbody",null,[e("tr",null,[e("td",We,[s(S)?(i(),m(s(A),{key:0,width:"5rem",height:"1rem"})):(i(),l("span",Qe,a(X.value),1))]),e("td",{class:"align-middle",title:M.value},a(M.value),9,Ze)]),e("tr",null,[e("td",et,[s(S)?(i(),m(s(A),{key:0,width:"3rem",height:"1rem"})):(i(),l("span",tt,a(s(Q)),1))]),e("td",{class:"align-middle",title:t.$t("subscription.creditsYouveAdded")},a(t.$t("subscription.creditsYouveAdded")),9,st)])])]),e("div",at,[e("a",{href:j.value,target:"_blank",rel:"noopener noreferrer",class:"text-sm underline text-center text-muted"},a(t.$t("subscription.viewUsageHistory")),9,rt),s(_)?(i(),m(f,{key:0,variant:"secondary",class:"p-2 min-h-8 rounded-lg text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected",onClick:s(ee)},{default:u(()=>[p(a(t.$t("subscription.addCredits")),1)]),_:1},8,["onClick"])):w("",!0)])],2)])]),e("div",it,[e("div",ot,a(t.$t("subscription.yourPlanIncludes")),1),e("div",nt,[(i(!0),l(B,null,de(G.value,b=>(i(),l("div",{key:b.key,class:"flex items-center gap-2 py-2"},[b.type==="feature"?(i(),l("i",lt)):b.type==="metric"&&b.value?(i(),l("span",dt,a(b.value),1)):w("",!0),e("span",ct,a(b.label),1)]))),128))])])])]),e("div",ut,[r[2]||(r[2]=e("i",{class:"pi pi-external-link text-muted"},null,-1)),e("a",pt,a(t.$t("subscription.viewMoreDetailsPlans")),1)])]),e("div",mt,[e("div",ft,[g(f,{variant:"muted-textonly",class:"text-xs text-text-secondary",onClick:s(se)},{default:u(()=>[r[3]||(r[3]=e("i",{class:"pi pi-question-circle text-text-secondary text-xs"},null,-1)),p(" "+a(t.$t("subscription.learnMore")),1)]),_:1},8,["onClick"]),g(f,{variant:"muted-textonly",class:"text-xs text-text-secondary",onClick:ae},{default:u(()=>[r[4]||(r[4]=e("i",{class:"pi pi-question-circle text-text-secondary text-xs"},null,-1)),p(" "+a(t.$t("subscription.partnerNodesCredits")),1)]),_:1}),g(f,{variant:"muted-textonly",class:"text-xs text-text-secondary",loading:s(Z),onClick:s(te)},{default:u(()=>[r[5]||(r[5]=e("i",{class:"pi pi-comment text-text-secondary text-xs"},null,-1)),p(" "+a(t.$t("subscription.messageSupport")),1)]),_:1},8,["loading","onClick"])]),g(f,{variant:"muted-textonly",class:"text-xs text-text-secondary",onClick:s(O)},{default:u(()=>[p(a(t.$t("subscription.invoiceHistory"))+" ",1),r[6]||(r[6]=e("i",{class:"pi pi-external-link text-text-secondary text-xs"},null,-1))]),_:1},8,["onClick"])])])]),_:1}))}}),Ut=we(xt,[["__scopeId","data-v-0384e6f9"]]);export{Ut as default};
1
+ var re=Object.defineProperty;var n=(d,v)=>re(d,"name",{value:v,configurable:!0});import{f as ie,lt as A}from"./vendor-primevue-DcMRXJN3.js";import{$o as a,Ba as e,Do as u,Ha as w,Jo as s,Ka as p,Pa as B,Ua as l,Va as m,Xo as oe,Ya as ne,co as U,do as i,oo as le,po as de,qa as g,za as o,zo as ce}from"./vendor-other-DlQF6V2E.js";import{n as F}from"./vendor-vue-D9IUuwPJ.js";import{Lt as ue}from"./api-Dwq2LQIW.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import"./useErrorHandling-Cfa5N_7c.js";import{t as pe}from"./src-DTrob8OL.js";import{t as f}from"./Button-Do2I1OAA.js";import{k as me}from"./PanelTemplate-BJda9e5J.js";import{Ir as fe,Lr as ve,Mr as Y,Nr as z,Or as xe,Pr as _e,Rr as be,Ur as ge,Vr as ye,ft as he,o as ke,t as Ce,zr as Se}from"./dialogService-YG0RH337.js";import{t as we}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 $e}from"./SubscribeButton-DZBycfCA.js";import{t as De}from"./CloudBadge-B4nmLus2.js";import{t as Le}from"./useSubscriptionDialog-792qfEJ2.js";function Pe(){const d=Ce(),v=Y(),x=xe(),y=he(),{fetchStatus:c}=z(),h=ce(!1);U(()=>{C()});const _=n(()=>{d.showTopUpCreditsDialog()},"handleAddApiCredits"),$=n(async()=>{try{h.value=!0,ue&&y?.trackHelpResourceClicked({resource_type:"help_feedback",is_external:!0,source:"subscription"}),await x.execute("Comfy.ContactSupport")}catch(k){console.error("[useSubscriptionActions] Error contacting support:",k)}finally{h.value=!1}},"handleMessageSupport"),C=n(async()=>{try{await Promise.all([v.fetchBalance(),c()])}catch(k){console.error("[useSubscriptionActions] Error refreshing data:",k)}},"handleRefresh");return{isLoadingSupport:h,handleAddApiCredits:_,handleMessageSupport:$,handleRefresh:C,handleLearnMoreClick:n(()=>{window.open("https://docs.comfy.org/get_started/cloud","_blank")},"handleLearnMoreClick")}}n(Pe,"useSubscriptionActions");function Ae(){const d=ye(),{locale:v}=F(),x=n(y=>ke({cents:y??0,locale:v.value,numberOptions:{minimumFractionDigits:0,maximumFractionDigits:0}}),"formatBalance");return{totalCredits:o(()=>x(d.balance?.amount_micros)),monthlyBonusCredits:o(()=>x(d.balance?.cloud_credit_balance_micros)),prepaidCredits:o(()=>x(d.balance?.prepaid_balance_micros)),isLoadingBalance:o(()=>d.isFetchingBalance)}}n(Ae,"useSubscriptionCredits");var Be={class:"flex h-full flex-col gap-6"},Te={class:"flex items-center gap-2"},Ee={class:"text-2xl font-inter font-semibold leading-tight"},Ie={class:"pt-1"},Re={class:"grow overflow-auto"},Me={class:"rounded-2xl border border-interface-stroke p-6"},Ne={class:"flex items-center justify-between gap-2"},Ue={class:"flex flex-col gap-2"},Fe={class:"text-sm font-bold text-text-primary"},Ye={class:"flex items-baseline gap-1 font-inter font-semibold"},ze={class:"text-2xl"},He={class:"text-base"},Oe={key:0,class:"text-sm text-text-secondary"},Ve={class:"flex flex-col lg:flex-row gap-6 pt-9"},Ke={class:"flex flex-col shrink-0"},je={class:"flex flex-col gap-3"},qe={class:"flex flex-col gap-2"},Xe={class:"text-sm text-muted"},Ge={key:1,class:"text-2xl font-bold"},Je={class:"text-sm text-muted"},We={class:"pr-4 font-bold text-left align-middle"},Qe={key:1},Ze=["title"],et={class:"pr-4 font-bold text-left align-middle"},tt={key:1},st=["title"],at={class:"flex items-center justify-between"},rt=["href"],it={class:"flex flex-col gap-2"},ot={class:"text-sm text-text-primary"},nt={class:"flex flex-col gap-0"},lt={key:0,class:"pi pi-check text-xs text-text-primary"},dt={key:1,class:"text-sm font-normal whitespace-nowrap text-text-primary"},ct={class:"text-sm text-muted"},ut={class:"flex items-center gap-2 py-4"},pt={href:"https://www.comfy.org/cloud/pricing",target:"_blank",rel:"noopener noreferrer",class:"text-sm underline hover:opacity-80 text-muted"},mt={class:"flex items-center justify-between border-t border-interface-stroke pt-3"},ft={class:"flex gap-2"},T="pending_topup_timestamp",vt=300*1e3,xt=ne({__name:"SubscriptionPanel",setup(d){const{buildDocsUrl:v,docsPaths:x}=me(),y=Y(),{t:c,n:h}=F(),{isActiveSubscription:_,isCancelled:$,formattedRenewalDate:C,formattedEndDate:E,subscriptionTier:k,subscriptionTierName:H,subscriptionStatus:I,isYearlySubscription:D,handleInvoiceHistory:O}=z(),{show:V}=Le(),L=o(()=>{const t=k.value;return t?fe[t]??"standard":_e}),K=o(()=>Se(L.value,D.value)),j=o(()=>`${ge()}/profile/usage`),R=o(()=>{if(!I.value?.renewal_date)return"";const t=new Date(I.value.renewal_date),r=String(t.getDate()).padStart(2,"0");return`${String(t.getMonth()+1).padStart(2,"0")}/${r}/${String(t.getFullYear()).slice(-2)}`}),M=o(()=>D.value?c("subscription.creditsRemainingThisYear",{date:R.value}):c("subscription.creditsRemainingThisMonth",{date:R.value})),q=o(()=>{const t=ve(L.value);return h(D.value?t*12:t)}),X=o(()=>`${W.value} / ${q.value}`),G=o(()=>{const t=L.value,r=[{key:"maxDuration",type:"metric",value:c(`subscription.maxDuration.${t}`),label:c("subscription.maxDurationLabel")},{key:"gpu",type:"feature",label:c("subscription.gpuLabel")},{key:"addCredits",type:"feature",label:c("subscription.addCreditsLabel")}];return be(t).customLoRAs&&r.push({key:"customLoRAs",type:"feature",label:c("subscription.customLoRAsLabel")}),r}),{totalCredits:J,monthlyBonusCredits:W,prepaidCredits:Q,isLoadingBalance:S}=Ae(),{isLoadingSupport:Z,handleAddApiCredits:ee,handleMessageSupport:te,handleRefresh:P,handleLearnMoreClick:se}=Pe();function N(){const t=localStorage.getItem(T);if(!t)return;const r=parseInt(t,10);if(Date.now()-r>vt){localStorage.removeItem(T);return}P(),localStorage.removeItem(T)}n(N,"handleWindowFocus"),U(()=>{window.addEventListener("focus",N)}),le(()=>{window.removeEventListener("focus",N)});const ae=n(()=>{window.open(v(x.partnerNodesPricing,{includeLocale:!0}),"_blank")},"handleOpenPartnerNodesInfo");return(t,r)=>(i(),m(s(ie),{value:"PlanCredits",class:"subscription-container h-full"},{default:u(()=>[e("div",Be,[e("div",Te,[e("span",Ee,a(s(_)?t.$t("subscription.title"):t.$t("subscription.titleUnsubscribed")),1),e("div",Ie,[g(De,{"reverse-order":"","background-color":"var(--p-dialog-background)"})])]),e("div",Re,[e("div",Me,[e("div",null,[e("div",Ne,[e("div",Ue,[e("div",Fe,a(s(H)),1),e("div",Ye,[e("span",ze,"$"+a(K.value),1),e("span",He,a(t.$t("subscription.perMonth")),1)]),s(_)?(i(),l("div",Oe,[s($)?(i(),l(B,{key:0},[p(a(t.$t("subscription.expiresDate",{date:s(E)})),1)],64)):(i(),l(B,{key:1},[p(a(t.$t("subscription.renewsDate",{date:s(C)})),1)],64))])):w("",!0)]),s(_)?(i(),m(f,{key:0,variant:"secondary",class:"ml-auto rounded-lg px-4 py-2 text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected",onClick:r[0]||(r[0]=async()=>{await s(y).accessBillingPortal()})},{default:u(()=>[p(a(t.$t("subscription.manageSubscription")),1)]),_:1})):w("",!0),s(_)?(i(),m(f,{key:1,variant:"primary",class:"rounded-lg px-4 py-2 text-sm font-normal text-text-primary",onClick:s(V)},{default:u(()=>[p(a(t.$t("subscription.upgradePlan")),1)]),_:1},8,["onClick"])):(i(),m($e,{key:2,label:t.$t("subscription.subscribeNow"),size:"sm",fluid:!1,class:"text-xs",onSubscribed:s(P)},null,8,["label","onSubscribed"]))])]),e("div",Ve,[e("div",Ke,[e("div",je,[e("div",{class:oe(s(pe)("relative flex flex-col gap-6 rounded-2xl p-5","bg-modal-panel-background"))},[g(f,{variant:"muted-textonly",size:"icon-sm",class:"absolute top-4 right-4",loading:s(S),onClick:s(P)},{default:u(()=>r[1]||(r[1]=[e("i",{class:"pi pi-sync text-text-secondary text-sm"},null,-1)])),_:1},8,["loading","onClick"]),e("div",qe,[e("div",Xe,a(t.$t("subscription.totalCredits")),1),s(S)?(i(),m(s(A),{key:0,width:"8rem",height:"2rem"})):(i(),l("div",Ge,a(s(J)),1))]),e("table",Je,[e("tbody",null,[e("tr",null,[e("td",We,[s(S)?(i(),m(s(A),{key:0,width:"5rem",height:"1rem"})):(i(),l("span",Qe,a(X.value),1))]),e("td",{class:"align-middle",title:M.value},a(M.value),9,Ze)]),e("tr",null,[e("td",et,[s(S)?(i(),m(s(A),{key:0,width:"3rem",height:"1rem"})):(i(),l("span",tt,a(s(Q)),1))]),e("td",{class:"align-middle",title:t.$t("subscription.creditsYouveAdded")},a(t.$t("subscription.creditsYouveAdded")),9,st)])])]),e("div",at,[e("a",{href:j.value,target:"_blank",rel:"noopener noreferrer",class:"text-sm underline text-center text-muted"},a(t.$t("subscription.viewUsageHistory")),9,rt),s(_)?(i(),m(f,{key:0,variant:"secondary",class:"p-2 min-h-8 rounded-lg text-sm font-normal text-text-primary bg-interface-menu-component-surface-selected",onClick:s(ee)},{default:u(()=>[p(a(t.$t("subscription.addCredits")),1)]),_:1},8,["onClick"])):w("",!0)])],2)])]),e("div",it,[e("div",ot,a(t.$t("subscription.yourPlanIncludes")),1),e("div",nt,[(i(!0),l(B,null,de(G.value,b=>(i(),l("div",{key:b.key,class:"flex items-center gap-2 py-2"},[b.type==="feature"?(i(),l("i",lt)):b.type==="metric"&&b.value?(i(),l("span",dt,a(b.value),1)):w("",!0),e("span",ct,a(b.label),1)]))),128))])])])]),e("div",ut,[r[2]||(r[2]=e("i",{class:"pi pi-external-link text-muted"},null,-1)),e("a",pt,a(t.$t("subscription.viewMoreDetailsPlans")),1)])]),e("div",mt,[e("div",ft,[g(f,{variant:"muted-textonly",class:"text-xs text-text-secondary",onClick:s(se)},{default:u(()=>[r[3]||(r[3]=e("i",{class:"pi pi-question-circle text-text-secondary text-xs"},null,-1)),p(" "+a(t.$t("subscription.learnMore")),1)]),_:1},8,["onClick"]),g(f,{variant:"muted-textonly",class:"text-xs text-text-secondary",onClick:ae},{default:u(()=>[r[4]||(r[4]=e("i",{class:"pi pi-question-circle text-text-secondary text-xs"},null,-1)),p(" "+a(t.$t("subscription.partnerNodesCredits")),1)]),_:1}),g(f,{variant:"muted-textonly",class:"text-xs text-text-secondary",loading:s(Z),onClick:s(te)},{default:u(()=>[r[5]||(r[5]=e("i",{class:"pi pi-comment text-text-secondary text-xs"},null,-1)),p(" "+a(t.$t("subscription.messageSupport")),1)]),_:1},8,["loading","onClick"])]),g(f,{variant:"muted-textonly",class:"text-xs text-text-secondary",onClick:s(O)},{default:u(()=>[p(a(t.$t("subscription.invoiceHistory"))+" ",1),r[6]||(r[6]=e("i",{class:"pi pi-external-link text-text-secondary text-xs"},null,-1))]),_:1},8,["onClick"])])])]),_:1}))}}),Ut=we(xt,[["__scopeId","data-v-0384e6f9"]]);export{Ut as default};
2
2
 
3
- //# sourceMappingURL=SubscriptionPanel-D9uv7z8f.js.map
3
+ //# sourceMappingURL=SubscriptionPanel-B6txX4Vm.js.map