diff --git a/src/components/dialog/content/ApiNodesSignInContent.vue b/src/components/dialog/content/ApiNodesSignInContent.vue index 0e12f8a61..a3139daaf 100644 --- a/src/components/dialog/content/ApiNodesSignInContent.vue +++ b/src/components/dialog/content/ApiNodesSignInContent.vue @@ -29,7 +29,10 @@ import Button from 'primevue/button' import { useI18n } from 'vue-i18n' +import { useExternalLink } from '@/composables/useExternalLink' + const { t } = useI18n() +const { buildDocsUrl } = useExternalLink() const { apiNodeNames, onLogin, onCancel } = defineProps<{ apiNodeNames: string[] @@ -38,6 +41,9 @@ const { apiNodeNames, onLogin, onCancel } = defineProps<{ }>() const handleLearnMoreClick = () => { - window.open('https://docs.comfy.org/tutorials/api-nodes/faq', '_blank') + window.open( + buildDocsUrl('/tutorials/api-nodes/faq', { includeLocale: true }), + '_blank' + ) } diff --git a/src/components/dialog/content/setting/CreditsPanel.vue b/src/components/dialog/content/setting/CreditsPanel.vue index 614312ad4..a7e0febcb 100644 --- a/src/components/dialog/content/setting/CreditsPanel.vue +++ b/src/components/dialog/content/setting/CreditsPanel.vue @@ -123,6 +123,7 @@ import { computed, ref, watch } from 'vue' import UserCredit from '@/components/common/UserCredit.vue' import UsageLogsTable from '@/components/dialog/content/setting/UsageLogsTable.vue' import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' +import { useExternalLink } from '@/composables/useExternalLink' import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription' import { useTelemetry } from '@/platform/telemetry' import { useDialogService } from '@/services/dialogService' @@ -137,6 +138,7 @@ interface CreditHistoryItemData { isPositive: boolean } +const { buildDocsUrl } = useExternalLink() const dialogService = useDialogService() const authStore = useFirebaseAuthStore() const authActions = useFirebaseAuthActions() @@ -183,12 +185,17 @@ const handleMessageSupport = async () => { } const handleFaqClick = () => { - window.open('https://docs.comfy.org/tutorials/api-nodes/faq', '_blank') + window.open( + buildDocsUrl('/tutorials/api-nodes/faq', { includeLocale: true }), + '_blank' + ) } const handleOpenPartnerNodesInfo = () => { window.open( - 'https://docs.comfy.org/tutorials/api-nodes/overview#api-nodes', + buildDocsUrl('/tutorials/api-nodes/overview#api-nodes', { + includeLocale: true + }), '_blank' ) } diff --git a/src/components/helpcenter/HelpCenterMenuContent.vue b/src/components/helpcenter/HelpCenterMenuContent.vue index e028a3eb1..788d84c81 100644 --- a/src/components/helpcenter/HelpCenterMenuContent.vue +++ b/src/components/helpcenter/HelpCenterMenuContent.vue @@ -143,6 +143,7 @@ import type { CSSProperties, Component } from 'vue' import { useI18n } from 'vue-i18n' import PuzzleIcon from '@/components/icons/PuzzleIcon.vue' +import { useExternalLink } from '@/composables/useExternalLink' import { isCloud } from '@/platform/distribution/types' import { useSettingStore } from '@/platform/settings/settingStore' import { useTelemetry } from '@/platform/telemetry' @@ -168,15 +169,6 @@ interface MenuItem { } // Constants -const EXTERNAL_LINKS = { - DOCS: 'https://docs.comfy.org/', - DISCORD: 'https://www.comfy.org/discord', - GITHUB: 'https://github.com/comfyanonymous/ComfyUI', - DESKTOP_GUIDE_WINDOWS: 'https://docs.comfy.org/installation/desktop/windows', - DESKTOP_GUIDE_MACOS: 'https://docs.comfy.org/installation/desktop/macos', - UPDATE_GUIDE: 'https://docs.comfy.org/installation/update_comfyui' -} as const - const TIME_UNITS = { MINUTE: 60 * 1000, HOUR: 60 * 60 * 1000, @@ -193,7 +185,8 @@ const SUBMENU_CONFIG = { } as const // Composables -const { t, locale } = useI18n() +const { t } = useI18n() +const { staticUrls, buildDocsUrl } = useExternalLink() const releaseStore = useReleaseStore() const commandStore = useCommandStore() const settingStore = useSettingStore() @@ -230,11 +223,12 @@ const moreItems = computed(() => { visible: isElectron(), action: () => { trackResourceClick('docs', true) - const docsUrl = - electronAPI().getPlatform() === 'darwin' - ? EXTERNAL_LINKS.DESKTOP_GUIDE_MACOS - : EXTERNAL_LINKS.DESKTOP_GUIDE_WINDOWS - openExternalLink(docsUrl) + openExternalLink( + buildDocsUrl('/installation/desktop', { + includeLocale: true, + platform: true + }) + ) emit('close') } }, @@ -286,7 +280,7 @@ const menuItems = computed(() => { label: t('helpCenter.docs'), action: () => { trackResourceClick('docs', true) - openExternalLink(EXTERNAL_LINKS.DOCS) + openExternalLink(buildDocsUrl('/', { includeLocale: true })) emit('close') } }, @@ -297,7 +291,7 @@ const menuItems = computed(() => { label: 'Discord', action: () => { trackResourceClick('discord', true) - openExternalLink(EXTERNAL_LINKS.DISCORD) + openExternalLink(staticUrls.discord) emit('close') } }, @@ -308,7 +302,7 @@ const menuItems = computed(() => { label: t('helpCenter.github'), action: () => { trackResourceClick('github', true) - openExternalLink(EXTERNAL_LINKS.GITHUB) + openExternalLink(staticUrls.github) emit('close') } }, @@ -533,25 +527,19 @@ const onReleaseClick = (release: ReleaseNote): void => { trackResourceClick('release_notes', true) void releaseStore.handleShowChangelog(release.version) const versionAnchor = formatVersionAnchor(release.version) - const changelogUrl = `${getChangelogUrl()}#${versionAnchor}` + const changelogUrl = `${buildDocsUrl('/changelog', { includeLocale: true })}#${versionAnchor}` openExternalLink(changelogUrl) emit('close') } const onUpdate = (_: ReleaseNote): void => { trackResourceClick('docs', true) - openExternalLink(EXTERNAL_LINKS.UPDATE_GUIDE) + openExternalLink( + buildDocsUrl('/installation/update_comfyui', { includeLocale: true }) + ) emit('close') } -// Generate language-aware changelog URL -const getChangelogUrl = (): string => { - const isChineseLocale = locale.value === 'zh' - return isChineseLocale - ? 'https://docs.comfy.org/zh-CN/changelog' - : 'https://docs.comfy.org/changelog' -} - // Lifecycle onMounted(async () => { telemetry?.trackHelpCenterOpened({ source: 'sidebar' }) diff --git a/src/components/topbar/CurrentUserPopover.vue b/src/components/topbar/CurrentUserPopover.vue index d11418e5e..93d7e9223 100644 --- a/src/components/topbar/CurrentUserPopover.vue +++ b/src/components/topbar/CurrentUserPopover.vue @@ -101,6 +101,7 @@ import UserAvatar from '@/components/common/UserAvatar.vue' import UserCredit from '@/components/common/UserCredit.vue' import { useCurrentUser } from '@/composables/auth/useCurrentUser' import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' +import { useExternalLink } from '@/composables/useExternalLink' import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue' import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription' import { isCloud } from '@/platform/distribution/types' @@ -111,6 +112,8 @@ const emit = defineEmits<{ close: [] }>() +const { buildDocsUrl } = useExternalLink() + const planSettingsLabel = isCloud ? 'settingsCategories.PlanCredits' : 'settingsCategories.Credits' @@ -145,7 +148,9 @@ const handleTopUp = () => { const handleOpenPartnerNodesInfo = () => { window.open( - 'https://docs.comfy.org/tutorials/api-nodes/overview#api-nodes', + buildDocsUrl('/tutorials/api-nodes/overview#api-nodes', { + includeLocale: true + }), '_blank' ) emit('close') diff --git a/src/components/topbar/LoginButton.vue b/src/components/topbar/LoginButton.vue index f2812e61d..cefdb6dc9 100644 --- a/src/components/topbar/LoginButton.vue +++ b/src/components/topbar/LoginButton.vue @@ -22,7 +22,7 @@
{{ t('auth.loginButton.tooltipHelp') }}
{{ t('auth.loginButton.tooltipLearnMore') }} | null>(null) let hideTimeout: ReturnType | null = null let showTimeout: ReturnType | null = null diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index f691ff7f1..7662fd0a6 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -1,6 +1,7 @@ import { useCurrentUser } from '@/composables/auth/useCurrentUser' import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems' +import { useExternalLink } from '@/composables/useExternalLink' import { useModelSelectorDialog } from '@/composables/useModelSelectorDialog' import { DEFAULT_DARK_COLOR_PALETTE, @@ -76,6 +77,7 @@ export function useCoreCommands(): ComfyCommand[] { const canvasStore = useCanvasStore() const executionStore = useExecutionStore() const telemetry = useTelemetry() + const { staticUrls, buildDocsUrl } = useExternalLink() const bottomPanelStore = useBottomPanelStore() @@ -761,10 +763,7 @@ export function useCoreCommands(): ComfyCommand[] { is_external: true, source: 'menu' }) - window.open( - 'https://github.com/comfyanonymous/ComfyUI/issues', - '_blank' - ) + window.open(staticUrls.githubIssues, '_blank') } }, { @@ -779,7 +778,7 @@ export function useCoreCommands(): ComfyCommand[] { is_external: true, source: 'menu' }) - window.open('https://docs.comfy.org/', '_blank') + window.open(buildDocsUrl('/', { includeLocale: true }), '_blank') } }, { @@ -794,7 +793,7 @@ export function useCoreCommands(): ComfyCommand[] { is_external: true, source: 'menu' }) - window.open('https://www.comfy.org/discord', '_blank') + window.open(staticUrls.discord, '_blank') } }, { @@ -861,7 +860,7 @@ export function useCoreCommands(): ComfyCommand[] { is_external: true, source: 'menu' }) - window.open('https://forum.comfy.org/', '_blank') + window.open(staticUrls.forum, '_blank') } }, { diff --git a/src/composables/useExternalLink.ts b/src/composables/useExternalLink.ts new file mode 100644 index 000000000..59cb9f689 --- /dev/null +++ b/src/composables/useExternalLink.ts @@ -0,0 +1,99 @@ +import { computed } from 'vue' + +import { electronAPI, isElectron } from '@/utils/envUtil' +import { useI18n } from 'vue-i18n' + +/** + * Composable for building docs.comfy.org URLs with automatic locale and platform detection + * + * @example + * ```ts + * const { buildDocsUrl } = useExternalLink() + * + * // Simple usage + * const changelogUrl = buildDocsUrl('/changelog', { includeLocale: true }) + * // => 'https://docs.comfy.org/zh-CN/changelog' (if Chinese) + * + * // With platform detection + * const desktopUrl = buildDocsUrl('/installation/desktop', { + * includeLocale: true, + * platform: true + * }) + * // => 'https://docs.comfy.org/zh-CN/installation/desktop/macos' (if Chinese + macOS) + * ``` + */ +export function useExternalLink() { + const { locale } = useI18n() + + const isChinese = computed(() => { + return locale.value === 'zh' || locale.value === 'zh-TW' + }) + + const platform = computed(() => { + if (!isElectron()) { + return null + } + + const electronPlatform = electronAPI().getPlatform() + return electronPlatform === 'darwin' ? 'macos' : 'windows' + }) + + /** + * Build a docs.comfy.org URL with optional locale and platform + * + * @param path - The path after the domain (e.g., '/installation/desktop') + * @param options - Options for building the URL + * @param options.includeLocale - Whether to include locale prefix (default: false) + * @param options.platform - Whether to include platform suffix (default: false) + * @returns The complete docs URL + * + * @example + * ```ts + * buildDocsUrl('/changelog') // => 'https://docs.comfy.org/changelog' + * buildDocsUrl('/changelog', { includeLocale: true }) // => 'https://docs.comfy.org/zh-CN/changelog' (if Chinese) + * buildDocsUrl('/installation/desktop', { includeLocale: true, platform: true }) + * // => 'https://docs.comfy.org/zh-CN/installation/desktop/macos' (if Chinese + macOS) + * ``` + */ + const buildDocsUrl = ( + path: string, + options: { + includeLocale?: boolean + platform?: boolean + } = {} + ): string => { + const { includeLocale = false, platform: includePlatform = false } = options + + let url = 'https://docs.comfy.org' + + if (includeLocale && isChinese.value) { + url += '/zh-CN' + } + + const normalizedPath = path.startsWith('/') ? path : `/${path}` + url += normalizedPath + + if (includePlatform && platform.value) { + url = url.endsWith('/') ? url : `${url}/` + url += platform.value + } + + return url + } + + const staticUrls = { + // Static external URLs + discord: 'https://www.comfy.org/discord', + github: 'https://github.com/comfyanonymous/ComfyUI', + githubIssues: 'https://github.com/comfyanonymous/ComfyUI/issues', + githubFrontend: 'https://github.com/Comfy-Org/ComfyUI_frontend', + githubElectron: 'https://github.com/Comfy-Org/electron', + forum: 'https://forum.comfy.org/', + comfyOrg: 'https://www.comfy.org/' + } + + return { + buildDocsUrl, + staticUrls + } +} diff --git a/src/extensions/core/electronAdapter.ts b/src/extensions/core/electronAdapter.ts index ac6239d08..9fec24414 100644 --- a/src/extensions/core/electronAdapter.ts +++ b/src/extensions/core/electronAdapter.ts @@ -1,5 +1,6 @@ import log from 'loglevel' +import { useExternalLink } from '@/composables/useExternalLink' import { PYTHON_MIRROR } from '@/constants/uvMirrors' import { t } from '@/i18n' import { useToastStore } from '@/platform/updates/common/toastStore' @@ -8,13 +9,6 @@ import { app } from '@/scripts/app' import { useDialogService } from '@/services/dialogService' import { checkMirrorReachable } from '@/utils/electronMirrorCheck' import { electronAPI as getElectronAPI, isElectron } from '@/utils/envUtil' - -// Desktop documentation URLs -const DESKTOP_DOCS = { - WINDOWS: 'https://docs.comfy.org/installation/desktop/windows', - MACOS: 'https://docs.comfy.org/installation/desktop/macos' -} as const - ;(async () => { if (!isElectron()) return @@ -22,6 +16,7 @@ const DESKTOP_DOCS = { const desktopAppVersion = await electronAPI.getElectronVersion() const workflowStore = useWorkflowStore() const toastStore = useToastStore() + const { staticUrls, buildDocsUrl } = useExternalLink() const onChangeRestartApp = (newValue: string, oldValue: string) => { // Add a delay to allow changes to take effect before restarting. @@ -165,11 +160,13 @@ const DESKTOP_DOCS = { label: 'Desktop User Guide', icon: 'pi pi-book', function() { - const docsUrl = - electronAPI.getPlatform() === 'darwin' - ? DESKTOP_DOCS.MACOS - : DESKTOP_DOCS.WINDOWS - window.open(docsUrl, '_blank') + window.open( + buildDocsUrl('/installation/desktop', { + includeLocale: true, + platform: true + }), + '_blank' + ) } }, { @@ -304,7 +301,7 @@ const DESKTOP_DOCS = { aboutPageBadges: [ { label: 'ComfyUI_desktop v' + desktopAppVersion, - url: 'https://github.com/Comfy-Org/electron', + url: staticUrls.githubElectron, icon: 'pi pi-github' } ] diff --git a/src/platform/cloud/subscription/components/SubscriptionPanel.vue b/src/platform/cloud/subscription/components/SubscriptionPanel.vue index 6105378ad..8dbf203ad 100644 --- a/src/platform/cloud/subscription/components/SubscriptionPanel.vue +++ b/src/platform/cloud/subscription/components/SubscriptionPanel.vue @@ -309,6 +309,7 @@ import Skeleton from 'primevue/skeleton' import TabPanel from 'primevue/tabpanel' import CloudBadge from '@/components/topbar/CloudBadge.vue' +import { useExternalLink } from '@/composables/useExternalLink' import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue' import SubscriptionBenefits from '@/platform/cloud/subscription/components/SubscriptionBenefits.vue' import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription' @@ -316,6 +317,8 @@ import { useSubscriptionActions } from '@/platform/cloud/subscription/composable import { useSubscriptionCredits } from '@/platform/cloud/subscription/composables/useSubscriptionCredits' import { cn } from '@/utils/tailwindUtil' +const { buildDocsUrl } = useExternalLink() + const { isActiveSubscription, isCancelled, @@ -340,7 +343,9 @@ const { const handleOpenPartnerNodesInfo = () => { window.open( - 'https://docs.comfy.org/tutorials/api-nodes/overview#api-nodes', + buildDocsUrl('/tutorials/api-nodes/overview#api-nodes', { + includeLocale: true + }), '_blank' ) } diff --git a/src/platform/updates/components/ReleaseNotificationToast.vue b/src/platform/updates/components/ReleaseNotificationToast.vue index 94f21a554..4b48b6fe0 100644 --- a/src/platform/updates/components/ReleaseNotificationToast.vue +++ b/src/platform/updates/components/ReleaseNotificationToast.vue @@ -46,14 +46,14 @@