[PyCharm] SingleDistributive (feat): Reworked Django and JS promo pages in NPW in PyCharm Community and Standard. #PY-78995 Ready For Merge

1. In Community on promo buttons and text titles now have "Pro" suffix
2. In Standard, when a user has no license, we started to show promo pages.
3. Promo pages in Standard are looking closer to sketches.

GitOrigin-RevId: f95c8ab0dd83c86b3068e29a29353b0b9f1b87d7
This commit is contained in:
Nikita Pavlenko
2025-09-10 11:40:56 +00:00
committed by intellij-monorepo-bot
parent 187077d9da
commit ce3e5aada4
14 changed files with 76 additions and 29 deletions
@@ -79,7 +79,7 @@ sealed interface PluginAdvertiserService {
@Suppress("HardCodedStringLiteral", "DialogTitleCapitalization")
val pyCharmProfessional: SuggestedIde = SuggestedIde(
name = "PyCharm",
name = "PyCharm Pro",
productCode = "PY",
defaultDownloadUrl = "https://www.jetbrains.com/pycharm/download/",
platformSpecificDownloadUrlTemplate = "https://www.jetbrains.com/pycharm/download/download-thanks.html?platform={type}",
@@ -61,7 +61,7 @@ class PluginsAdvertiserTest {
fun suggestedIdeInPyCharmCommunity() = runBlocking {
preparePluginCache("*.js" to PluginData("JavaScript"))
val suggestion = getSuggestionData(projectRule.project, "PC", "foo.js", PlainTextFileType.INSTANCE)
assertEquals(listOf("PyCharm"), suggestion!!.suggestedIdes.map { it.name })
assertEquals(listOf("PyCharm Pro"), suggestion!!.suggestedIdes.map { it.name })
}
@Test
+6 -2
View File
@@ -9,11 +9,15 @@ jvm_resources(
jvm_library(
name = "customization.shared",
module_name = "intellij.pycharm.community.customization.shared",
visibility = ["//visibility:public"],
srcs = glob([], allow_empty = True),
srcs = glob(["src/**/*.kt", "src/**/*.java", "src/**/*.form"], allow_empty = True),
deps = [
"//python/ide/impl",
"//platform/platform-impl:ide-impl",
],
runtime_deps = [
":customization.shared_resources",
"//python/ide/impl",
"//python/ide/implTraining/intellij.pycharm.community.ide.implTraining:pycharm-community-ide-implTraining",
"//platform/whatsNew",
]
@@ -4,11 +4,13 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="intellij.pycharm.community.ide.impl" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.pycharm.community.ide.impl" />
<orderEntry type="module" module-name="intellij.pycharm.community.ide.implTraining" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.platform.whatsNew" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
</component>
</module>
@@ -0,0 +1,11 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.pycharm.community.customization.shared // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
import com.intellij.icons.AllIcons
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PromoFeatureListItem
import com.intellij.pycharm.community.ide.impl.PyCharmCommunityCustomizationBundle
val djangoPromoFeatureList: List<PromoFeatureListItem> = listOf(
PromoFeatureListItem(AllIcons.Actions.ReformatCode, PyCharmCommunityCustomizationBundle.message("feature.django.code")),
PromoFeatureListItem(AllIcons.FileTypes.Html, PyCharmCommunityCustomizationBundle.message("feature.django.djangoTemplates")),
PromoFeatureListItem(AllIcons.General.Web, PyCharmCommunityCustomizationBundle.message("feature.django.endpoints"))
)
@@ -0,0 +1,12 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.pycharm.community.customization.shared // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
import com.intellij.icons.AllIcons
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.FeaturePromoBundle
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PromoFeatureListItem
val javaScriptPromoFeatureList: List<PromoFeatureListItem> = listOf(
PromoFeatureListItem(AllIcons.Actions.ReformatCode, FeaturePromoBundle.message("feature.javascript.code")),
PromoFeatureListItem(AllIcons.Actions.SuggestedRefactoringBulb, FeaturePromoBundle.message("feature.javascript.refactor")),
@Suppress("DialogTitleCapitalization") // Properly capitalized, React and Vue are names.
PromoFeatureListItem(AllIcons.FileTypes.UiForm, FeaturePromoBundle.message("feature.javascript.frameworks"))
)
@@ -25,6 +25,7 @@ jvm_library(
"//platform/statistics",
"@lib//:http-client",
"//platform/core-ui",
"//python/ide/customization.shared",
],
runtime_deps = [":pycharm-community-ide-impl-promotion_resources"]
)
@@ -20,5 +20,6 @@
<orderEntry type="module" module-name="intellij.platform.statistics" />
<orderEntry type="library" name="http-client" level="project" />
<orderEntry type="module" module-name="intellij.platform.core.ui" />
<orderEntry type="module" module-name="intellij.pycharm.community.customization.shared" />
</component>
</module>
@@ -8,6 +8,7 @@ import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PluginAdver
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PromoFeaturePage
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PromoPages
import com.intellij.openapi.util.NlsSafe
import com.intellij.pycharm.community.customization.shared.djangoPromoFeatureList
import com.intellij.pycharm.community.ide.impl.PyCharmCommunityCustomizationBundle
import com.jetbrains.python.icons.PythonIcons
import com.jetbrains.python.newProjectWizard.promotion.PromoProjectGenerator
@@ -4,13 +4,17 @@ package com.intellij.pycharm.community.ide.impl.promotion
import com.intellij.icons.AllIcons
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.*
import com.intellij.openapi.util.NlsContexts
import com.intellij.pycharm.community.customization.shared.djangoPromoFeatureList
import com.intellij.pycharm.community.customization.shared.javaScriptPromoFeatureList
import com.intellij.pycharm.community.ide.impl.PyCharmCommunityCustomizationBundle
import com.jetbrains.python.icons.PythonIcons
import javax.swing.JComponent
private fun featurePage(@NlsContexts.Label title: String, items: List<PromoFeatureListItem>,
source: PromoEventSource,
topic: PromoTopic): JComponent {
private fun featurePage(
@NlsContexts.Label title: String, items: List<PromoFeatureListItem>,
source: PromoEventSource,
topic: PromoTopic,
): JComponent {
val page = PromoFeaturePage(
productIcon = PythonIcons.Python.Pycharm,
suggestedIde = PluginAdvertiserService.pyCharmProfessional,
@@ -39,7 +43,6 @@ internal fun databaseFeatures(source: PromoEventSource): JComponent = featurePag
)
internal fun javascriptFeatures(source: PromoEventSource, promoTopic: PromoTopic): JComponent {
@Suppress("DialogTitleCapitalization")
return featurePage(
FeaturePromoBundle.message("feature.javascript.description.html",
"https://www.jetbrains.com/help/pycharm/javascript-specific-guidelines.html"),
@@ -59,7 +62,6 @@ internal fun djangoFeatures(source: PromoEventSource): JComponent {
)
}
internal fun jupyterFeatures(source: PromoEventSource): JComponent {
return featurePage(
PyCharmCommunityCustomizationBundle.message("feature.jupyter.description.html",
@@ -87,16 +89,4 @@ internal fun sshFeatures(source: PromoEventSource): JComponent {
source,
PromoTopic.RemoteSSH
)
}
internal val djangoPromoFeatureList = listOf(
PromoFeatureListItem(AllIcons.Actions.ReformatCode, PyCharmCommunityCustomizationBundle.message("feature.django.code")),
PromoFeatureListItem(AllIcons.FileTypes.Html, PyCharmCommunityCustomizationBundle.message("feature.django.djangoTemplates")),
PromoFeatureListItem(AllIcons.General.Web, PyCharmCommunityCustomizationBundle.message("feature.django.endpoints"))
)
internal val javaScriptPromoFeatureList = listOf(
PromoFeatureListItem(AllIcons.Actions.ReformatCode, FeaturePromoBundle.message("feature.javascript.code")),
PromoFeatureListItem(AllIcons.Actions.SuggestedRefactoringBulb, FeaturePromoBundle.message("feature.javascript.refactor")),
PromoFeatureListItem(AllIcons.FileTypes.UiForm, FeaturePromoBundle.message("feature.javascript.frameworks"))
)
}
@@ -8,6 +8,7 @@ import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PluginAdver
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PromoFeaturePage
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PromoPages
import com.intellij.openapi.util.NlsSafe
import com.intellij.pycharm.community.customization.shared.javaScriptPromoFeatureList
import com.jetbrains.python.icons.PythonIcons
import com.jetbrains.python.newProjectWizard.promotion.PromoProjectGenerator
import javax.swing.Icon
@@ -32,7 +32,6 @@ internal class PyV3NewProjectStepAction : AbstractNewProjectStep<PyV3BaseProject
else -> ProjectSettingsStepBase(projectGenerator, callback)
}
override fun getActions(
generators: List<DirectoryProjectGenerator<*>>,
callback: AbstractCallback<PyV3BaseProjectSettings>,
@@ -1695,7 +1695,7 @@ python.uv.lockfile.out.of.sync=The lock file at `uv.lock` is out of sync
python.uv.update.lock=Update uv lock
pycharm.free.mode.upgrade.title=Upgrade to PyCharm Pro
pycharm.free.mode.upgrade.body=Unlock the full potential of your IDE! Boost productivity, <br/> work faster, and be more efficient with powerful development tools.<ul><li>Support for JavaScript, TypeScript, React and others</li><li>Advances support for Django, Flask and FastAPI</li><li>Remote development</li><li>Local and remote full-scale Jupyter notebooks</li></ul>
pycharm.free.mode.upgrade.body=Unlock the full potential of your IDE! Boost productivity, <br/> work faster and be more efficient with powerful development tools.<ul><li>Support for JavaScript, TypeScript, React and others</li><li style="margin-top: 10px;">Advances support for Django, Flask and FastAPI</li><li style="margin-top: 10px;">Remote development</li><li style="margin-top: 10px;">Local and remote full-scale Jupyter notebooks</li></ul>
pycharm.free.mode.upgrade.button=Purchase Subscription
python.packaging.sync.packages=Syncing project dependencies
python.sdk.read.only=Python SDK ''{0}'' is read only and operation cannot be performed.
@@ -1,19 +1,24 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python
import com.intellij.ide.BrowserUtil
import com.intellij.ide.browsers.BrowserLauncher
import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.PromoFeatureListItem
import com.intellij.ui.dsl.builder.*
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.ui.JBFont
import com.jetbrains.python.icons.PythonIcons
import org.jetbrains.annotations.Nls
import javax.swing.JPanel
/**
* Panel that suggests user to buy Pro version
* Panel that suggests user to buy a Pro version.
* By default, it shows a "pycharm.free.mode.upgrade.body" promo text.
* Could be customized by passing descriptionHtml and features list. In this case, default text will be substituted to features list.
*/
@RequiresEdt
internal fun createPromoPanel(): JPanel = panel {
fun createPromoPanel(@Nls descriptionHtml: String? = null, features: List<PromoFeatureListItem>? = null): JPanel = panel {
row {
icon(PythonIcons.Python.Pycharm32).resizableColumn().align(AlignX.RIGHT + AlignY.TOP)
panel {
@@ -22,10 +27,30 @@ internal fun createPromoPanel(): JPanel = panel {
text(PyBundle.message("pycharm.free.mode.upgrade.title")).align(AlignY.BOTTOM).applyToComponent {
font = JBFont.h1()
}
}.topGap(TopGap.SMALL)
if (descriptionHtml != null) {
row {
text(descriptionHtml) { BrowserUtil.browse(it.url.toExternalForm()) }
}
}
row {
text(PyBundle.message("pycharm.free.mode.upgrade.body")).align(AlignY.TOP).applyToComponent { putClientProperty(DslComponentProperty.VERTICAL_COMPONENT_GAP, VerticalComponentGap(top = false, bottom = false)) }
if (features == null) {
row {
text(PyBundle.message("pycharm.free.mode.upgrade.body")).align(AlignY.TOP).applyToComponent {
putClientProperty(DslComponentProperty.VERTICAL_COMPONENT_GAP, VerticalComponentGap(top = true, bottom = false))
}
}.topGap(TopGap.SMALL)
}
else {
features.forEach { feature ->
row {
icon(feature.icon).gap(RightGap.SMALL).align(AlignY.TOP)
text(feature.title)
}
}
}
row {
button(PyBundle.message("pycharm.free.mode.upgrade.button")) {
BrowserLauncher.instance.open("https://www.jetbrains.com/pycharm/buy/?utm_source=product&utm_medium=referral&utm_campaign=pycharm&utm_content=pro_upgrade&section=commercial&billing=yearly")