fix(jetbrains): label tool window create actions

This commit is contained in:
kirillk
2026-08-27 12:07:49 -04:00
parent 156fb64fdb
commit d96b1b3c3d
10 changed files with 167 additions and 11 deletions
@@ -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.
@@ -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)
@@ -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)
}
@@ -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)
}
}
@@ -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) {
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 4H8V7H11V8H8V11H7V8H4V7H7V4Z" fill="#6C707E"/>
</svg>

After

Width:  |  Height:  |  Size: 202 B

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 4H8V7H11V8H8V11H7V8H4V7H7V4Z" fill="#CED0D6"/>
</svg>

After

Width:  |  Height:  |  Size: 202 B

@@ -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
@@ -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 {
@@ -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)
}
}