From 3eb9a66fd001362121154850a345fbad87156884 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:37:04 +0000 Subject: [PATCH 1/3] Initial plan From d9a979b967463629f396ec0e7b096e1df44d5905 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:42:02 +0000 Subject: [PATCH 2/3] Initial plan for PicoCode plugin enhancements Co-authored-by: Mte90 <403283+Mte90@users.noreply.github.com> --- ide-plugins/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ide-plugins/build.gradle.kts b/ide-plugins/build.gradle.kts index 6070961..aa528f2 100644 --- a/ide-plugins/build.gradle.kts +++ b/ide-plugins/build.gradle.kts @@ -41,6 +41,7 @@ tasks { } patchPluginXml { - version.set(project.version.toString()) + sinceBuild.set("231") + untilBuild.set("241.*") } } From 59b9825c5656c5ac900a06f76a3402ccce13a72d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:45:53 +0000 Subject: [PATCH 3/3] Implement status bar click handler, settings service, and configurable UI Co-authored-by: Mte90 <403283+Mte90@users.noreply.github.com> --- .../com/picocode/PicoCodeConfigurable.kt | 68 +++++++++++++++++++ .../kotlin/com/picocode/PicoCodeSettings.kt | 39 +++++++++++ .../com/picocode/PicoCodeStatusBarWidget.kt | 32 +++++++-- .../com/picocode/PicoCodeToolWindowContent.kt | 36 ++++------ .../src/main/resources/META-INF/plugin.xml | 5 ++ 5 files changed, 152 insertions(+), 28 deletions(-) create mode 100644 ide-plugins/src/main/kotlin/com/picocode/PicoCodeConfigurable.kt create mode 100644 ide-plugins/src/main/kotlin/com/picocode/PicoCodeSettings.kt diff --git a/ide-plugins/src/main/kotlin/com/picocode/PicoCodeConfigurable.kt b/ide-plugins/src/main/kotlin/com/picocode/PicoCodeConfigurable.kt new file mode 100644 index 0000000..970adf3 --- /dev/null +++ b/ide-plugins/src/main/kotlin/com/picocode/PicoCodeConfigurable.kt @@ -0,0 +1,68 @@ +package com.picocode + +import com.intellij.openapi.options.Configurable +import com.intellij.openapi.project.Project +import com.intellij.ui.components.JBLabel +import com.intellij.ui.components.JBTextField +import com.intellij.util.ui.FormBuilder +import javax.swing.JComponent +import javax.swing.JPanel + +/** + * Project settings configurable for PicoCode plugin + * Provides UI to configure backend server host and port + */ +class PicoCodeConfigurable(private val project: Project) : Configurable { + + private val hostField = JBTextField(20) + private val portField = JBTextField(10) + private var settingsPanel: JPanel? = null + + override fun getDisplayName(): String = "PicoCode" + + override fun createComponent(): JComponent { + val settings = PicoCodeSettings.getInstance(project) + val state = settings.state + + // Initialize fields with current settings + hostField.text = state.serverHost + portField.text = state.serverPort.toString() + + // Create the settings panel with form layout + settingsPanel = FormBuilder.createFormBuilder() + .addLabeledComponent(JBLabel("Backend Host:"), hostField, 1, false) + .addLabeledComponent(JBLabel("Backend Port:"), portField, 1, false) + .addComponentFillVertically(JPanel(), 0) + .panel + + return settingsPanel!! + } + + override fun isModified(): Boolean { + val settings = PicoCodeSettings.getInstance(project) + val state = settings.state + + val hostModified = hostField.text != state.serverHost + val portModified = portField.text.toIntOrNull() != state.serverPort + + return hostModified || portModified + } + + override fun apply() { + val settings = PicoCodeSettings.getInstance(project) + val state = settings.state + + state.serverHost = hostField.text.trim().ifEmpty { "localhost" } + state.serverPort = portField.text.trim().toIntOrNull() ?: 8000 + + settings.loadState(state) + } + + override fun reset() { + val settings = PicoCodeSettings.getInstance(project) + val state = settings.state + + hostField.text = state.serverHost + portField.text = state.serverPort.toString() + } +} diff --git a/ide-plugins/src/main/kotlin/com/picocode/PicoCodeSettings.kt b/ide-plugins/src/main/kotlin/com/picocode/PicoCodeSettings.kt new file mode 100644 index 0000000..a8f7a63 --- /dev/null +++ b/ide-plugins/src/main/kotlin/com/picocode/PicoCodeSettings.kt @@ -0,0 +1,39 @@ +package com.picocode + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.components.Service +import com.intellij.openapi.project.Project +import com.intellij.util.xmlb.XmlSerializerUtil + +/** + * Project-level settings for PicoCode plugin + * Stores backend server host and port configuration + */ +@State( + name = "PicoCodeSettings", + storages = [Storage("picocode.xml")] +) +@Service(Service.Level.PROJECT) +class PicoCodeSettings : PersistentStateComponent { + + data class SettingsState( + var serverHost: String = "localhost", + var serverPort: Int = 8000 + ) + + private var state = SettingsState() + + override fun getState(): SettingsState = state + + override fun loadState(state: SettingsState) { + XmlSerializerUtil.copyBean(state, this.state) + } + + companion object { + fun getInstance(project: Project): PicoCodeSettings { + return project.getService(PicoCodeSettings::class.java) + } + } +} diff --git a/ide-plugins/src/main/kotlin/com/picocode/PicoCodeStatusBarWidget.kt b/ide-plugins/src/main/kotlin/com/picocode/PicoCodeStatusBarWidget.kt index e3c61fb..9f88e3a 100644 --- a/ide-plugins/src/main/kotlin/com/picocode/PicoCodeStatusBarWidget.kt +++ b/ide-plugins/src/main/kotlin/com/picocode/PicoCodeStatusBarWidget.kt @@ -5,6 +5,8 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.wm.StatusBar import com.intellij.openapi.wm.StatusBarWidget import com.intellij.openapi.wm.StatusBarWidgetFactory +import com.intellij.openapi.wm.ToolWindowManager +import com.intellij.openapi.ui.Messages import com.intellij.util.Consumer import com.google.gson.Gson import com.google.gson.JsonObject @@ -22,8 +24,6 @@ class PicoCodeStatusBarWidget(private val project: Project) : StatusBarWidget, companion object { const val ID = "PicoCodeStatusWidget" - private const val DEFAULT_HOST = "localhost" - private const val DEFAULT_PORT = 8000 private const val POLLING_INTERVAL_SECONDS = 5L } @@ -93,7 +93,21 @@ class PicoCodeStatusBarWidget(private val project: Project) : StatusBarWidget, override fun getClickConsumer(): Consumer? { return Consumer { - // Optional: could open tool window or trigger re-indexing + // Open the PicoCode RAG tool window on click + ApplicationManager.getApplication().invokeLater { + val toolWindowManager = ToolWindowManager.getInstance(project) + val toolWindow = toolWindowManager.getToolWindow("PicoCode RAG") + + if (toolWindow != null) { + toolWindow.show() + } else { + Messages.showInfoMessage( + project, + "PicoCode RAG tool window is not available. Please ensure the plugin is properly installed.", + "PicoCode RAG" + ) + } + } } } @@ -127,7 +141,11 @@ class PicoCodeStatusBarWidget(private val project: Project) : StatusBarWidget, private fun getOrCreateProject(projectPath: String): String? { return try { - val url = URL("http://$DEFAULT_HOST:$DEFAULT_PORT/api/projects") + val settings = PicoCodeSettings.getInstance(project) + val host = settings.state.serverHost + val port = settings.state.serverPort + + val url = URL("http://$host:$port/api/projects") val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.setRequestProperty("Content-Type", "application/json") @@ -149,7 +167,11 @@ class PicoCodeStatusBarWidget(private val project: Project) : StatusBarWidget, private fun fetchProjectStatus(projectId: String): Pair { return try { - val url = URL("http://$DEFAULT_HOST:$DEFAULT_PORT/api/projects/$projectId") + val settings = PicoCodeSettings.getInstance(project) + val host = settings.state.serverHost + val port = settings.state.serverPort + + val url = URL("http://$host:$port/api/projects/$projectId") val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" diff --git a/ide-plugins/src/main/kotlin/com/picocode/PicoCodeToolWindowContent.kt b/ide-plugins/src/main/kotlin/com/picocode/PicoCodeToolWindowContent.kt index 5a60b82..1dee053 100644 --- a/ide-plugins/src/main/kotlin/com/picocode/PicoCodeToolWindowContent.kt +++ b/ide-plugins/src/main/kotlin/com/picocode/PicoCodeToolWindowContent.kt @@ -1,16 +1,10 @@ package com.picocode import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.fileEditor.FileEditorManager -import com.intellij.openapi.fileEditor.OpenFileDescriptor import com.intellij.openapi.project.Project -import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.ui.components.JBScrollPane import com.intellij.ui.components.JBTextArea -import com.intellij.ui.components.JBTextField -import com.intellij.util.ui.FormBuilder import java.awt.BorderLayout -import java.io.File import javax.swing.* import java.net.HttpURLConnection import java.net.URL @@ -23,10 +17,6 @@ import com.google.gson.JsonObject * Assumes PicoCode server is already running */ class PicoCodeToolWindowContent(private val project: Project) { - // PicoCode server configuration (only host and port needed) - private val serverHostField = JBTextField("localhost") - private val serverPortField = JBTextField("8000") - // Chat components private val chatArea = JBTextArea(25, 60) private val inputField = JBTextArea(3, 60) @@ -42,26 +32,26 @@ class PicoCodeToolWindowContent(private val project: Project) { inputField.wrapStyleWord = true } - private fun getServerHost(): String = serverHostField.text.trim().ifEmpty { "localhost" } - private fun getServerPort(): Int = serverPortField.text.trim().toIntOrNull() ?: 8000 + private fun getServerHost(): String { + val settings = PicoCodeSettings.getInstance(project) + return settings.state.serverHost + } + + private fun getServerPort(): Int { + val settings = PicoCodeSettings.getInstance(project) + return settings.state.serverPort + } fun getContent(): JComponent { val panel = JPanel(BorderLayout()) - // Server config panel with re-index button - val configPanel = FormBuilder.createFormBuilder() - .addLabeledComponent("PicoCode Host:", serverHostField) - .addLabeledComponent("PicoCode Port:", serverPortField) - .panel - - // Add a re-index button to config panel + // Add a re-index button at the top val reindexBtn = JButton("Re-index Project") reindexBtn.addActionListener { reindexProject() } - val configPanelWithButton = JPanel(BorderLayout()) - configPanelWithButton.add(configPanel, BorderLayout.CENTER) - configPanelWithButton.add(reindexBtn, BorderLayout.SOUTH) + val topPanel = JPanel(BorderLayout()) + topPanel.add(reindexBtn, BorderLayout.EAST) // Chat display area val chatScrollPane = JBScrollPane(chatArea) @@ -99,7 +89,7 @@ class PicoCodeToolWindowContent(private val project: Project) { inputPanel.add(buttonPanel, BorderLayout.SOUTH) // Layout - panel.add(configPanelWithButton, BorderLayout.NORTH) + panel.add(topPanel, BorderLayout.NORTH) panel.add(chatScrollPane, BorderLayout.CENTER) panel.add(inputPanel, BorderLayout.SOUTH) diff --git a/ide-plugins/src/main/resources/META-INF/plugin.xml b/ide-plugins/src/main/resources/META-INF/plugin.xml index 37b035b..50ff761 100644 --- a/ide-plugins/src/main/resources/META-INF/plugin.xml +++ b/ide-plugins/src/main/resources/META-INF/plugin.xml @@ -18,5 +18,10 @@ factoryClass="com.picocode.PicoCodeToolWindowFactory"/> + +