From 6eba81785718bfb3af579877fb332336f3a6eae3 Mon Sep 17 00:00:00 2001 From: kirillk Date: Mon, 3 Aug 2026 15:44:25 -0400 Subject: [PATCH] fix(jetbrains): show absolute reverted file tooltips --- .../client/session/ui/RevertBanner.kt | 29 +++++++++++++++++-- .../session/ui/SessionMessageListPanelTest.kt | 27 +++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/RevertBanner.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/RevertBanner.kt index 5579e96a468..06ca8f68f04 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/RevertBanner.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/RevertBanner.kt @@ -24,7 +24,11 @@ import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil import com.intellij.util.ui.components.BorderLayoutPanel import java.awt.BorderLayout +import java.awt.Component +import java.awt.Container import java.awt.Dimension +import java.nio.file.Path +import javax.swing.JComponent import javax.swing.JPanel import javax.swing.ScrollPaneConstants @@ -133,7 +137,7 @@ class RevertBanner( val row = rows.getOrPut(item.file) { Row(item) } - row.update(item, names[item.file] ?: item.file) + row.update(item, names[item.file] ?: item.file, absolute(item.file)) row.panel } if (files.components.toList() != order) { @@ -188,6 +192,15 @@ class RevertBanner( openDiff(diffs, KiloBundle.message("revert.banner.openDiff.title"), "revert:${sessionId ?: "pending"}:${revert.messageID}") } + private fun absolute(file: String): String { + val path = runCatching { Path.of(file) }.getOrNull() ?: return file + if (path.isAbsolute) return path.normalize().toString() + val root = model.session?.directory + ?.takeIf { it.isNotBlank() } + ?.let { runCatching { Path.of(it) }.getOrNull() } + return (root?.resolve(path) ?: path.toAbsolutePath()).normalize().toString() + } + /** Height that fits at most [MAX_FILE_ROWS] rows, or 0 when the list is short enough to show in full. */ private fun rowCap(): Int { val comps = files.components @@ -207,17 +220,27 @@ class RevertBanner( init { applyStyle() + tip(item.file) } - fun update(item: DiffFileDto, text: String) { + fun update(item: DiffFileDto, text: String, tip: String) { if (label.text != text) label.text = text - if (label.toolTipText != item.file) label.toolTipText = item.file + if (panel.toolTipText != tip) tip(tip) badge.update(item.additions, item.deletions) } fun applyStyle() { label.foreground = UIUtil.getLabelForeground() } + + private fun tip(text: String) { + tip(panel, text) + } + + private fun tip(node: Component, text: String) { + if (node is JComponent && node.toolTipText != text) node.toolTipText = text + if (node is Container) node.components.forEach { tip(it, text) } + } } } diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanelTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanelTest.kt index 3e23202907b..95ff55bf948 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanelTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanelTest.kt @@ -39,6 +39,8 @@ import ai.kilocode.rpc.dto.MessageTimeDto import ai.kilocode.rpc.dto.MessageWithPartsDto import ai.kilocode.rpc.dto.PartDto import ai.kilocode.rpc.dto.SessionRevertDto +import ai.kilocode.rpc.dto.SessionDto +import ai.kilocode.rpc.dto.SessionTimeDto import ai.kilocode.rpc.dto.TodoDto import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI import com.intellij.openapi.Disposable @@ -1161,6 +1163,31 @@ class SessionMessageListPanelTest : BasePlatformTestCase() { assertTrue(labels.contains("Button.kt" to "packages/ui/src/Button.kt")) } + fun `test rollback banner uses full path tooltip for entire file row`() { + val banner = RevertBanner(model, {}, {}, {}) + model.setSession(SessionDto( + id = "ses", + projectID = "proj", + directory = "/workspace/root", + title = "Session", + version = "1", + time = SessionTimeDto(0.0, 0.0), + )) + model.upsertMessage(msg("u1", "user")) + model.setRevert(SessionRevertDto("u1", snapshot = "snap1")) + model.setDiff(listOf( + DiffFileDto("project/dir1/shared-alpha.txt", 0, 4), + DiffFileDto("project/dir2/shared-alpha.txt", 0, 4), + )) + + banner.update() + + val label = rowLabels(banner).first { it.text == "dir1/shared-alpha.txt" } + val row = label.parent as JComponent + assertEquals("/workspace/root/project/dir1/shared-alpha.txt", row.toolTipText) + assertTrue(components(row).filterIsInstance().all { it.toolTipText == "/workspace/root/project/dir1/shared-alpha.txt" }) + } + fun `test rollback banner opens rolled back diff`() { val diff = DiffFileDto("src/A.kt", 1, 0, PATCH, "modified") val opened = mutableListOf>()