Skip to content

Commit d1bb26c

Browse files
committed
feat(settings): add actions for LLMClients table
1 parent 324946d commit d1bb26c

File tree

6 files changed

+159
-74
lines changed

6 files changed

+159
-74
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import java.util.*
88
import javax.swing.DefaultListCellRenderer
99
import javax.swing.JList
1010

11-
class AppSettingsListCellRenderer : DefaultListCellRenderer() {
11+
class AICommitsListCellRenderer : DefaultListCellRenderer() {
1212
override fun getListCellRendererComponent(
1313
list: JList<*>?,
1414
value: Any?,

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/AppSettings2.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
5454
TestAIClient(),
5555
TestAIClient("TestAI2")
5656
)
57+
5758
private var activeLlmClient = "OpenAI"
5859

5960
@XMap
@@ -113,10 +114,10 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
113114
return llmClients.find { it.displayName == activeLlmClient }!!
114115
}
115116

116-
fun setActiveLlmClient(llmClientName: String) {
117+
fun setActiveLlmClient(llmClient: LLMClient) {
117118
// TODO @Blarc: Throw exception if llm client name is not valid
118-
llmClients.find { it.displayName == llmClientName }?.let {
119-
activeLlmClient = llmClientName
119+
llmClients.find { it.displayName == llmClient.displayName }?.let {
120+
activeLlmClient = llmClient.displayName
120121
}
121122
}
122123

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/AppSettingsConfigurable.kt

Lines changed: 83 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,93 +2,112 @@ package com.github.blarc.ai.commits.intellij.plugin.settings
22

33
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle
44
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message
5+
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClient
56
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClientTable
67
import com.github.blarc.ai.commits.intellij.plugin.settings.prompts.Prompt
78
import com.github.blarc.ai.commits.intellij.plugin.settings.prompts.PromptTable
89
import com.intellij.openapi.options.BoundConfigurable
910
import com.intellij.openapi.ui.ComboBox
1011
import com.intellij.ui.CommonActionsPanel
1112
import com.intellij.ui.ToolbarDecorator
12-
import com.intellij.ui.components.JBLabel
1313
import com.intellij.ui.dsl.builder.*
1414
import java.util.*
1515

1616
class AppSettingsConfigurable : BoundConfigurable(message("settings.general.group.title")) {
1717

1818
private val llmClientTable = LLMClientTable()
19-
private val promptTable = PromptTable()
2019
private lateinit var llmClientToolbarDecorator: ToolbarDecorator
20+
private lateinit var llmClientComboBox: ComboBox<LLMClient>
21+
private val promptTable = PromptTable()
2122
private lateinit var toolbarDecorator: ToolbarDecorator
22-
private lateinit var promptComboBox: Cell<ComboBox<Prompt>>
23+
private lateinit var promptComboBox: ComboBox<Prompt>
2324

2425
override fun createPanel() = panel {
2526

26-
group(JBLabel("OpenAI")) {
27-
row {
28-
llmClientToolbarDecorator = ToolbarDecorator.createDecorator(llmClientTable.table)
29-
.setAddAction {
30-
llmClientTable.addLlmClient()
27+
row {
28+
label(message("settings.llmClient")).widthGroup("labelPrompt")
29+
llmClientComboBox = comboBox(AppSettings2.instance.llmClients, AICommitsListCellRenderer())
30+
.bindItem(getter = AppSettings2.instance::getActiveLLMClient) {
31+
it?.let {
32+
AppSettings2.instance.setActiveLlmClient(it)
3133
}
32-
.disableUpDownActions()
33-
34-
cell(llmClientToolbarDecorator.createPanel())
35-
.align(Align.FILL)
36-
}
34+
}.widthGroup("input")
35+
.component
3736
}
38-
39-
group(JBLabel("Prompt")) {
40-
row {
41-
label(message("settings.locale")).widthGroup("labelPrompt")
42-
comboBox(Locale.getAvailableLocales()
43-
.distinctBy { it.displayLanguage }
44-
.sortedBy { it.displayLanguage },
45-
AppSettingsListCellRenderer()
46-
)
47-
.bindItem(AppSettings2.instance::locale.toNullableProperty())
48-
browserLink(message("settings.more-prompts"), AICommitsBundle.URL_PROMPTS_DISCUSSION.toString())
49-
.align(AlignX.RIGHT)
50-
}
51-
row {
52-
label(message("settings.prompt")).widthGroup("labelPrompt")
53-
promptComboBox = comboBox(AppSettings2.instance.prompts.values, AppSettingsListCellRenderer())
54-
.bindItem(AppSettings2.instance::activePrompt.toNullableProperty())
55-
}
56-
row {
57-
toolbarDecorator = ToolbarDecorator.createDecorator(promptTable.table)
58-
.setAddAction {
59-
promptTable.addPrompt().let {
60-
promptComboBox.component.addItem(it)
61-
}
62-
}
63-
.setEditAction {
64-
promptTable.editPrompt()?.let {
65-
val editingSelected = promptComboBox.component.selectedItem == it.first
66-
promptComboBox.component.removeItem(it.first)
67-
promptComboBox.component.addItem(it.second)
68-
69-
if (editingSelected) {
70-
promptComboBox.component.selectedItem = it.second
71-
}
72-
}
37+
row {
38+
llmClientToolbarDecorator = ToolbarDecorator.createDecorator(llmClientTable.table)
39+
.setAddAction {
40+
llmClientTable.addLlmClient()
41+
}
42+
.setEditAction {
43+
llmClientTable.editLlmClient()
44+
}
45+
.setRemoveAction {
46+
llmClientTable.removeLlmClient()?.let {
47+
llmClientComboBox.removeItem(it)
7348
}
74-
.setEditActionUpdater {
75-
updateActionAvailability(CommonActionsPanel.Buttons.EDIT)
76-
true
49+
}
50+
.disableUpDownActions()
51+
52+
cell(llmClientToolbarDecorator.createPanel())
53+
.align(Align.FILL)
54+
}.resizableRow()
55+
56+
row {
57+
label(message("settings.locale")).widthGroup("labelPrompt")
58+
comboBox(Locale.getAvailableLocales()
59+
.distinctBy { it.displayLanguage }
60+
.sortedBy { it.displayLanguage },
61+
AICommitsListCellRenderer()
62+
)
63+
.widthGroup("input")
64+
.bindItem(AppSettings2.instance::locale.toNullableProperty())
65+
66+
browserLink(message("settings.more-prompts"), AICommitsBundle.URL_PROMPTS_DISCUSSION.toString())
67+
.align(AlignX.RIGHT)
68+
}
69+
row {
70+
label(message("settings.prompt")).widthGroup("labelPrompt")
71+
promptComboBox = comboBox(AppSettings2.instance.prompts.values, AICommitsListCellRenderer())
72+
.bindItem(AppSettings2.instance::activePrompt.toNullableProperty())
73+
.widthGroup("input")
74+
.component
75+
}
76+
row {
77+
toolbarDecorator = ToolbarDecorator.createDecorator(promptTable.table)
78+
.setAddAction {
79+
promptTable.addPrompt().let {
80+
promptComboBox.addItem(it)
7781
}
78-
.setRemoveAction {
79-
promptTable.removePrompt()?.let {
80-
promptComboBox.component.removeItem(it)
82+
}
83+
.setEditAction {
84+
promptTable.editPrompt()?.let {
85+
val editingSelected = promptComboBox.selectedItem == it.first
86+
promptComboBox.removeItem(it.first)
87+
promptComboBox.addItem(it.second)
88+
89+
if (editingSelected) {
90+
promptComboBox.selectedItem = it.second
8191
}
8292
}
83-
.setRemoveActionUpdater {
84-
updateActionAvailability(CommonActionsPanel.Buttons.REMOVE)
85-
true
93+
}
94+
.setEditActionUpdater {
95+
updateActionAvailability(CommonActionsPanel.Buttons.EDIT)
96+
true
97+
}
98+
.setRemoveAction {
99+
promptTable.removePrompt()?.let {
100+
promptComboBox.removeItem(it)
86101
}
87-
.disableUpDownActions()
102+
}
103+
.setRemoveActionUpdater {
104+
updateActionAvailability(CommonActionsPanel.Buttons.REMOVE)
105+
true
106+
}
107+
.disableUpDownActions()
88108

89-
cell(toolbarDecorator.createPanel())
90-
.align(Align.FILL)
91-
}.resizableRow()
109+
cell(toolbarDecorator.createPanel())
110+
.align(Align.FILL)
92111
}.resizableRow()
93112

94113
row {
@@ -103,18 +122,20 @@ class AppSettingsConfigurable : BoundConfigurable(message("settings.general.grou
103122
}
104123

105124
override fun isModified(): Boolean {
106-
return super.isModified() || promptTable.isModified()
125+
return super.isModified() || promptTable.isModified() || llmClientTable.isModified()
107126
}
108127

109128
override fun apply() {
110129
// TODO @Blarc
111130
// AppSettings2.instance.getActiveLLMClient().hosts.add(hostComboBox.item)
112131
promptTable.apply()
132+
llmClientTable.apply()
113133
super.apply()
114134
}
115135

116136
override fun reset() {
117137
promptTable.reset()
138+
llmClientTable.reset()
118139
super.reset()
119140
}
120141

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientTable.kt

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package com.github.blarc.ai.commits.intellij.plugin.settings.clients
22

3-
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle
3+
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message
44
import com.github.blarc.ai.commits.intellij.plugin.createColumn
5+
import com.github.blarc.ai.commits.intellij.plugin.settings.AICommitsListCellRenderer
56
import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettings2
6-
import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettingsListCellRenderer
77
import com.intellij.openapi.ui.DialogWrapper
88
import com.intellij.ui.JBCardLayout
99
import com.intellij.ui.components.Panel
1010
import com.intellij.ui.dsl.builder.Align
11+
import com.intellij.ui.dsl.builder.bindItem
1112
import com.intellij.ui.dsl.builder.panel
13+
import com.intellij.ui.dsl.builder.toNullableProperty
1214
import com.intellij.ui.table.TableView
1315
import com.intellij.util.ui.ListTableModel
16+
import java.awt.event.MouseAdapter
17+
import java.awt.event.MouseEvent
1418
import javax.swing.ListSelectionModel.SINGLE_SELECTION
1519

1620
class LLMClientTable {
@@ -20,11 +24,24 @@ class LLMClientTable {
2024
val table = TableView(tableModel).apply {
2125
setShowColumns(true)
2226
setSelectionMode(SINGLE_SELECTION)
27+
28+
columnModel.getColumn(0).preferredWidth = 150
29+
columnModel.getColumn(0).maxWidth = 250
30+
31+
addMouseListener(object : MouseAdapter() {
32+
override fun mouseClicked(e: MouseEvent?) {
33+
if (e?.clickCount == 2) {
34+
editLlmClient()
35+
}
36+
}
37+
})
2338
}
2439

2540
private fun createTableModel(): ListTableModel<LLMClient> = ListTableModel(
2641
arrayOf(
27-
createColumn<LLMClient>(AICommitsBundle.message("settings.prompt.name")) { llmClient -> llmClient.displayName },
42+
createColumn<LLMClient>(message("settings.llmClient.name")) { llmClient -> llmClient.displayName },
43+
createColumn(message("settings.llmClient.host")) { llmClient -> llmClient.host },
44+
createColumn(message("settings.llmClient.modelId")) { llmClient -> llmClient.modelId }
2845
),
2946
llmClients.toList()
3047
)
@@ -33,18 +50,57 @@ class LLMClientTable {
3350
val dialog = LLMClientDialog()
3451

3552
if (dialog.showAndGet()) {
36-
return null
53+
llmClients = llmClients.plus(dialog.llmClient)
54+
refreshTableModel()
55+
return dialog.llmClient
56+
}
57+
return null
58+
}
59+
60+
fun removeLlmClient(): LLMClient? {
61+
val selectedLlmClient = table.selectedObject ?: return null
62+
llmClients = llmClients.minus(selectedLlmClient)
63+
refreshTableModel()
64+
return selectedLlmClient
65+
66+
}
67+
68+
fun editLlmClient(): LLMClient? {
69+
val selectedLlmClient = table.selectedObject ?: return null
70+
val dialog = LLMClientDialog(selectedLlmClient)
71+
72+
if (dialog.showAndGet()) {
73+
refreshTableModel()
74+
return selectedLlmClient
3775
}
3876
return null
3977
}
4078

79+
private fun refreshTableModel() {
80+
tableModel.items = llmClients.toList()
81+
}
82+
83+
fun reset() {
84+
llmClients = AppSettings2.instance.llmClients
85+
refreshTableModel()
86+
}
87+
88+
fun isModified() = llmClients != AppSettings2.instance.llmClients
89+
90+
fun apply() {
91+
AppSettings2.instance.llmClients = llmClients
92+
}
93+
4194
private class LLMClientDialog(val newLlmClient: LLMClient? = null) : DialogWrapper(true) {
4295
private val cardLayout = JBCardLayout()
4396
private val cardPanel = Panel(null, cardLayout)
4497
private val llmClients: List<LLMClient> = getLlmClients(newLlmClient)
98+
var llmClient = newLlmClient ?: llmClients[0]
4599

46100
init {
47101
title = newLlmClient?.let { "Edit LLM Client" } ?: "Add LLM Client"
102+
setOKButtonText(newLlmClient?.let { message("actions.update") } ?: message("actions.add"))
103+
48104
llmClients.forEach {
49105
cardPanel.add(it.displayName, it.panel().create())
50106
}
@@ -54,21 +110,22 @@ class LLMClientTable {
54110

55111
override fun createCenterPanel() = panel {
56112
row("Client") {
57-
comboBox(llmClients, AppSettingsListCellRenderer())
113+
comboBox(llmClients, AICommitsListCellRenderer())
58114
.align(Align.FILL)
59115
.applyToComponent {
60116
addItemListener { cardLayout.show(cardPanel, (it.item as LLMClient).displayName) }
61117
}
62118
.applyToComponent {
63119
isEnabled = newLlmClient == null
64120
}
121+
.bindItem(::llmClient.toNullableProperty())
65122
}
66123
row {
67124
cell(cardPanel)
68125
}
69126
}
70127

71-
private fun getLlmClients(newLLMClient: LLMClient?) : List<LLMClient> {
128+
private fun getLlmClients(newLLMClient: LLMClient?): List<LLMClient> {
72129
return if (newLLMClient == null) {
73130
// TODO: Find a better way to create the list of all possible LLM Clients that implement LLMClient abstract class
74131
listOf(

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/OpenAIClientPanel.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ class OpenAIClientPanel(private val client: OpenAIClient) : LLMClientPanel {
6464
cell(tokenPasswordField)
6565
.bindText(client::token)
6666
.emptyText(message("settings.openAITokenExample"))
67-
.align(Align.FILL)
6867
.resizableColumn()
6968
.focused()
69+
.widthGroup("input")
7070
button(message("settings.verifyToken")) { verifyToken() }
7171
.align(AlignX.RIGHT)
7272
.widthGroup("button")
@@ -87,8 +87,8 @@ class OpenAIClientPanel(private val client: OpenAIClient) : LLMClientPanel {
8787
client.modelId = it
8888
}
8989
})
90+
.widthGroup("input")
9091
.resizableColumn()
91-
.align(Align.FILL)
9292
button(message("settings.refreshModels")) {
9393
runBackgroundableTask(message("settings.loadingModels")) {
9494
runBlocking(Dispatchers.IO) {
@@ -114,6 +114,8 @@ class OpenAIClientPanel(private val client: OpenAIClient) : LLMClientPanel {
114114
.validationOnInput { temperatureValid(it.text) }
115115

116116
contextHelp(message("settings.openAITemperatureComment"))
117+
.resizableColumn()
118+
.align(AlignX.LEFT)
117119
}
118120

119121
row {

src/main/resources/messages/MyBundle.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,7 @@ settings.exclusions.app.title=Global exclusions
7171
settings.exclusions.project.title=Project exclusions
7272
settings.prompt.hint=Hint
7373
settings.prompt.hint.comment=Note: This field is for testing purposes only. When generating the actual prompt, the {hint} variable will be replaced with content from the commit dialog.
74+
settings.llmClient=LLM Client
75+
settings.llmClient.name=Name
76+
settings.llmClient.host=Host
77+
settings.llmClient.modelId=Model

0 commit comments

Comments
 (0)