fix(jetbrains): refine agent manager badges

This commit is contained in:
kirillk
2026-08-20 16:30:58 -04:00
parent 082b38b234
commit 0161c3a4ba
7 changed files with 90 additions and 35 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Improve JetBrains Agent Manager session status badges, selection persistence, and PR badge theming.
@@ -145,7 +145,7 @@ class AgentManagerPanel(
}
fun refresh() {
selected = currentEditorWorktree()
selected = selected ?: currentEditorWorktree()
controller.reload()
project?.service<WorktreeStatusService>()?.refreshStats()
project?.service<WorktreeStatusService>()?.refreshPr()
@@ -14,6 +14,7 @@ import ai.kilocode.client.session.history.LocalHistoryItem
import ai.kilocode.client.plugin.KiloPluginSettings
import ai.kilocode.client.telemetry.Telemetry
import ai.kilocode.client.ui.list.ActiveList
import ai.kilocode.client.ui.list.ActiveListBadge
import ai.kilocode.client.ui.list.ActiveListConfig
import ai.kilocode.client.ui.list.ActiveListDeleteOptions
import ai.kilocode.client.ui.list.ActiveListEditOptions
@@ -575,7 +576,11 @@ class WorktreeSessionEditorPanel(
return name
}
override val tooltip: String get() = title
override val icon: Icon? get() = WorktreeIcons.forKind(kind)
override val badges: List<ActiveListBadge>
get() {
if (deleting) return emptyList()
return listOfNotNull(kind?.let { ActiveListBadge(it.label(), it.style()) })
}
override val section: String get() = HistoryTime.title(HistoryTime.section(item))
override val search: String get() = listOf(session.title, session.id, session.directory).joinToString(" ")
}
@@ -3,6 +3,7 @@ package ai.kilocode.client.ui
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.editor.colors.EditorColorsScheme
import com.intellij.ui.ColorUtil
import com.intellij.ui.JBColor
import com.intellij.util.ui.JBFont
import com.intellij.util.ui.JBUI
@@ -52,6 +53,8 @@ object UiStyle {
/** Filled badge styles shared across JetBrains UI surfaces. */
object Badge {
private const val PR_SOFT_ALPHA = 0.15
interface Style {
fun bg(): Color
@@ -145,51 +148,47 @@ object UiStyle {
}
object PullRequestOpen : Style {
override fun bg(): Color = JBColor.namedColor(
"Kilo.PullRequest.openBadgeBackground",
JBColor(Color(0x1F, 0x88, 0x3D), Color(0x23, 0x86, 0x36)),
private val accent = JBColor.namedColor(
"Kilo.PullRequest.openBadgeForeground",
JBColor(Color(0x1A, 0x7F, 0x37), Color(0x3F, 0xB9, 0x50)),
)
override fun fg(): Color = JBColor.namedColor(
"Kilo.PullRequest.openBadgeForeground",
Color.WHITE,
)
override fun bg(): Color = ColorUtil.withAlpha(accent, PR_SOFT_ALPHA)
override fun fg(): Color = accent
}
object PullRequestDraft : Style {
override fun bg(): Color = JBColor.namedColor(
"Kilo.PullRequest.draftBadgeBackground",
JBColor(Color(0x6E, 0x77, 0x81), Color(0x6E, 0x76, 0x81)),
private val accent = JBColor.namedColor(
"Kilo.PullRequest.draftBadgeForeground",
JBColor(Color(0x59, 0x63, 0x6E), Color(0x91, 0x98, 0xA1)),
)
override fun fg(): Color = JBColor.namedColor(
"Kilo.PullRequest.draftBadgeForeground",
Color.WHITE,
)
override fun bg(): Color = ColorUtil.withAlpha(accent, PR_SOFT_ALPHA)
override fun fg(): Color = accent
}
object PullRequestMerged : Style {
override fun bg(): Color = JBColor.namedColor(
"Kilo.PullRequest.mergedBadgeBackground",
JBColor(Color(0x82, 0x50, 0xDF), Color(0x89, 0x57, 0xE5)),
private val accent = JBColor.namedColor(
"Kilo.PullRequest.mergedBadgeForeground",
JBColor(Color(0x82, 0x50, 0xDF), Color(0xA3, 0x71, 0xF7)),
)
override fun fg(): Color = JBColor.namedColor(
"Kilo.PullRequest.mergedBadgeForeground",
Color.WHITE,
)
override fun bg(): Color = ColorUtil.withAlpha(accent, PR_SOFT_ALPHA)
override fun fg(): Color = accent
}
object PullRequestClosed : Style {
override fun bg(): Color = JBColor.namedColor(
"Kilo.PullRequest.closedBadgeBackground",
JBColor(Color(0xCF, 0x22, 0x2E), Color(0xDA, 0x36, 0x33)),
private val accent = JBColor.namedColor(
"Kilo.PullRequest.closedBadgeForeground",
JBColor(Color(0xCF, 0x22, 0x2E), Color(0xF8, 0x51, 0x49)),
)
override fun fg(): Color = JBColor.namedColor(
"Kilo.PullRequest.closedBadgeForeground",
Color.WHITE,
)
override fun bg(): Color = ColorUtil.withAlpha(accent, PR_SOFT_ALPHA)
override fun fg(): Color = accent
}
}
@@ -233,7 +233,7 @@ class AgentManagerPanelTest : BasePlatformTestCase() {
assertEquals(second.id, edt { (list.selectedValue as ActiveListItem).key })
}
fun `test refresh selects active worktree editor`() {
fun `test refresh keeps existing selection`() {
val first = WorktreeDto("/repo/.kilo/worktrees/feature-x", "feature-x", "feature/x", "/repo/.kilo/worktrees/feature-x")
val second = WorktreeDto("/repo/.kilo/worktrees/feature-y", "feature-y", "feature/y", "/repo/.kilo/worktrees/feature-y")
rpc.listed += first
@@ -251,6 +251,26 @@ class AgentManagerPanelTest : BasePlatformTestCase() {
}
flush()
assertEquals(first.id, edt { (list.selectedValue as ActiveListItem).key })
}
fun `test refresh uses active worktree editor when no selection exists`() {
val first = WorktreeDto("/repo/.kilo/worktrees/feature-x", "feature-x", "feature/x", "/repo/.kilo/worktrees/feature-x")
val second = WorktreeDto("/repo/.kilo/worktrees/feature-y", "feature-y", "feature/y", "/repo/.kilo/worktrees/feature-y")
rpc.listed += first
rpc.listed += second
val controller = WorktreeController(service, "/test", coroutines.scope)
val panel = edt { AgentManagerPanel(testRootDisposable, controller, project) }
edt { controller.reload() }
flush()
val list = edt { UIUtil.findComponentOfType(panel, JBList::class.java)!! }
edt {
ensureWorktreeSessionEditorKind()
project.service<KiloVfsManager>().open(WorktreeSessionEditorKind.ID, worktreeSessionParams(second), focus = true)
panel.refresh()
}
flush()
assertEquals(second.id, edt { (list.selectedValue as ActiveListItem).key })
}
@@ -14,9 +14,9 @@ import ai.kilocode.client.testing.FakeSessionRpcApi
import ai.kilocode.client.testing.TestCoroutines
import ai.kilocode.client.testing.pumpEdt
import ai.kilocode.client.testing.fire
import ai.kilocode.client.plugin.KiloBundle
import ai.kilocode.client.plugin.KiloPluginSettings
import ai.kilocode.client.ui.list.ActiveList
import ai.kilocode.client.ui.list.ActiveListBadge
import ai.kilocode.client.ui.list.ActiveListItem
import ai.kilocode.client.ui.list.activeListSectionTitle
import ai.kilocode.client.ui.list.activeListToolWindowBackground
@@ -236,7 +236,7 @@ class WorktreeSessionEditorPanelTest : BasePlatformTestCase() {
assertEquals("new", edt { (list.selectedValue as ActiveListItem).key })
}
fun `test running session row shows the activity icon and no badge`() {
fun `test running session row shows activity badge without leading icon`() {
manager.kinds = mapOf("ses_1" to SessionActivityKind.RUNNING)
val session = session("ses_1", nowSeconds())
rpc.listed += session
@@ -246,13 +246,25 @@ class WorktreeSessionEditorPanelTest : BasePlatformTestCase() {
val row = row("ses_1")
assertEquals("Session ses_1", row.title)
assertSame(WorktreeIcons.running, row.icon)
assertNull(row.icon)
assertNull(row.description)
assertEquals(emptyList<ActiveListBadge>(), row.badges)
assertEquals(KiloBundle.message("session.part.tool.running"), row.badges.single().text)
assertNull(row.trailing)
assertEquals(HistoryTime.title(HistoryTime.section(LocalHistoryItem(session))), row.section)
}
fun `test plan session row shows activity badge without leading icon`() {
manager.kinds = mapOf("ses_1" to SessionActivityKind.PLAN)
rpc.listed += session("ses_1", nowSeconds())
edt { controller.reload() }
flush()
val row = row("ses_1")
assertNull(row.icon)
assertEquals(KiloBundle.message("history.badge.plan"), row.badges.single().text)
}
fun `test session row shows the live agent title over the listed placeholder`() {
rpc.listed += session("ses_1", nowSeconds()).copy(title = "New session - 2026-07-30T19:01:40.945Z")
edt { controller.reload() }
@@ -64,4 +64,18 @@ class UiStyleTest : BasePlatformTestCase() {
assertSame(UiStyle.Badge.PullRequestMerged, style(GhState.MERGED))
assertSame(UiStyle.Badge.PullRequestClosed, style(GhState.CLOSED))
}
fun `test pull request badges use soft accent backgrounds`() {
val styles = listOf(
UiStyle.Badge.PullRequestOpen,
UiStyle.Badge.PullRequestDraft,
UiStyle.Badge.PullRequestMerged,
UiStyle.Badge.PullRequestClosed,
)
styles.forEach { style ->
assertTrue(style.bg().alpha < Color.WHITE.alpha)
assertEquals(Color.WHITE.alpha, style.fg().alpha)
}
}
}