fix(jetbrains): increase user prompt transcript gap

This commit is contained in:
kirillk
2026-07-09 14:09:57 -04:00
parent a29baa9ff1
commit 8ea3f10495
8 changed files with 104 additions and 4 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/kilo-jetbrains": patch
---
Increase spacing before non-initial user prompts in the JetBrains session transcript.
@@ -44,7 +44,7 @@ class SessionLayout(
var first = true
for (comp in parent.components) {
if (!comp.isVisible) continue
if (!first) h += gap
if (!first) h += gap(comp)
first = false
val child = bounds(ins, w, comp)
// Pre-size to available width so HTML panes reflow before we measure
@@ -63,7 +63,7 @@ class SessionLayout(
var first = true
for (comp in parent.components) {
if (!comp.isVisible) continue
if (!first) y += gap
if (!first) y += gap(comp)
first = false
val child = bounds(ins, w, comp)
// Fix width first so HTML reflows, then read the resulting height
@@ -75,7 +75,7 @@ class SessionLayout(
}
private fun bounds(ins: Insets, width: Int, comp: Component): Bounds {
val view = comp as? SessionView ?: return Bounds(ins.left, width)
val view = view(comp) ?: return Bounds(ins.left, width)
if (view.sessionViewKind != SessionView.Kind.UserPrompt) return Bounds(ins.left, width)
val shift = JBUI.scale(SessionUiStyle.SessionLayout.USER_PROMPT_INDENT)
val next = width - shift
@@ -93,6 +93,13 @@ class SessionLayout(
)
}
private fun gap(comp: Component): Int {
if (view(comp)?.sessionGapKind == SessionView.Kind.UserPrompt) return JBUI.scale(SessionUiStyle.SessionLayout.USER_PROMPT_GAP)
return gap
}
private fun view(comp: Component): SessionView? = comp as? SessionView
private data class Bounds(val left: Int, val width: Int)
}
@@ -2,6 +2,7 @@ package ai.kilocode.client.session.ui
interface SessionView {
val sessionViewKind: Kind
val sessionGapKind: Kind get() = sessionViewKind
enum class Kind {
Default,
@@ -16,6 +16,7 @@ object SessionUiStyle {
/** Geometry for the transcript list and its scroll behavior. */
object SessionLayout {
const val GAP = 3
const val USER_PROMPT_GAP = 10
const val TRANSCRIPT_SCROLLBAR_PADDING = 10
val InnerInsets = Insets(
UiStyle.Gap.md(),
@@ -4,6 +4,7 @@ import ai.kilocode.client.session.SessionFileOpener
import ai.kilocode.client.session.model.FileAttachment
import ai.kilocode.client.session.model.Message
import ai.kilocode.client.session.ui.SessionLayoutPanel
import ai.kilocode.client.session.ui.SessionView
import ai.kilocode.client.session.ui.style.SessionEditorStyle
import ai.kilocode.client.session.ui.selection.SessionSelection
import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget
@@ -35,10 +36,15 @@ class TurnView(
private val repo: String? = null,
private val hover: ((PartView, Boolean) -> Unit)? = null,
private val revert: ((String) -> Unit)? = null,
) : SessionLayoutPanel(JBUI.scale(SessionUiStyle.SessionLayout.GAP)), Disposable, SessionEditorStyleTarget {
) : SessionLayoutPanel(JBUI.scale(SessionUiStyle.SessionLayout.GAP)), Disposable, SessionEditorStyleTarget, SessionView {
private val messages = LinkedHashMap<String, MessageView>()
override val sessionViewKind = SessionView.Kind.Default
override val sessionGapKind: SessionView.Kind
get() = messages.values.firstOrNull { it.isVisible }?.sessionViewKind ?: SessionView.Kind.Default
init {
isOpaque = false
}
@@ -11,10 +11,12 @@ import ai.kilocode.client.session.ui.ConnectionPanel
import ai.kilocode.client.session.ui.empty.EmptySessionPanel
import ai.kilocode.client.session.ui.LoadingPanel
import ai.kilocode.client.session.ui.SessionDropOverlay
import ai.kilocode.client.session.ui.SessionLayoutPanel
import ai.kilocode.client.session.ui.prompt.PromptPanel
import ai.kilocode.client.session.ui.account.SessionAccountOverlay
import ai.kilocode.client.session.ui.SessionMessageListPanel
import ai.kilocode.client.session.ui.SessionRootPanel
import ai.kilocode.client.session.ui.SessionView
import ai.kilocode.client.session.ui.style.SessionEditorStyle
import ai.kilocode.client.session.ui.header.SessionHeaderPanel
import ai.kilocode.client.session.ui.style.SessionUiStyle
@@ -29,7 +31,9 @@ import ai.kilocode.client.session.views.permission.PermissionView
import ai.kilocode.client.session.views.question.QuestionView
import ai.kilocode.rpc.dto.MessageWithPartsDto
import com.intellij.ui.components.JBScrollPane
import java.awt.Dimension
import javax.swing.JLayeredPane
import javax.swing.JPanel
@Suppress("UnstableApiUsage")
class SessionUiLayoutTest : SessionUiTestBase() {
@@ -73,6 +77,24 @@ class SessionUiLayoutTest : SessionUiTestBase() {
assertFalse(root.content.components.contains(connection))
}
fun `test transcript uses larger standard gap before prompts after first item`() {
val panel = SessionLayoutPanel(gap = SessionUiStyle.SessionLayout.GAP).apply {
setSize(400, 300)
add(Row(SessionView.Kind.UserPrompt))
add(Row(SessionView.Kind.Default))
add(Row(SessionView.Kind.UserPrompt))
}
panel.doLayout()
assertEquals(0, panel.getComponent(0).y)
assertEquals(SessionUiStyle.SessionLayout.GAP, panel.getComponent(1).y - panel.getComponent(0).bounds.maxY.toInt())
assertEquals(
JBUI.scale(SessionUiStyle.SessionLayout.USER_PROMPT_GAP),
panel.getComponent(2).y - panel.getComponent(1).bounds.maxY.toInt(),
)
}
fun `test drop overlay is attached under root overlay layer`() {
val root = find<SessionRootPanel>(ui)
val drop = find<SessionDropOverlay>(ui)
@@ -640,4 +662,8 @@ class SessionUiLayoutTest : SessionUiTestBase() {
.components
.single()
.let { it as javax.swing.JComponent }
private class Row(override val sessionViewKind: SessionView.Kind) : JPanel(), SessionView {
override fun getPreferredSize() = Dimension(100, 10)
}
}
@@ -1,5 +1,6 @@
package ai.kilocode.client.session.ui
import ai.kilocode.client.session.ui.style.SessionUiStyle
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.components.BorderLayoutPanel
@@ -68,6 +69,29 @@ class SessionLayoutTest : BasePlatformTestCase() {
assertEquals(33, c3.y) // 10 + 4 + 15 + 4
}
fun `test user prompt after first component uses prompt gap`() {
val p = panel(gap = 4, width = 300)
val c1 = label(height = 20)
val c2 = view(height = 30, kind = SessionView.Kind.UserPrompt)
p.add(c1)
p.add(c2)
p.doLayout()
assertEquals(20 + JBUI.scale(SessionUiStyle.SessionLayout.USER_PROMPT_GAP), c2.y)
}
fun `test first user prompt uses standard gap`() {
val p = panel(gap = 4, width = 300)
val c1 = view(height = 20, kind = SessionView.Kind.UserPrompt)
val c2 = label(height = 30)
p.add(c1)
p.add(c2)
p.doLayout()
assertEquals(0, c1.y)
assertEquals(20 + 4, c2.y)
}
fun `test all children receive full available width`() {
val p = panel(width = 500)
val c1 = label(height = 20)
@@ -199,6 +223,16 @@ class SessionLayoutTest : BasePlatformTestCase() {
assertEquals(10 + 4 + 15 + 4 + 20, size.height)
}
fun `test preferredLayoutSize uses prompt gap before non-first user prompt`() {
val p = panel(gap = 4, width = 300)
p.add(label(height = 10))
p.add(view(height = 15, kind = SessionView.Kind.UserPrompt))
p.doLayout()
val size = p.layout.preferredLayoutSize(p)
assertEquals(10 + JBUI.scale(SessionUiStyle.SessionLayout.USER_PROMPT_GAP) + 15, size.height)
}
fun `test preferredLayoutSize with no children is zero`() {
val p = panel(width = 300)
val size = p.layout.preferredLayoutSize(p)
@@ -42,6 +42,7 @@ import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBScrollPane
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import java.awt.BorderLayout
import java.awt.Color
@@ -105,6 +106,25 @@ class SessionMessageListPanelTest : BasePlatformTestCase() {
assertEquals(right, left)
}
fun `test top level user turns use prompt gap after previous turn`() {
model.upsertMessage(msg("u1", "user"))
model.updateContent("u1", part("p1", "u1", "text", text = "first"))
model.upsertMessage(msg("a1", "assistant"))
model.updateContent("a1", part("p2", "a1", "text", text = "answer"))
model.upsertMessage(msg("u2", "user"))
model.updateContent("u2", part("p3", "u2", "text", text = "second"))
panel.setSize(600, 1000)
layout(panel)
val first = panel.findTurn("u1")!!
val second = panel.findTurn("u2")!!
assertEquals(
JBUI.scale(SessionUiStyle.SessionLayout.USER_PROMPT_GAP),
second.y - first.bounds.maxY.toInt(),
)
}
// ------ TurnAdded ------
fun `test user message creates turn and is findable by message id`() {