From d96b1b3c3d2a5424b0d5a5ecb299731cd1e88a37 Mon Sep 17 00:00:00 2001 From: kirillk Date: Thu, 27 Aug 2026 12:07:49 -0400 Subject: [PATCH] fix(jetbrains): label tool window create actions --- .../jetbrains-toolwindow-create-buttons.md | 5 ++ .../kilocode/client/KiloToolWindowFactory.kt | 2 + .../client/actions/KiloActionIcons.kt | 8 ++ .../client/actions/NewSessionAction.kt | 8 +- .../client/actions/NewWorktreeAction.kt | 14 +++- .../src/main/resources/icons/add-small.svg | 3 + .../main/resources/icons/add-small_dark.svg | 3 + .../resources/messages/KiloBundle.properties | 2 + .../client/actions/NewSessionActionTest.kt | 58 ++++++++++++-- .../client/actions/NewWorktreeActionTest.kt | 75 +++++++++++++++++++ 10 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 .changeset/jetbrains-toolwindow-create-buttons.md create mode 100644 packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/KiloActionIcons.kt create mode 100644 packages/kilo-jetbrains/frontend/src/main/resources/icons/add-small.svg create mode 100644 packages/kilo-jetbrains/frontend/src/main/resources/icons/add-small_dark.svg create mode 100644 packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/actions/NewWorktreeActionTest.kt diff --git a/.changeset/jetbrains-toolwindow-create-buttons.md b/.changeset/jetbrains-toolwindow-create-buttons.md new file mode 100644 index 0000000000..e38cb53ef1 --- /dev/null +++ b/.changeset/jetbrains-toolwindow-create-buttons.md @@ -0,0 +1,5 @@ +--- +"@kilocode/kilo-jetbrains": patch +--- + +Label the Kilo tool window's create buttons as + Session and + Worktree with a compact plus icon. diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/KiloToolWindowFactory.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/KiloToolWindowFactory.kt index 5718081914..45ef35c80e 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/KiloToolWindowFactory.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/KiloToolWindowFactory.kt @@ -19,6 +19,7 @@ import ai.kilocode.log.KiloLog import com.intellij.openapi.actionSystem.ActionGroup import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.DataProvider +import com.intellij.openapi.actionSystem.Separator import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.project.DumbAware @@ -178,6 +179,7 @@ internal class KiloToolWindowSetupService( val actions = listOfNotNull( ActionManager.getInstance().getAction("Kilo.NewSession"), ActionManager.getInstance().getAction("Kilo.NewWorktree"), + Separator.create(), ActionManager.getInstance().getAction("Kilo.History"), ) toolWindow.setTitleActions(actions) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/KiloActionIcons.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/KiloActionIcons.kt new file mode 100644 index 0000000000..768e1de5d6 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/KiloActionIcons.kt @@ -0,0 +1,8 @@ +package ai.kilocode.client.actions + +import com.intellij.openapi.util.IconLoader +import javax.swing.Icon + +internal object KiloActionIcons { + val add: Icon = IconLoader.getIcon("/icons/add-small.svg", KiloActionIcons::class.java) +} diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/NewSessionAction.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/NewSessionAction.kt index e9eec19c39..11f83d7ed1 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/NewSessionAction.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/NewSessionAction.kt @@ -5,15 +5,15 @@ import ai.kilocode.client.session.SessionManager import ai.kilocode.client.telemetry.Telemetry import ai.kilocode.client.agentManager.SidePanelKeys import ai.kilocode.client.agentManager.SidePanelMode -import com.intellij.icons.AllIcons import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.project.DumbAware class NewSessionAction : AnAction( KiloBundle.message("action.Kilo.NewSession.text"), KiloBundle.message("action.Kilo.NewSession.description"), - AllIcons.General.Add, + KiloActionIcons.add, ), DumbAware { override fun actionPerformed(e: AnActionEvent) { Telemetry.send("New Session Clicked", mapOf("surface" to "tool_window")) @@ -23,5 +23,9 @@ class NewSessionAction : AnAction( override fun update(e: AnActionEvent) { e.presentation.isVisible = e.getData(SidePanelKeys.MODE) != SidePanelMode.AGENT_MANAGER e.presentation.isEnabled = e.getData(SessionManager.KEY) != null + e.presentation.icon = KiloActionIcons.add + if (!e.isFromActionToolbar) return + e.presentation.text = KiloBundle.message("action.Kilo.NewSession.toolbar") + e.presentation.putClientProperty(ActionUtil.SHOW_TEXT_IN_TOOLBAR, true) } } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/NewWorktreeAction.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/NewWorktreeAction.kt index 90c2a7d218..53a8f20f09 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/NewWorktreeAction.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/actions/NewWorktreeAction.kt @@ -2,23 +2,31 @@ package ai.kilocode.client.actions import ai.kilocode.client.agentManager.SidePanelKeys import ai.kilocode.client.agentManager.SidePanelMode +import ai.kilocode.client.plugin.KiloBundle import ai.kilocode.client.telemetry.Telemetry -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.actionSystem.ex.ActionUtil import com.intellij.openapi.project.DumbAware /** * `+` toolbar action shown in Agent Manager mode. Opens the New Worktree dialog (New + Import tabs). */ -class NewWorktreeAction : AnAction(), DumbAware { +class NewWorktreeAction : AnAction( + KiloBundle.message("action.Kilo.NewWorktree.text"), + KiloBundle.message("action.Kilo.NewWorktree.description"), + KiloActionIcons.add, +), DumbAware { override fun getActionUpdateThread() = ActionUpdateThread.BGT override fun update(e: AnActionEvent) { e.presentation.isVisible = e.getData(SidePanelKeys.MODE) == SidePanelMode.AGENT_MANAGER e.presentation.isEnabled = e.getData(SidePanelKeys.WORKTREE_PANEL) != null - e.presentation.icon = AllIcons.General.Add + e.presentation.icon = KiloActionIcons.add + if (!e.isFromActionToolbar) return + e.presentation.text = KiloBundle.message("action.Kilo.NewWorktree.toolbar") + e.presentation.putClientProperty(ActionUtil.SHOW_TEXT_IN_TOOLBAR, true) } override fun actionPerformed(e: AnActionEvent) { diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/icons/add-small.svg b/packages/kilo-jetbrains/frontend/src/main/resources/icons/add-small.svg new file mode 100644 index 0000000000..498115b2c0 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/main/resources/icons/add-small.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/icons/add-small_dark.svg b/packages/kilo-jetbrains/frontend/src/main/resources/icons/add-small_dark.svg new file mode 100644 index 0000000000..340edfe239 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/main/resources/icons/add-small_dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties index f4f8db6c1e..93c9e4f31f 100644 --- a/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties +++ b/packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties @@ -458,8 +458,10 @@ action.Kilo.Settings.text=Settings action.Kilo.Settings.description=Kilo Code settings action.Kilo.NewSession.text=New Session action.Kilo.NewSession.description=Start a new Kilo session +action.Kilo.NewSession.toolbar=Session action.Kilo.NewWorktree.text=New Worktree action.Kilo.NewWorktree.description=Create a new git worktree +action.Kilo.NewWorktree.toolbar=Worktree action.Kilo.History.text=History action.Kilo.History.description=Show session history action.Kilo.ShowProfile.text=Profile diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/actions/NewSessionActionTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/actions/NewSessionActionTest.kt index 16264fd7a4..9e480f9991 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/actions/NewSessionActionTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/actions/NewSessionActionTest.kt @@ -1,7 +1,11 @@ package ai.kilocode.client.actions +import ai.kilocode.client.agentManager.SidePanelKeys +import ai.kilocode.client.agentManager.SidePanelMode import ai.kilocode.client.session.SessionManager import ai.kilocode.client.session.SessionRef +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.actionSystem.ActionUiKind import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.Presentation @@ -29,19 +33,61 @@ class NewSessionActionTest : BasePlatformTestCase() { val action = NewSessionAction() val presentation = Presentation().apply { copyFrom(action.templatePresentation) } - ActionUtil.updateAction(action, AnActionEvent.createFromDataContext("", presentation) { null }) + ActionUtil.updateAction(action, event(action, presentation = presentation)) assertFalse(presentation.isEnabled) } + fun `test toolbar presentation uses short text`() { + val manager = FakeManager() + val action = NewSessionAction() + val event = event(action, manager = manager, ui = ActionUiKind.TOOLBAR) + + ActionUtil.updateAction(action, event) + + assertEquals("Session", event.presentation.text) + assertEquals(true, event.presentation.getClientProperty(ActionUtil.SHOW_TEXT_IN_TOOLBAR)) + assertSame(KiloActionIcons.add, event.presentation.icon) + } + + fun `test non-toolbar presentation keeps full text`() { + val manager = FakeManager() + val action = NewSessionAction() + val event = event(action, manager = manager) + + ActionUtil.updateAction(action, event) + + assertEquals("New Session", event.presentation.text) + assertNull(event.presentation.getClientProperty(ActionUtil.SHOW_TEXT_IN_TOOLBAR)) + } + + fun `test action hidden on agent manager tab`() { + val manager = FakeManager() + val action = NewSessionAction() + val event = event(action, manager = manager, mode = SidePanelMode.AGENT_MANAGER) + + ActionUtil.updateAction(action, event) + + assertFalse(event.presentation.isVisible) + } + private fun event(manager: SessionManager): AnActionEvent { - val presentation = Presentation().apply { - copyFrom(NewSessionAction().templatePresentation) - } + return event(NewSessionAction(), manager = manager) + } + + private fun event( + action: NewSessionAction, + manager: SessionManager? = null, + mode: SidePanelMode? = null, + ui: ActionUiKind = ActionUiKind.NONE, + presentation: Presentation = Presentation().apply { copyFrom(action.templatePresentation) }, + ): AnActionEvent { val context = DataContext { id -> - if (SessionManager.KEY.`is`(id)) manager else null + if (SessionManager.KEY.`is`(id)) return@DataContext manager + if (SidePanelKeys.MODE.`is`(id)) return@DataContext mode + null } - return AnActionEvent.createFromDataContext("", presentation, context) + return AnActionEvent.createEvent(context, presentation, ActionPlaces.TOOLWINDOW_TITLE, ui, null) } private class FakeManager : SessionManager { diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/actions/NewWorktreeActionTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/actions/NewWorktreeActionTest.kt new file mode 100644 index 0000000000..5df568340f --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/actions/NewWorktreeActionTest.kt @@ -0,0 +1,75 @@ +package ai.kilocode.client.actions + +import ai.kilocode.client.agentManager.SidePanelKeys +import ai.kilocode.client.agentManager.SidePanelMode +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.actionSystem.ActionUiKind +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataContext +import com.intellij.openapi.actionSystem.Presentation +import com.intellij.openapi.actionSystem.ex.ActionUtil +import com.intellij.testFramework.fixtures.BasePlatformTestCase + +@Suppress("UnstableApiUsage") +class NewWorktreeActionTest : BasePlatformTestCase() { + fun `test toolbar presentation uses short text`() { + val action = NewWorktreeAction() + val event = event(action, mode = SidePanelMode.AGENT_MANAGER, ui = ActionUiKind.TOOLBAR) + + ActionUtil.updateAction(action, event) + + assertEquals("Worktree", event.presentation.text) + assertEquals(true, event.presentation.getClientProperty(ActionUtil.SHOW_TEXT_IN_TOOLBAR)) + assertSame(KiloActionIcons.add, event.presentation.icon) + } + + fun `test non-toolbar presentation keeps full text`() { + val action = NewWorktreeAction() + val event = event(action, mode = SidePanelMode.AGENT_MANAGER) + + ActionUtil.updateAction(action, event) + + assertEquals("New Worktree", event.presentation.text) + assertNull(event.presentation.getClientProperty(ActionUtil.SHOW_TEXT_IN_TOOLBAR)) + } + + fun `test action visible on agent manager tab`() { + val action = NewWorktreeAction() + val event = event(action, mode = SidePanelMode.AGENT_MANAGER) + + ActionUtil.updateAction(action, event) + + assertTrue(event.presentation.isVisible) + } + + fun `test action hidden on chat tab`() { + val action = NewWorktreeAction() + val event = event(action, mode = SidePanelMode.CHAT) + + ActionUtil.updateAction(action, event) + + assertFalse(event.presentation.isVisible) + } + + fun `test action disabled without worktree panel`() { + val action = NewWorktreeAction() + val event = event(action, mode = SidePanelMode.AGENT_MANAGER) + + ActionUtil.updateAction(action, event) + + assertFalse(event.presentation.isEnabled) + } + + private fun event( + action: NewWorktreeAction, + mode: SidePanelMode? = null, + ui: ActionUiKind = ActionUiKind.NONE, + ): AnActionEvent { + val presentation = Presentation().apply { copyFrom(action.templatePresentation) } + val context = DataContext { id -> + if (SidePanelKeys.MODE.`is`(id)) return@DataContext mode + null + } + return AnActionEvent.createEvent(context, presentation, ActionPlaces.TOOLWINDOW_TITLE, ui, null) + } +}