diff --git a/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties b/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties index 79ecca81754..c4eaa3ec9fd 100644 --- a/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties +++ b/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties @@ -1,4 +1,3 @@ -action.q.hello.description=Hello description amazonqInlineChat.hint.edit = Edit amazonqInlineChat.popup.accept=Accept \u23CE amazonqInlineChat.popup.cancel=Cancel \u238B @@ -10,9 +9,9 @@ amazonqInlineChat.popup.title=Enter Instructions for Q amazonq.refresh.panel=Refresh Chat Session amazonq.title=Amazon Q amazonq.workspace.settings.open.prompt=Workspace index is now enabled. You can disable it from Amazon Q settings. -action.q.profile.usage.text=You changed profile -action.q.profile.usage=You're using the '{0}' profile for Amazon Q. -action.q.switchProfiles.text=Change profile +action.q.profile.usage.text=You changed your profile +action.q.profile.usage=You''re using the ''{0}'' profile for Amazon Q. +action.q.switchProfiles.text=Change Profile action.q.switchProfiles.dialog.text=Amazon Q Developer Profile action.q.switchProfiles.dialog.account.label=Account: {0} action.q.switchProfiles.dialog.panel.text=Change your Q Developer profile diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/actions/QSwitchProfilesAction.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/actions/QSwitchProfilesAction.kt index 2e8949d0f66..4735d748a62 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/actions/QSwitchProfilesAction.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/actions/QSwitchProfilesAction.kt @@ -7,17 +7,10 @@ import com.intellij.icons.AllIcons import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.DumbAware -import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection -import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager -import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection -import software.aws.toolkits.jetbrains.services.amazonq.profile.QProfileSwitchIntent import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileDialog import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager import software.aws.toolkits.resources.AmazonQBundle.message -import software.aws.toolkits.telemetry.MetricResult -import software.aws.toolkits.telemetry.Telemetry class QSwitchProfilesAction : AnAction(message("action.q.switchProfiles.text")), DumbAware { @@ -29,30 +22,9 @@ class QSwitchProfilesAction : AnAction(message("action.q.switchProfiles.text")), override fun actionPerformed(e: AnActionEvent) { val project = e.project ?: return - ApplicationManager.getApplication().executeOnPooledThread { - val profiles = try { - QRegionProfileManager.getInstance().listRegionProfiles(project) - } catch (e: Exception) { - val conn = ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection - Telemetry.amazonq.didSelectProfile.use { span -> - span.source(QProfileSwitchIntent.User.value) - .amazonQProfileRegion(QRegionProfileManager.getInstance().activeProfile(project)?.region ?: "not-set") - .ssoRegion(conn?.region) - .credentialStartUrl(conn?.startUrl) - .result(MetricResult.Failed) - .reason(e.message) - } - throw e - } - ?: error("Attempted to fetch profiles while there does not exist") - val selectedProfile = QRegionProfileManager.getInstance().activeProfile(project) ?: profiles[0] - ApplicationManager.getApplication().invokeLater { - QRegionProfileDialog( - project, - profiles = profiles, - selectedProfile = selectedProfile - ).show() - } - } + QRegionProfileDialog( + project, + selectedProfile = QRegionProfileManager.getInstance().activeProfile(project) + ).show() } } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/profile/QRegionProfileDialog.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/profile/QRegionProfileDialog.kt index ead6ab5aca3..c0921f3362f 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/profile/QRegionProfileDialog.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/profile/QRegionProfileDialog.kt @@ -7,24 +7,61 @@ import com.intellij.icons.AllIcons import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.DialogWrapper +import com.intellij.ui.ColoredListCellRenderer +import com.intellij.ui.SimpleTextAttributes +import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.BottomGap -import com.intellij.ui.dsl.builder.bind +import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.panel +import com.intellij.ui.dsl.builder.toNullableProperty import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection import software.aws.toolkits.jetbrains.core.help.HelpIds +import software.aws.toolkits.jetbrains.ui.AsyncComboBox +import software.aws.toolkits.jetbrains.utils.ui.selected import software.aws.toolkits.resources.AmazonQBundle.message +import software.aws.toolkits.resources.AwsCoreBundle import software.aws.toolkits.telemetry.MetricResult import software.aws.toolkits.telemetry.Telemetry import javax.swing.JComponent +import javax.swing.JList + +data class QRegionProfileDialogState( + var selectedProfile: QRegionProfile? = null, +) class QRegionProfileDialog( private var project: Project, - private var profiles: List, - private var selectedProfile: QRegionProfile, // default + val state: QRegionProfileDialogState = QRegionProfileDialogState(), + private var selectedProfile: QRegionProfile?, ) : DialogWrapper(project) { + private val renderer = object : ColoredListCellRenderer() { + override fun customizeCellRenderer( + list: JList, + value: QRegionProfile?, + index: Int, + selected: Boolean, + hasFocus: Boolean, + ) { + value?.let { + append( + if (it == selectedProfile) { + "${it.profileName} - ${it.region} (connected)" + } else { + "${it.profileName} - ${it.region}" + }, + SimpleTextAttributes.REGULAR_ATTRIBUTES + ) + + append(" " + message("action.q.switchProfiles.dialog.account.label", it.accountId), SimpleTextAttributes.GRAY_SMALL_ATTRIBUTES) + } + } + } + + private val combo = AsyncComboBox(customRenderer = renderer) + private val panel: DialogPanel by lazy { panel { row { label(message("action.q.switchProfiles.dialog.panel.text")).bold() } @@ -36,32 +73,40 @@ class QRegionProfileDialog( } separator().bottomGap(BottomGap.MEDIUM) - buttonsGroup { - profiles.forEach { profile -> - row { - radioButton("", profile) - - panel { - val regionDisplay = if (profile == selectedProfile) { - "${profile.profileName} - ${profile.region} (connected)" - } else { - "${profile.profileName} - ${profile.region}" - } - row { label(regionDisplay) } - row { - label(message("action.q.switchProfiles.dialog.account.label", profile.accountId)).applyToComponent { - font = font.deriveFont(font.size2D - 2.0f) - } - } - } - }.bottomGap(BottomGap.MEDIUM) + combo.proposeModelUpdate { model -> + try { + QRegionProfileManager.getInstance().listRegionProfiles(project)?.forEach { + model.addElement(it) + } ?: error("Attempted to fetch profiles while there does not exist") + + model.selectedItem = selectedProfile + } catch (e: Exception) { + val conn = ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection + Telemetry.amazonq.didSelectProfile.use { span -> + span.source(QProfileSwitchIntent.User.value) + .amazonQProfileRegion(QRegionProfileManager.getInstance().activeProfile(project)?.region ?: "not-set") + .ssoRegion(conn?.region) + .credentialStartUrl(conn?.startUrl) + .result(MetricResult.Failed) + .reason(e.message) + } + throw e } - }.bind({ selectedOption }, { selectedOption = it }) + } + + row { + cell(combo) + .align(AlignX.FILL) + .errorOnApply(AwsCoreBundle.message("gettingstarted.setup.error.not_selected")) { it.selected() == null } + .bindItem(state::selectedProfile.toNullableProperty()) + } separator().bottomGap(BottomGap.MEDIUM) } } - private var selectedOption: QRegionProfile = selectedProfile // user selected + + private val selectedOption + get() = state.selectedProfile // user selected init { title = message("action.q.switchProfiles.dialog.text") @@ -87,7 +132,7 @@ class QRegionProfileDialog( Telemetry.amazonq.didSelectProfile.use { span -> span.source(QProfileSwitchIntent.User.value) .amazonQProfileRegion(profileManager.activeProfile(project)?.region ?: "not-set") - .profileCount(profiles.size) + .profileCount(combo.model.size) .ssoRegion(conn?.region) .credentialStartUrl(conn?.startUrl) .result(MetricResult.Cancelled)