fix(jetbrains): address history review gaps

This commit is contained in:
kirillk
2026-05-11 16:59:10 -04:00
parent 0833ef4221
commit 9a08ccf576
6 changed files with 45 additions and 29 deletions
@@ -18,7 +18,7 @@ class HistoryController(
private val deleted: (String) -> Unit = {},
) {
companion object {
const val CLOUD_LIMIT = 150
const val CLOUD_LIMIT = 50
}
val local = HistoryModel<LocalHistoryItem>()
@@ -26,11 +26,10 @@ class HistoryController(
private val deleting = mutableSetOf<String>()
private val opener = open
private var git: String? = null
fun reload(gitUrl: String? = null) {
fun reload() {
reloadLocal()
reloadCloud(gitUrl)
reloadCloud()
}
fun reloadLocal() {
@@ -46,8 +45,7 @@ class HistoryController(
}
}
fun reloadCloud(gitUrl: String? = null) {
git = gitUrl
fun reloadCloud() {
loadCloud(reset = true)
}
@@ -104,11 +102,10 @@ class HistoryController(
private fun loadCloud(reset: Boolean) {
val cursor = cloud.cursor.takeUnless { reset }
val gitUrl = git
edt { cloud.start(reset) }
cs.launch {
try {
val result = sessions.cloudSessions(workspace.directory, cursor, CLOUD_LIMIT, gitUrl)
val result = sessions.cloudSessions(workspace.directory, cursor, CLOUD_LIMIT, null)
val items = result.sessions.map(::cloudItem)
edt {
if (reset) cloud.replace(items, result.nextCursor)
@@ -66,8 +66,6 @@ open class HistoryModel<T : HistoryItem> : AbstractListModel<T>() {
filter()
}
fun hasFilter(): Boolean = query.isNotEmpty()
fun refresh() {
val end = size.coerceAtLeast(1) - 1
fireContentsChanged(this, 0, end)
@@ -7,13 +7,16 @@ import ai.kilocode.client.session.ui.style.SessionEditorStyle
import ai.kilocode.client.ui.UiStyle
import com.intellij.icons.AllIcons
import com.intellij.ide.ui.LafManagerListener
import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DataProvider
import com.intellij.openapi.actionSystem.Presentation
import com.intellij.openapi.actionSystem.impl.ActionButton
import com.intellij.openapi.util.Disposer
import com.intellij.ui.DocumentAdapter
import com.intellij.ui.PopupHandler
@@ -53,7 +56,6 @@ import javax.swing.event.ListDataListener
class HistoryPanel(
parent: Disposable,
private val controller: HistoryController,
private val gitUrl: () -> String? = { null },
private val nav: () -> Unit = {},
private val manager: SessionManager? = null,
) : BorderLayoutPanel(), Disposable, DataProvider {
@@ -116,7 +118,7 @@ class HistoryPanel(
fun refresh() {
stale = false
updateTheme()
controller.reload(gitUrl())
controller.reload()
}
private fun bindTheme() {
@@ -167,14 +169,19 @@ class HistoryPanel(
)
}
private fun back() = BorderLayoutPanel().apply {
add(JButton(KiloBundle.message("history.back"), AllIcons.Actions.Back).apply {
putClientProperty(DarculaButtonUI.DEFAULT_STYLE_KEY, true)
isFocusable = false
private fun back(): BorderLayoutPanel {
val label = KiloBundle.message("history.back")
val action = object : AnAction(label, null, AllIcons.Actions.Back) {
override fun actionPerformed(e: AnActionEvent) = nav()
}
val presentation = Presentation(label).apply { icon = AllIcons.Actions.Back }
val btn = ActionButton(action, presentation, ActionPlaces.TOOLBAR, JBUI.size(16)).apply {
cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
addActionListener { nav() }
}, BorderLayout.WEST)
border = JBUI.Borders.emptyRight(UiStyle.Gap.lg())
}
return BorderLayoutPanel().apply {
add(btn, BorderLayout.WEST)
border = JBUI.Borders.emptyRight(UiStyle.Gap.lg())
}
}
private fun panel(search: SearchTextField, list: JList<out HistoryItem>, footer: JComponent? = null): JComponent {
@@ -356,17 +363,17 @@ class HistoryPanel(
internal fun backText(): String? {
val view = activeInfo().foreSideComponent ?: return null
return UIUtil.uiTraverser(view).filter(JButton::class.java).firstOrNull()?.text
return UIUtil.uiTraverser(view).filter(ActionButton::class.java).firstOrNull()?.presentation?.text
}
internal fun backCursor(): Int? {
val view = activeInfo().foreSideComponent ?: return null
return UIUtil.uiTraverser(view).filter(JButton::class.java).firstOrNull()?.cursor?.type
return UIUtil.uiTraverser(view).filter(ActionButton::class.java).firstOrNull()?.cursor?.type
}
internal fun clickBack() {
val view = activeInfo().foreSideComponent ?: return
UIUtil.uiTraverser(view).filter(JButton::class.java).firstOrNull()?.doClick()
UIUtil.uiTraverser(view).filter(ActionButton::class.java).firstOrNull()?.click()
}
internal fun clickDelete() {
@@ -35,8 +35,8 @@ internal object HistoryTime {
val today = Instant.ofEpochMilli(now).atZone(zone).toLocalDate()
if (date == today) return HistorySection.TODAY
if (date == today.minusDays(1)) return HistorySection.YESTERDAY
if (date.isAfter(today.minusDays(7)) && date.isBefore(today)) return HistorySection.WEEK
if (date.isAfter(today.minusDays(30)) && date.isBefore(today)) return HistorySection.MONTH
if (!date.isBefore(today.minusDays(7)) && date.isBefore(today)) return HistorySection.WEEK
if (!date.isBefore(today.minusDays(30)) && date.isBefore(today)) return HistorySection.MONTH
return HistorySection.OLDER
}
@@ -96,7 +96,6 @@ history.delete.confirm.message.multiple=Delete {0} sessions from local history?
history.rename.title=Rename Session
history.rename.prompt=New session name:
history.cloud.load.more=Load more
history.cloud.repo.only=Only this repository
history.group.today=Today
history.group.yesterday=Yesterday
history.group.week=This Week
@@ -81,13 +81,13 @@ class HistoryControllerTest : BasePlatformTestCase() {
rpc.cloudCursor = "next_1"
val controller = controller()
controller.reloadCloud(gitUrl = "git@example.com:repo.git")
controller.reloadCloud()
flush()
assertEquals(1, controller.cloud.items.size)
assertEquals("cloud_1", controller.cloud.items[0].id)
assertEquals("next_1", controller.cloud.cursor)
assertEquals(FakeSessionRpcApi.CloudCall("/test", null, 150, "git@example.com:repo.git"), rpc.cloudCalls[0])
assertEquals(FakeSessionRpcApi.CloudCall("/test", null, 50, null), rpc.cloudCalls[0])
rpc.cloud.clear()
rpc.cloud += cloud("cloud_2", "Cloud Two")
@@ -96,7 +96,7 @@ class HistoryControllerTest : BasePlatformTestCase() {
flush()
assertEquals(listOf("cloud_1", "cloud_2"), controller.cloud.items.map { it.id })
assertEquals(FakeSessionRpcApi.CloudCall("/test", "next_1", 150, "git@example.com:repo.git"), rpc.cloudCalls[1])
assertEquals(FakeSessionRpcApi.CloudCall("/test", "next_1", 50, null), rpc.cloudCalls[1])
}
fun `test local delete calls rpc and removes item`() {
@@ -298,6 +298,21 @@ class HistoryControllerTest : BasePlatformTestCase() {
assertEquals(KiloBundle.message("history.time.hours", 10), HistoryTime.relative(offset, now.toEpochMilli()))
}
fun `test history section boundaries are inclusive at 7 and 30 days`() {
val now = Instant.now()
val exactly7 = LocalHistoryItem(session("s7", "7d", now.minus(7, ChronoUnit.DAYS).toEpochMilli().toDouble()))
val exactly30 = LocalHistoryItem(session("s30", "30d", now.minus(30, ChronoUnit.DAYS).toEpochMilli().toDouble()))
val within7 = LocalHistoryItem(session("s6", "6d", now.minus(6, ChronoUnit.DAYS).toEpochMilli().toDouble()))
val within30 = LocalHistoryItem(session("s29", "29d", now.minus(29, ChronoUnit.DAYS).toEpochMilli().toDouble()))
val beyond30 = LocalHistoryItem(session("s31", "31d", now.minus(31, ChronoUnit.DAYS).toEpochMilli().toDouble()))
assertEquals(HistorySection.WEEK, HistoryTime.section(exactly7, now.toEpochMilli()))
assertEquals(HistorySection.WEEK, HistoryTime.section(within7, now.toEpochMilli()))
assertEquals(HistorySection.MONTH, HistoryTime.section(exactly30, now.toEpochMilli()))
assertEquals(HistorySection.MONTH, HistoryTime.section(within30, now.toEpochMilli()))
assertEquals(HistorySection.OLDER, HistoryTime.section(beyond30, now.toEpochMilli()))
}
fun `test list is focusable and uses multiple interval selection`() {
val panel = HistoryPanel(parent, controller())
flush()