Skip to content

Commit fc61b34

Browse files
CopilotMte90
andcommitted
Fix IDE plugin UI: dark mode support and markdown rendering
Co-authored-by: Mte90 <403283+Mte90@users.noreply.github.com>
1 parent bcfb55c commit fc61b34

File tree

1 file changed

+68
-9
lines changed

1 file changed

+68
-9
lines changed

ide-plugins/src/main/kotlin/com/picocode/PicoCodeToolWindowContent.kt

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import com.intellij.openapi.application.ApplicationManager
44
import com.intellij.openapi.project.Project
55
import com.intellij.ui.components.JBScrollPane
66
import com.intellij.ui.components.JBTextArea
7+
import com.intellij.ui.JBColor
78
import java.awt.BorderLayout
89
import java.awt.Dimension
910
import java.awt.FlowLayout
1011
import javax.swing.*
12+
import javax.swing.text.html.HTMLEditorKit
1113
import java.net.HttpURLConnection
1214
import java.net.URL
1315
import com.google.gson.Gson
@@ -157,24 +159,76 @@ class PicoCodeToolWindowContent(private val project: Project) {
157159
return panel
158160
}
159161

162+
/**
163+
* Convert markdown to HTML for rendering
164+
*/
165+
private fun markdownToHtml(markdown: String): String {
166+
var html = markdown
167+
// Escape HTML special characters first
168+
.replace("&", "&amp;")
169+
.replace("<", "&lt;")
170+
.replace(">", "&gt;")
171+
// Code blocks (```)
172+
.replace(Regex("```([\\s\\S]*?)```"), "<pre style='background-color: #f5f5f5; padding: 8px; border-radius: 4px;'><code>$1</code></pre>")
173+
// Inline code (`)
174+
.replace(Regex("`([^`]+)`"), "<code style='background-color: #f0f0f0; padding: 2px 4px; border-radius: 3px;'>$1</code>")
175+
// Bold (**text**)
176+
.replace(Regex("\\*\\*([^*]+)\\*\\*"), "<strong>$1</strong>")
177+
// Italic (*text*)
178+
.replace(Regex("\\*([^*]+)\\*"), "<em>$1</em>")
179+
// Headers
180+
.replace(Regex("^### (.+)$", RegexOption.MULTILINE), "<h3>$1</h3>")
181+
.replace(Regex("^## (.+)$", RegexOption.MULTILINE), "<h2>$1</h2>")
182+
.replace(Regex("^# (.+)$", RegexOption.MULTILINE), "<h1>$1</h1>")
183+
// Lists
184+
.replace(Regex("^- (.+)$", RegexOption.MULTILINE), "<li>$1</li>")
185+
.replace(Regex("^\\* (.+)$", RegexOption.MULTILINE), "<li>$1</li>")
186+
// Line breaks
187+
.replace("\n", "<br/>")
188+
189+
// Wrap lists in <ul> tags
190+
html = html.replace(Regex("(<li>.*?</li>)+")) { matchResult ->
191+
"<ul>${matchResult.value}</ul>"
192+
}
193+
194+
return "<html><body style='font-family: sans-serif; font-size: 12px;'>$html</body></html>"
195+
}
196+
160197
private fun renderChatHistory() {
161198
chatPanel.removeAll()
162199

163200
for ((index, msg) in chatHistory.withIndex()) {
164201
val messagePanel = JPanel(BorderLayout())
202+
203+
// Use theme-aware colors
204+
val borderColor = if (msg.sender == "You")
205+
JBColor.BLUE
206+
else
207+
JBColor.GRAY
208+
165209
messagePanel.border = BorderFactory.createCompoundBorder(
166210
BorderFactory.createEmptyBorder(5, 5, 5, 5),
167-
BorderFactory.createLineBorder(if (msg.sender == "You") java.awt.Color.BLUE else java.awt.Color.GRAY, 1)
211+
BorderFactory.createLineBorder(borderColor, 1)
168212
)
169213

170-
val textArea = JBTextArea(msg.message)
171-
textArea.isEditable = false
172-
textArea.lineWrap = true
173-
textArea.wrapStyleWord = true
174-
textArea.background = if (msg.sender == "You") java.awt.Color(230, 240, 255) else java.awt.Color.WHITE
214+
// Use JEditorPane for HTML/Markdown rendering
215+
val editorPane = JEditorPane()
216+
editorPane.contentType = "text/html"
217+
editorPane.editorKit = HTMLEditorKit()
218+
editorPane.text = markdownToHtml(msg.message)
219+
editorPane.isEditable = false
220+
editorPane.isOpaque = true
221+
222+
// Use theme-aware background colors
223+
editorPane.background = if (msg.sender == "You")
224+
JBColor.namedColor("EditorPane.inactiveBackground", JBColor(0xE6F0FF, 0x2D3239))
225+
else
226+
JBColor.namedColor("EditorPane.background", JBColor.background())
227+
228+
editorPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true)
175229

176230
val headerPanel = JPanel(BorderLayout())
177-
headerPanel.add(JLabel("[$msg.sender]"), BorderLayout.WEST)
231+
headerPanel.add(JLabel("[${msg.sender}]"), BorderLayout.WEST)
178232

179233
// Add delete button for each message
180234
val deleteBtn = JButton("×")
@@ -186,7 +240,11 @@ class PicoCodeToolWindowContent(private val project: Project) {
186240
headerPanel.add(deleteBtn, BorderLayout.EAST)
187241

188242
messagePanel.add(headerPanel, BorderLayout.NORTH)
189-
messagePanel.add(textArea, BorderLayout.CENTER)
243+
244+
// Wrap editorPane in a scroll pane for long messages
245+
val messageScrollPane = JBScrollPane(editorPane)
246+
messageScrollPane.border = null
247+
messagePanel.add(messageScrollPane, BorderLayout.CENTER)
190248

191249
// Add context information if available
192250
if (msg.contexts.isNotEmpty()) {
@@ -196,7 +254,8 @@ class PicoCodeToolWindowContent(private val project: Project) {
196254
}
197255
val contextArea = JBTextArea(contextText.toString())
198256
contextArea.isEditable = false
199-
contextArea.background = java.awt.Color(250, 250, 250)
257+
// Use theme-aware background for context
258+
contextArea.background = JBColor.namedColor("Panel.background", JBColor(0xFAFAFA, 0x3C3F41))
200259
messagePanel.add(contextArea, BorderLayout.SOUTH)
201260
}
202261

0 commit comments

Comments
 (0)