mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-18 09:24:02 +08:00
feat(jetbrains): refresh session UI colors and fix sticky header hover
Consolidate the session palette around three tunable roles: session background (the whole backdrop), code-block background (code blocks, tool/shell output, the user prompt bubble, and the prompt input), and foreground (with hint derived from it). Containers stay transparent over the backdrop and raised surfaces share the one code-block key, so the transcript reads as a single dialog-like surface. Apply the style correctly on first paint: re-run applyStyle on attach and propagate it to the dynamically shown empty/recent view, instead of only correcting colors after a theme switch. Fix tool-row headers keeping a hover tint after the pointer left the row. Hover was bound only to enumerated header elements, so leaving through an unbound nested child (e.g. the file-name link) never delivered mouseExited. Track hover across the whole header subtree, following children added later, while keeping click/cursor binding on the enumerated set so a link click still opens the file rather than toggling the card.
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Refresh the session UI colors around three tunable roles — session background, code-block background (shared by code blocks, the prompt bubble, and the input), and foreground — and apply them correctly on first paint instead of only after switching themes.
|
||||
+11
-5
@@ -234,6 +234,10 @@ class SessionUi(
|
||||
override fun addNotify() {
|
||||
if (disposed) return
|
||||
super.addNotify()
|
||||
// First realized paint: the constructor's applyStyle can run before the tool window is
|
||||
// attached, when panel/editor colors are still provisional. Re-apply from the live scheme
|
||||
// so the session is styled correctly without needing a theme toggle to trigger it.
|
||||
applyStyle(SessionEditorStyle.current())
|
||||
resumeOpen()
|
||||
}
|
||||
|
||||
@@ -988,16 +992,18 @@ class SessionUi(
|
||||
selection.applyStyle(style)
|
||||
editorTheme = style.editorScheme
|
||||
colorTheme = UIManager.getLookAndFeel()
|
||||
background = style.editorBackground
|
||||
root.background = style.editorBackground
|
||||
root.content.background = style.editorBackground
|
||||
sessionContent.background = style.editorBackground
|
||||
blankBody.background = style.editorBackground
|
||||
val bg = SessionUiStyle.Colors.sessionBackground()
|
||||
background = bg
|
||||
root.background = bg
|
||||
root.content.background = bg
|
||||
sessionContent.background = bg
|
||||
blankBody.background = bg
|
||||
load.applyStyle(style)
|
||||
header.applyStyle(style)
|
||||
prompt.applyStyle(style)
|
||||
connection.applyStyle(style)
|
||||
scroll.applyStyle(style)
|
||||
empty?.applyStyle(style)
|
||||
refresh()
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -5,6 +5,7 @@ import ai.kilocode.client.session.ui.SessionMessageListPanel
|
||||
import ai.kilocode.client.session.ui.SessionRootPanel
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.ui.components.JBLabel
|
||||
@@ -223,8 +224,9 @@ internal class SessionScroll(
|
||||
@RequiresEdt
|
||||
fun applyStyle(style: SessionEditorStyle) {
|
||||
this.style = style
|
||||
component.background = style.editorBackground
|
||||
component.viewport.background = style.editorBackground
|
||||
val bg = SessionUiStyle.Colors.sessionBackground()
|
||||
component.background = bg
|
||||
component.viewport.background = bg
|
||||
syncIcon()
|
||||
messages.applyStyle(style)
|
||||
val view = component.viewport.view
|
||||
|
||||
+4
-4
@@ -64,7 +64,7 @@ class ConnectionPanel(
|
||||
}
|
||||
|
||||
private val label = JBLabel().apply {
|
||||
foreground = UiStyle.Colors.weak()
|
||||
foreground = SessionUiStyle.Colors.hint()
|
||||
addMouseListener(click)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class ConnectionPanel(
|
||||
isOpaque = false
|
||||
lineWrap = true
|
||||
wrapStyleWord = true
|
||||
foreground = UiStyle.Colors.fg()
|
||||
foreground = SessionUiStyle.Colors.foreground()
|
||||
}
|
||||
|
||||
private val scroll = JBScrollPane(details).apply {
|
||||
@@ -136,7 +136,7 @@ class ConnectionPanel(
|
||||
}
|
||||
|
||||
private fun showConnecting() {
|
||||
label.foreground = UiStyle.Colors.weak()
|
||||
label.foreground = SessionUiStyle.Colors.hint()
|
||||
label.text = KiloBundle.message("session.connection.connecting")
|
||||
detail = null
|
||||
expanded = false
|
||||
@@ -147,7 +147,7 @@ class ConnectionPanel(
|
||||
}
|
||||
|
||||
private fun showDownloading(percent: Int, version: String?, platform: String?) {
|
||||
label.foreground = UiStyle.Colors.weak()
|
||||
label.foreground = SessionUiStyle.Colors.hint()
|
||||
val pct = percent.coerceIn(0, 100)
|
||||
label.text = if (version != null && platform != null) {
|
||||
KiloBundle.message("session.connection.downloading.version", version, platform, pct)
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ import ai.kilocode.client.plugin.KiloBundle
|
||||
import ai.kilocode.client.session.model.SessionState
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.util.ui.Centerizer
|
||||
@@ -33,7 +34,7 @@ class LoadingPanel : JPanel(BorderLayout()), SessionEditorStyleTarget {
|
||||
|
||||
else -> {
|
||||
label.text = KiloBundle.message("session.empty.loading")
|
||||
label.foreground = UiStyle.Colors.weak()
|
||||
label.foreground = SessionUiStyle.Colors.hint()
|
||||
}
|
||||
}
|
||||
revalidate()
|
||||
|
||||
+3
-3
@@ -156,7 +156,7 @@ class ModifiedFilesView private constructor(
|
||||
}
|
||||
val panel = popup.mountFiles(files)
|
||||
popup.applyStyle(style)
|
||||
return HeaderPopupBody(panel, owner, style.editorBackground, SessionUiStyle.View.Popup.WIDE_MAX_WIDTH)
|
||||
return HeaderPopupBody(panel, owner, SessionUiStyle.Colors.codeBlockBackground(), SessionUiStyle.View.Popup.WIDE_MAX_WIDTH)
|
||||
}
|
||||
|
||||
private class Header {
|
||||
@@ -189,8 +189,8 @@ class ModifiedFilesView private constructor(
|
||||
setForeground(glyph, SessionUiStyle.View.Tool.completed())
|
||||
setFont(title, style.boldEditorFont)
|
||||
setFont(count, style.transcriptFont)
|
||||
setForeground(title, UiStyle.Colors.fg())
|
||||
setForeground(count, UiStyle.Colors.weak())
|
||||
setForeground(title, SessionUiStyle.Colors.foreground())
|
||||
setForeground(count, SessionUiStyle.Colors.hint())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -6,6 +6,7 @@ import ai.kilocode.client.session.model.SessionModelEvent
|
||||
import ai.kilocode.client.session.model.SessionState
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import ai.kilocode.client.ui.layout.Stack
|
||||
import ai.kilocode.client.ui.layout.StackAxis
|
||||
@@ -44,7 +45,7 @@ class ProgressPanel(
|
||||
foreground = style.editorForeground
|
||||
}
|
||||
private val elapsed = JBLabel().apply {
|
||||
foreground = UiStyle.Colors.weak()
|
||||
foreground = SessionUiStyle.Colors.hint()
|
||||
}
|
||||
private val spinner = JBLabel(AnimatedIcon.Default())
|
||||
private val tick = clock.timer(1000) { syncElapsed() }
|
||||
@@ -140,7 +141,7 @@ class ProgressPanel(
|
||||
this.style = style
|
||||
label.font = style.regularFont
|
||||
elapsed.font = style.regularFont
|
||||
elapsed.foreground = UiStyle.Colors.weak()
|
||||
elapsed.foreground = SessionUiStyle.Colors.hint()
|
||||
if (state is SessionState.Busy) label.foreground = style.editorForeground
|
||||
revalidate()
|
||||
repaint()
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import ai.kilocode.client.session.model.SessionModel
|
||||
import ai.kilocode.client.session.model.SessionState
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.session.views.SessionViewIcons
|
||||
import ai.kilocode.client.session.views.base.BaseQuestionView
|
||||
import ai.kilocode.client.session.views.base.PartHeader
|
||||
@@ -170,7 +171,7 @@ class RevertBanner(
|
||||
override fun applyStyle(style: SessionEditorStyle) {
|
||||
card.applyStyle(style)
|
||||
title.font = style.headerFont
|
||||
title.foreground = UiStyle.Colors.fg()
|
||||
title.foreground = SessionUiStyle.Colors.foreground()
|
||||
progress?.applyStyle(style)
|
||||
hint.foreground = UIUtil.getLabelForeground()
|
||||
notice.foreground = UIUtil.getContextHelpForeground()
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ package ai.kilocode.client.session.ui
|
||||
import ai.kilocode.client.plugin.KiloBundle
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import ai.kilocode.client.ui.layout.Stack
|
||||
import com.intellij.ui.AnimatedIcon
|
||||
@@ -36,7 +37,7 @@ class RevertProgress(onCancel: () -> Unit) : JPanel(), SessionEditorStyleTarget
|
||||
override fun applyStyle(style: SessionEditorStyle) {
|
||||
this.style = style
|
||||
label.font = style.regularFont
|
||||
label.foreground = UiStyle.Colors.fg()
|
||||
label.foreground = SessionUiStyle.Colors.foreground()
|
||||
cancel.font = style.regularFont
|
||||
revalidate()
|
||||
repaint()
|
||||
|
||||
+1
-1
@@ -610,7 +610,7 @@ class SessionMessageListPanel(
|
||||
|
||||
override fun applyStyle(style: SessionEditorStyle) {
|
||||
this.style = style
|
||||
background = style.editorBackground
|
||||
background = SessionUiStyle.Colors.sessionBackground()
|
||||
for (view in turnViews.values) view.applyStyle(style)
|
||||
question?.applyStyle(style)
|
||||
permission?.applyStyle(style)
|
||||
|
||||
+7
-1
@@ -1,5 +1,6 @@
|
||||
package ai.kilocode.client.session.ui
|
||||
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.LayeredOverlayPanel
|
||||
|
||||
class SessionRootPanel(
|
||||
@@ -12,5 +13,10 @@ class SessionRootPanel(
|
||||
|
||||
class Overlay : LayeredOverlayPanel.Overlay()
|
||||
|
||||
class Blocker : LayeredOverlayPanel.Blocker()
|
||||
class Blocker : LayeredOverlayPanel.Blocker() {
|
||||
override fun updateUI() {
|
||||
super.updateUI()
|
||||
background = SessionUiStyle.Colors.sessionBackground()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -183,7 +183,7 @@ open class AttachmentCard(
|
||||
try {
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
|
||||
val arc = JBUI.scale(SessionUiStyle.View.Attachment.CORNER_ARC)
|
||||
g2.color = SessionUiStyle.View.Surface.bgColor()
|
||||
g2.color = SessionUiStyle.Colors.codeBlockBackground()
|
||||
g2.fillRoundRect(0, 0, width, height, arc, arc)
|
||||
g2.color = SessionUiStyle.View.Outline.color()
|
||||
g2.drawRoundRect(0, 0, width - 1, height - 1, arc, arc)
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package ai.kilocode.client.session.ui.header
|
||||
|
||||
import ai.kilocode.client.plugin.KiloBundle
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.DiffStatBadge
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import ai.kilocode.client.ui.layout.Stack
|
||||
@@ -65,7 +66,7 @@ internal class BranchChangesBadge(
|
||||
|
||||
fun applyStyle(style: SessionEditorStyle) {
|
||||
count.font = style.smallFont
|
||||
count.foreground = UiStyle.Colors.weak()
|
||||
count.foreground = SessionUiStyle.Colors.hint()
|
||||
}
|
||||
|
||||
override fun getPreferredSize(): Dimension {
|
||||
|
||||
+5
-3
@@ -3,6 +3,7 @@ package ai.kilocode.client.session.ui.header
|
||||
import ai.kilocode.client.plugin.KiloBundle
|
||||
import ai.kilocode.client.session.model.ContextUsage
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.util.ui.JBUI
|
||||
@@ -41,9 +42,10 @@ internal class ContextBar : JPanel(BorderLayout(UiStyle.Gap.md(), 0)) {
|
||||
}
|
||||
|
||||
fun applyStyle(style: SessionEditorStyle) {
|
||||
background = style.editorBackground
|
||||
val bg = SessionUiStyle.Colors.sessionBackground()
|
||||
background = bg
|
||||
foreground = style.editorForeground
|
||||
meter.background = style.editorBackground
|
||||
meter.background = bg
|
||||
used.font = style.smallFont
|
||||
used.foreground = style.editorForeground
|
||||
limit.font = style.smallFont
|
||||
@@ -143,7 +145,7 @@ private class Meter : JComponent() {
|
||||
fun reservedColor(): Color = shade(0.28f)
|
||||
|
||||
private fun shade(alpha: Float): Color {
|
||||
val base = background ?: UiStyle.Colors.editorBackground()
|
||||
val base = background ?: SessionUiStyle.Colors.sessionBackground()
|
||||
val grey = if (UiStyle.Colors.bright(base)) Color.BLACK else Color.WHITE
|
||||
return UiStyle.Colors.blend(base, grey, alpha)
|
||||
}
|
||||
|
||||
+11
-10
@@ -312,20 +312,21 @@ class SessionHeaderPanel(
|
||||
|
||||
override fun applyStyle(style: SessionEditorStyle) {
|
||||
this.style = style
|
||||
background = style.editorBackground
|
||||
val bg = SessionUiStyle.Colors.sessionBackground()
|
||||
background = bg
|
||||
foreground = style.editorForeground
|
||||
top.background = style.editorBackground
|
||||
top.background = bg
|
||||
top.isOpaque = true
|
||||
top.border = JBUI.Borders.empty(UiStyle.Gap.md(), UiStyle.Gap.sm(), UiStyle.Gap.md(), UiStyle.Gap.sm())
|
||||
centerGroup.background = style.editorBackground
|
||||
centerGroup.background = bg
|
||||
centerGroup.isOpaque = true
|
||||
right.background = style.editorBackground
|
||||
changes.background = style.editorBackground
|
||||
tokens.background = style.editorBackground
|
||||
todoRow.background = style.editorBackground
|
||||
todoBox.background = style.editorBackground
|
||||
body.background = style.editorBackground
|
||||
viewport.background = style.editorBackground
|
||||
right.background = bg
|
||||
changes.background = bg
|
||||
tokens.background = bg
|
||||
todoRow.background = bg
|
||||
todoBox.background = bg
|
||||
body.background = bg
|
||||
viewport.background = bg
|
||||
title.font = style.boldFont
|
||||
title.foreground = style.editorForeground
|
||||
changes.applyStyle(style)
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ package ai.kilocode.client.session.ui.mode
|
||||
|
||||
import ai.kilocode.client.plugin.KiloBundle
|
||||
import ai.kilocode.client.session.ui.PickerRow
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.FilledBadgeIcon
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import com.intellij.icons.AllIcons
|
||||
@@ -72,7 +73,7 @@ internal class ModePickerRenderer(
|
||||
): JPanel {
|
||||
val focus = selected || list.hasFocus() || focused
|
||||
val fg = UIUtil.getListForeground(selected, focus)
|
||||
val weak = if (selected) fg else UiStyle.Colors.weak()
|
||||
val weak = if (selected) fg else SessionUiStyle.Colors.hint()
|
||||
|
||||
background = list.background
|
||||
wrap.update(list, selected, focus)
|
||||
|
||||
+3
-2
@@ -2,6 +2,7 @@ package ai.kilocode.client.session.ui.model
|
||||
|
||||
import ai.kilocode.client.plugin.KiloBundle
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.FilledBadgeIcon
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import ai.kilocode.client.ui.layout.Stack
|
||||
@@ -372,13 +373,13 @@ private fun descriptionText(value: String): String = value
|
||||
private class TagStyle(private val bg: Color) : UiStyle.Badge.Style {
|
||||
override fun bg() = bg
|
||||
|
||||
override fun fg() = UiStyle.Colors.fg()
|
||||
override fun fg() = SessionUiStyle.Colors.foreground()
|
||||
}
|
||||
|
||||
private fun tagStyle(index: Int): UiStyle.Badge.Style {
|
||||
if (index % 4 == 0) return UiStyle.Badge.Secondary
|
||||
val bg = when (index % 4) {
|
||||
1 -> UiStyle.Colors.blend(UiStyle.Colors.contentBackground(), UiStyle.Colors.fg(), 0.12f)
|
||||
1 -> UiStyle.Colors.blend(UiStyle.Colors.contentBackground(), SessionUiStyle.Colors.foreground(), 0.12f)
|
||||
2 -> UiStyle.Colors.blend(UiStyle.Colors.contentBackground(), JBUI.CurrentTheme.Link.Foreground.ENABLED, 0.18f)
|
||||
else -> UiStyle.Colors.blend(UiStyle.Colors.contentBackground(), UiStyle.Badge.Primary.bg(), 0.18f)
|
||||
}
|
||||
|
||||
+41
-18
@@ -1,6 +1,7 @@
|
||||
package ai.kilocode.client.session.ui.style
|
||||
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.UIUtil
|
||||
@@ -8,8 +9,38 @@ import java.awt.Color
|
||||
|
||||
/** Static style tokens owned by the chat session UI. */
|
||||
object SessionUiStyle {
|
||||
/**
|
||||
* The session palette is driven by three authored keys; everything else is derived:
|
||||
* - [sessionBackground] paints the whole session backdrop (containers stay transparent over it).
|
||||
* - [codeBlockBackground] is the single raised surface (code blocks, tool/shell output, prompt bubble, prompt input).
|
||||
* - [foreground] is normal text and links; [hint] is derived from it.
|
||||
*/
|
||||
object Colors {
|
||||
private const val HINT_BOOST = 0.35f
|
||||
|
||||
fun sessionBackground(): Color = JBColor.namedColor(
|
||||
"Kilo.Session.background",
|
||||
UiStyle.Colors.bg(),
|
||||
)
|
||||
|
||||
fun codeBlockBackground(): Color = JBColor.namedColor(
|
||||
"Kilo.Session.codeBlockBackground",
|
||||
JBColor.lazy { UiStyle.Colors.codeBlockBackground(EditorColorsManager.getInstance().globalScheme) },
|
||||
)
|
||||
|
||||
fun foreground(): Color = JBColor.namedColor(
|
||||
"Kilo.Session.foreground",
|
||||
UiStyle.Colors.fg(),
|
||||
)
|
||||
|
||||
fun hint(): Color = JBColor.namedColor(
|
||||
"Kilo.Session.hintForeground",
|
||||
UiStyle.Colors.blend(UiStyle.Colors.weak(), foreground(), HINT_BOOST),
|
||||
)
|
||||
}
|
||||
|
||||
object Transcript {
|
||||
fun bgColor(): Color = UiStyle.Colors.bg()
|
||||
fun bgColor(): Color = Colors.sessionBackground()
|
||||
}
|
||||
|
||||
/** Geometry for the transcript list and its scroll behavior. */
|
||||
@@ -69,13 +100,13 @@ object SessionUiStyle {
|
||||
internal const val HOVER_FILL_ALPHA = 0.10f
|
||||
|
||||
object Surface {
|
||||
fun bgColor(): Color = UiStyle.Colors.editorBackground()
|
||||
fun bgColor(): Color = Colors.sessionBackground()
|
||||
|
||||
fun headerBgColor(): Color = UiStyle.Colors.editorBackground()
|
||||
fun headerBgColor(): Color = Colors.sessionBackground()
|
||||
|
||||
/** Subtle hover fill, softer than the session-view outline. */
|
||||
fun headerHoverBgColor(): Color = JBColor.lazy {
|
||||
UiStyle.Colors.blend(headerBgColor(), Outline.hoverColor(), HOVER_FILL_ALPHA)
|
||||
UiStyle.Colors.blend(Colors.sessionBackground(), Outline.hoverColor(), HOVER_FILL_ALPHA)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +114,7 @@ object SessionUiStyle {
|
||||
fun color(): Color = UiStyle.Colors.contentBorder()
|
||||
|
||||
fun brightColor(): Color = JBColor.lazy {
|
||||
UiStyle.Colors.contrast(UiStyle.Colors.editorBackground(), BORDER_DELTA)
|
||||
UiStyle.Colors.contrast(Colors.sessionBackground(), BORDER_DELTA)
|
||||
}
|
||||
|
||||
/** Subtle hover outline, stronger than the hover fill. */
|
||||
@@ -96,15 +127,7 @@ object SessionUiStyle {
|
||||
|
||||
/** Prompt input dimensions and chrome inside the session view. */
|
||||
object Prompt {
|
||||
/**
|
||||
* Background of the prompt input and the transcript user-prompt bubble. Uses a dedicated
|
||||
* theme key so the prompt surface can be restyled independently, defaulting to the
|
||||
* code-fragment background so the prompt matches rendered code blocks.
|
||||
*/
|
||||
fun bgColor(style: SessionEditorStyle): Color = JBColor.namedColor(
|
||||
"Kilo.Session.Prompt.Background",
|
||||
UiStyle.Colors.codeBlockBackground(style.editorScheme),
|
||||
)
|
||||
fun bgColor(_style: SessionEditorStyle): Color = Colors.codeBlockBackground()
|
||||
|
||||
const val EDITOR_LINES = 1
|
||||
const val EDITOR_CHROME = 16
|
||||
@@ -145,7 +168,7 @@ object SessionUiStyle {
|
||||
private const val SCRIM_ALPHA = 210
|
||||
|
||||
fun scrim(): Color = JBColor.lazy {
|
||||
val bg = UiStyle.Colors.bg()
|
||||
val bg = Colors.sessionBackground()
|
||||
Color(bg.red, bg.green, bg.blue, SCRIM_ALPHA)
|
||||
}
|
||||
|
||||
@@ -220,11 +243,11 @@ object SessionUiStyle {
|
||||
*/
|
||||
const val DIFF_MAX_LINES = 2_000
|
||||
|
||||
fun pending(): Color = UiStyle.Colors.weak()
|
||||
fun pending(): Color = Colors.hint()
|
||||
|
||||
fun running(): Color = UiStyle.Colors.fg()
|
||||
fun running(): Color = Colors.foreground()
|
||||
|
||||
fun completed(): Color = UiStyle.Colors.weak()
|
||||
fun completed(): Color = Colors.hint()
|
||||
|
||||
fun error(): Color = UiStyle.Colors.errorLabelForeground()
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class CompactionView(@Suppress("UNUSED_PARAMETER") compaction: Compaction) : Par
|
||||
|
||||
override val contentId: String = compaction.id
|
||||
private val text = JBLabel(KiloBundle.message("session.part.compaction")).apply {
|
||||
foreground = UiStyle.Colors.weak()
|
||||
foreground = SessionUiStyle.Colors.hint()
|
||||
horizontalAlignment = SwingConstants.CENTER
|
||||
border = JBUI.Borders.empty(0, UiStyle.Gap.lg())
|
||||
}
|
||||
|
||||
+1
-1
@@ -486,7 +486,7 @@ class MessageView(
|
||||
g2.fillRoundRect(pt.x, pt.y, box.width, box.height, arc, arc)
|
||||
// When the prompt shares the session background there is no fill contrast, so draw the
|
||||
// outline to keep the bubble visible.
|
||||
if (bg.rgb == style.editorBackground.rgb) {
|
||||
if (bg.rgb == SessionUiStyle.Colors.sessionBackground().rgb) {
|
||||
val w = box.width - 1
|
||||
val h = box.height - 1
|
||||
g2.color = SessionUiStyle.View.Outline.color()
|
||||
|
||||
+11
-11
@@ -245,8 +245,8 @@ class ReasoningView(
|
||||
md.font = font
|
||||
changed = md.codeFont != style.editorFamily || changed
|
||||
md.codeFont = style.editorFamily
|
||||
changed = md.foreground.rgb != UiStyle.Colors.weak().rgb || changed
|
||||
md.foreground = UiStyle.Colors.weak()
|
||||
changed = md.foreground.rgb != SessionUiStyle.Colors.hint().rgb || changed
|
||||
md.foreground = SessionUiStyle.Colors.hint()
|
||||
return changed
|
||||
}
|
||||
|
||||
@@ -280,22 +280,22 @@ class ReasoningView(
|
||||
md.applyStyle(style)
|
||||
md.font = style.smallEditorFont.deriveFont(Font.ITALIC)
|
||||
md.codeFont = style.editorFamily
|
||||
md.foreground = UiStyle.Colors.weak()
|
||||
md.background = style.editorBackground
|
||||
md.foreground = SessionUiStyle.Colors.hint()
|
||||
md.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
md.component.border = JBUI.Borders.empty()
|
||||
md.set(text)
|
||||
// The shared popup wrapper (HeaderPopupBody) provides the scroll pane, so pass the content
|
||||
// panel directly instead of nesting a second scroll pane here.
|
||||
val panel = TrackPanel().apply {
|
||||
isOpaque = true
|
||||
background = style.editorBackground
|
||||
background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
border = JBUI.Borders.empty(
|
||||
JBUI.scale(SessionUiStyle.View.Reasoning.BODY_VERTICAL_PADDING),
|
||||
JBUI.scale(SessionUiStyle.View.Reasoning.BODY_HORIZONTAL_PADDING),
|
||||
)
|
||||
add(md.component, BorderLayout.CENTER)
|
||||
}
|
||||
return HeaderPopupBody(panel, md, style.editorBackground)
|
||||
return HeaderPopupBody(panel, md, SessionUiStyle.Colors.codeBlockBackground())
|
||||
}
|
||||
|
||||
private fun bodyMaxHeight(): Int {
|
||||
@@ -360,7 +360,7 @@ class ReasoningParts(
|
||||
}
|
||||
val panel = TrackPanel().apply {
|
||||
isOpaque = true
|
||||
background = SessionUiStyle.View.Surface.bgColor()
|
||||
background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
border = JBUI.Borders.empty(
|
||||
JBUI.scale(SessionUiStyle.View.Reasoning.BODY_VERTICAL_PADDING),
|
||||
JBUI.scale(SessionUiStyle.View.Reasoning.BODY_HORIZONTAL_PADDING),
|
||||
@@ -370,8 +370,8 @@ class ReasoningParts(
|
||||
val scroll = JBScrollPane(panel).apply {
|
||||
border = JBUI.Borders.empty()
|
||||
isOpaque = true
|
||||
background = SessionUiStyle.View.Surface.bgColor()
|
||||
viewport.background = SessionUiStyle.View.Surface.bgColor()
|
||||
background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
viewport.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
|
||||
verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED
|
||||
}
|
||||
@@ -386,8 +386,8 @@ class ReasoningBody(
|
||||
)
|
||||
|
||||
private fun reasoningParts(selection: SessionSelection? = null): ReasoningParts {
|
||||
val title = JBLabel(KiloBundle.message("session.part.reasoning")).apply { foreground = UiStyle.Colors.weak() }
|
||||
val icon = JBLabel(SessionViewIcons.brain).apply { foreground = UiStyle.Colors.weak() }
|
||||
val title = JBLabel(KiloBundle.message("session.part.reasoning")).apply { foreground = SessionUiStyle.Colors.hint() }
|
||||
val icon = JBLabel(SessionViewIcons.brain).apply { foreground = SessionUiStyle.Colors.hint() }
|
||||
val header = PartHeader().apply {
|
||||
leading(icon)
|
||||
left(title)
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ import ai.kilocode.client.session.openSessionLink
|
||||
import ai.kilocode.client.session.model.Content
|
||||
import ai.kilocode.client.session.model.Text
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.session.ui.selection.SessionCopyTarget
|
||||
import ai.kilocode.client.session.ui.selection.SessionSelection
|
||||
import ai.kilocode.client.session.views.base.PartView
|
||||
@@ -124,7 +125,7 @@ open class TextView(
|
||||
|
||||
protected open fun styleFont(style: SessionEditorStyle) = style.transcriptFont
|
||||
|
||||
protected open fun styleBackground(style: SessionEditorStyle) = style.editorBackground
|
||||
protected open fun styleBackground(style: SessionEditorStyle) = SessionUiStyle.Colors.codeBlockBackground()
|
||||
|
||||
protected fun refresh() {
|
||||
revalidate()
|
||||
|
||||
+33
-5
@@ -6,7 +6,10 @@ import com.intellij.ui.components.JBLabel
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Color
|
||||
import java.awt.Component
|
||||
import java.awt.Container
|
||||
import java.awt.Cursor
|
||||
import java.awt.event.ContainerAdapter
|
||||
import java.awt.event.ContainerEvent
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.JComponent
|
||||
@@ -30,6 +33,7 @@ abstract class AbstractSessionPartView(
|
||||
protected val arrow = JBLabel()
|
||||
protected val row = JPanel(BorderLayout(SessionUiStyle.View.Header.gap(), 0))
|
||||
private val bound = linkedSetOf<Component>()
|
||||
private val watched = linkedSetOf<Component>()
|
||||
private var body: JComponent? = null
|
||||
|
||||
private val click = object : MouseAdapter() {
|
||||
@@ -38,7 +42,10 @@ abstract class AbstractSessionPartView(
|
||||
toggle()
|
||||
}
|
||||
}
|
||||
private val mouse = object : MouseAdapter() {
|
||||
// Hover is tracked across the whole header subtree so leaving the row via any nested
|
||||
// element (e.g. an unbound file link) still clears the hover fill. Swing only delivers
|
||||
// mouseExited to the deepest component, so a single listener on the row is not enough.
|
||||
private val pointer = object : MouseAdapter() {
|
||||
override fun mouseEntered(e: MouseEvent) {
|
||||
setHovered(true)
|
||||
}
|
||||
@@ -48,6 +55,10 @@ abstract class AbstractSessionPartView(
|
||||
setHovered(false)
|
||||
}
|
||||
}
|
||||
private val nested = object : ContainerAdapter() {
|
||||
override fun componentAdded(e: ContainerEvent) = watch(e.child)
|
||||
override fun componentRemoved(e: ContainerEvent) = unwatch(e.child)
|
||||
}
|
||||
|
||||
init {
|
||||
layout = BorderLayout()
|
||||
@@ -56,6 +67,7 @@ abstract class AbstractSessionPartView(
|
||||
row.add(arrow, BorderLayout.EAST)
|
||||
add(row, BorderLayout.NORTH)
|
||||
bindHeader(row, header, arrow)
|
||||
watch(row)
|
||||
if (expanded && expandable) add(body(), BorderLayout.CENTER)
|
||||
if (!expandable) syncExpandable(false) else syncArrow()
|
||||
}
|
||||
@@ -148,19 +160,35 @@ abstract class AbstractSessionPartView(
|
||||
}
|
||||
|
||||
private fun bind(component: Component) {
|
||||
if (bound.contains(component)) return
|
||||
bound.add(component)
|
||||
if (!bound.add(component)) return
|
||||
component.addMouseListener(click)
|
||||
component.addMouseListener(mouse)
|
||||
}
|
||||
|
||||
private fun unbind(component: Component) {
|
||||
if (!bound.remove(component)) return
|
||||
component.removeMouseListener(click)
|
||||
component.removeMouseListener(mouse)
|
||||
component.cursor = Cursor.getDefaultCursor()
|
||||
}
|
||||
|
||||
/** Attaches the hover listener to [component] and its whole subtree, keeping up with later children. */
|
||||
private fun watch(component: Component) {
|
||||
if (!watched.add(component)) return
|
||||
component.addMouseListener(pointer)
|
||||
if (component is Container) {
|
||||
component.addContainerListener(nested)
|
||||
component.components.forEach { watch(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun unwatch(component: Component) {
|
||||
if (!watched.remove(component)) return
|
||||
component.removeMouseListener(pointer)
|
||||
if (component is Container) {
|
||||
component.removeContainerListener(nested)
|
||||
component.components.forEach { unwatch(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun body(): JComponent {
|
||||
val item = body
|
||||
if (item != null) return item
|
||||
|
||||
+2
-2
@@ -92,8 +92,8 @@ class BaseQuestionView(
|
||||
isVisible = false
|
||||
}
|
||||
|
||||
private val headerText: JBTextArea = makeText("", UiStyle.Colors.fg(), bold = true)
|
||||
private val descriptionText: JBTextArea = makeText("", UiStyle.Colors.weak(), bold = false).apply {
|
||||
private val headerText: JBTextArea = makeText("", SessionUiStyle.Colors.foreground(), bold = true)
|
||||
private val descriptionText: JBTextArea = makeText("", SessionUiStyle.Colors.hint(), bold = false).apply {
|
||||
isVisible = false
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ package ai.kilocode.client.session.views.base
|
||||
import ai.kilocode.client.session.model.Content
|
||||
import ai.kilocode.client.session.model.Generic
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import com.intellij.ui.components.JBLabel
|
||||
|
||||
@@ -23,7 +24,7 @@ class GenericView private constructor(
|
||||
override val contentId: String = content.id
|
||||
|
||||
init {
|
||||
label.foreground = UiStyle.Colors.weak()
|
||||
label.foreground = SessionUiStyle.Colors.hint()
|
||||
applyStyle(SessionEditorStyle.current())
|
||||
syncExpandable(false)
|
||||
border = null
|
||||
|
||||
+9
-8
@@ -12,6 +12,7 @@ import ai.kilocode.client.session.views.base.BaseQuestionView
|
||||
import ai.kilocode.client.session.ui.selection.SessionSelection
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.session.views.SessionViewIcons
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import ai.kilocode.client.ui.iconButton
|
||||
@@ -172,7 +173,7 @@ class PermissionView(
|
||||
this.style = style
|
||||
card.applyStyle(style)
|
||||
desc.font = style.hintFont
|
||||
desc.foreground = UiStyle.Colors.weak()
|
||||
desc.foreground = SessionUiStyle.Colors.hint()
|
||||
rules.applyStyle(style)
|
||||
md?.let { applyCodeStyle(it) }
|
||||
for (dv in diffViews) {
|
||||
@@ -276,7 +277,7 @@ class PermissionView(
|
||||
view.applyStyle(style)
|
||||
view.font = style.transcriptFont
|
||||
view.foreground = style.editorForeground
|
||||
view.background = style.editorBackground
|
||||
view.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
view.preBg = MdCommon.defaults(style).preBg
|
||||
view.codeFont = style.editorFamily
|
||||
view.component.border = JBUI.Borders.empty()
|
||||
@@ -353,7 +354,7 @@ class PermissionView(
|
||||
caret.isSelectionVisible = false
|
||||
lineWrap = true
|
||||
wrapStyleWord = true
|
||||
foreground = UiStyle.Colors.weak()
|
||||
foreground = SessionUiStyle.Colors.hint()
|
||||
font = style.hintFont
|
||||
border = JBUI.Borders.empty()
|
||||
isVisible = false
|
||||
@@ -706,7 +707,7 @@ internal class PermissionRulesView(
|
||||
@RequiresEdt
|
||||
fun applyStyle(style: SessionEditorStyle) {
|
||||
hint.font = style.hintFont
|
||||
hint.foreground = UiStyle.Colors.weak()
|
||||
hint.foreground = SessionUiStyle.Colors.hint()
|
||||
field.applyStyle(style)
|
||||
}
|
||||
|
||||
@@ -771,11 +772,11 @@ internal class PermissionRulesView(
|
||||
ed.setBorder(JBUI.Borders.empty())
|
||||
ed.scrollPane.border = JBUI.Borders.empty()
|
||||
ed.scrollPane.viewportBorder = JBUI.Borders.empty()
|
||||
ed.backgroundColor = style.editorBackground
|
||||
ed.scrollPane.background = style.editorBackground
|
||||
ed.backgroundColor = SessionUiStyle.Colors.codeBlockBackground()
|
||||
ed.scrollPane.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
ed.scrollPane.isOpaque = true
|
||||
ed.scrollPane.viewport.isOpaque = true
|
||||
ed.scrollPane.viewport.background = style.editorBackground
|
||||
ed.scrollPane.viewport.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
ed.settings.isUseSoftWraps = false
|
||||
ed.settings.isAdditionalPageAtBottom = false
|
||||
ed.scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
|
||||
@@ -839,7 +840,7 @@ internal class PermissionRulesView(
|
||||
val g2 = g.create() as Graphics2D
|
||||
try {
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
|
||||
val base = UiStyle.Colors.bg()
|
||||
val base = SessionUiStyle.Colors.sessionBackground()
|
||||
g2.color = when {
|
||||
active -> UiStyle.Colors.blend(base, if (approve) UiStyle.Colors.addedForeground() else UiStyle.Colors.removedForeground(), 0.15f)
|
||||
else -> UiStyle.Colors.actionHoverBackground()
|
||||
|
||||
+4
-4
@@ -58,7 +58,7 @@ class QuestionResultView(tool: Tool, private val selection: SessionSelection? =
|
||||
}
|
||||
private val glyph = JBLabel(SessionViewIcons.bubble)
|
||||
private val title = JBLabel()
|
||||
private val sub = JBLabel().apply { foreground = UiStyle.Colors.weak() }
|
||||
private val sub = JBLabel().apply { foreground = SessionUiStyle.Colors.hint() }
|
||||
private val arrow = JBLabel()
|
||||
private val center = JPanel(BorderLayout(JBUI.scale(SessionUiStyle.View.Layout.GAP), 0)).apply {
|
||||
isOpaque = false
|
||||
@@ -197,7 +197,7 @@ class QuestionResultView(tool: Tool, private val selection: SessionSelection? =
|
||||
title.text = KiloBundle.message("session.question.result.title")
|
||||
val count = result.answers.count { it.isNotEmpty() }
|
||||
sub.text = KiloBundle.message("session.question.result.answered", count)
|
||||
sub.foreground = UiStyle.Colors.weak()
|
||||
sub.foreground = SessionUiStyle.Colors.hint()
|
||||
}
|
||||
|
||||
private fun syncBody() {
|
||||
@@ -214,7 +214,7 @@ class QuestionResultView(tool: Tool, private val selection: SessionSelection? =
|
||||
}
|
||||
if (i > 0) row.border = JBUI.Borders.emptyTop(UiStyle.Gap.lg())
|
||||
|
||||
val qText = makeText(q, UiStyle.Colors.weak(), false)
|
||||
val qText = makeText(q, SessionUiStyle.Colors.hint(), false)
|
||||
qText.alignmentX = Component.LEFT_ALIGNMENT
|
||||
qText.border = JBUI.Borders.emptyBottom(UiStyle.Gap.xs())
|
||||
row.add(qText)
|
||||
@@ -222,7 +222,7 @@ class QuestionResultView(tool: Tool, private val selection: SessionSelection? =
|
||||
val joined = result.answers.getOrNull(i)?.joinToString(", ").orEmpty()
|
||||
val aText = makeText(
|
||||
joined.ifBlank { KiloBundle.message("session.question.review.notAnswered") },
|
||||
UiStyle.Colors.fg(),
|
||||
SessionUiStyle.Colors.foreground(),
|
||||
true,
|
||||
)
|
||||
aText.alignmentX = Component.LEFT_ALIGNMENT
|
||||
|
||||
+8
-8
@@ -185,7 +185,7 @@ class QuestionView(
|
||||
card.applyStyle(style)
|
||||
customEditor?.let { ed ->
|
||||
style.applyTranscriptToField(ed)
|
||||
ed.background = style.editorBackground
|
||||
ed.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
syncEditorHeight(ed)
|
||||
}
|
||||
val changed = texts.fold(false) { acc, item -> setFont(item.first, item.second) || acc }
|
||||
@@ -223,7 +223,7 @@ class QuestionView(
|
||||
val total = q.items.size
|
||||
val shown = minOf(idx + 1, total)
|
||||
summary.text = KiloBundle.message("session.question.summary", shown, total)
|
||||
summary.foreground = UiStyle.Colors.weak()
|
||||
summary.foreground = SessionUiStyle.Colors.hint()
|
||||
summary.isVisible = total > 1
|
||||
nav.isVisible = total > 1
|
||||
topPanel.isVisible = total > 1
|
||||
@@ -331,7 +331,7 @@ class QuestionView(
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
border = JBUI.Borders.emptyBottom(UiStyle.Gap.lg())
|
||||
}
|
||||
val qText = text(item.question, UiStyle.Colors.weak())
|
||||
val qText = text(item.question, SessionUiStyle.Colors.hint())
|
||||
qText.alignmentX = Component.LEFT_ALIGNMENT
|
||||
row.add(qText)
|
||||
|
||||
@@ -339,7 +339,7 @@ class QuestionView(
|
||||
val joined = answers.joinToString(", ")
|
||||
val answer = text(
|
||||
joined.ifBlank { KiloBundle.message("session.question.review.notAnswered") },
|
||||
UiStyle.Colors.fg(),
|
||||
SessionUiStyle.Colors.foreground(),
|
||||
true,
|
||||
)
|
||||
answer.alignmentX = Component.LEFT_ALIGNMENT
|
||||
@@ -442,7 +442,7 @@ class QuestionView(
|
||||
addMouseListener(press)
|
||||
}
|
||||
|
||||
val label = text(KiloBundle.message("session.question.custom.label"), UiStyle.Colors.fg(), true)
|
||||
val label = text(KiloBundle.message("session.question.custom.label"), SessionUiStyle.Colors.foreground(), true)
|
||||
label.alignmentX = Component.LEFT_ALIGNMENT
|
||||
label.addMouseListener(press)
|
||||
col.add(label)
|
||||
@@ -526,7 +526,7 @@ class QuestionView(
|
||||
}
|
||||
selection?.register(ed)?.let(regs::add)
|
||||
style.applyTranscriptToField(ed)
|
||||
ed.background = style.editorBackground
|
||||
ed.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
|
||||
// Pre-fill with saved text. This call also forces lazy document creation so
|
||||
// that addDocumentListener can install on a non-null document immediately.
|
||||
@@ -700,13 +700,13 @@ class QuestionView(
|
||||
layout = if (center) GridBagLayout() else BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
addMouseListener(press)
|
||||
}
|
||||
val label = text(opt.label, UiStyle.Colors.fg(), true)
|
||||
val label = text(opt.label, SessionUiStyle.Colors.foreground(), true)
|
||||
label.alignmentX = Component.LEFT_ALIGNMENT
|
||||
label.addMouseListener(press)
|
||||
col.add(label)
|
||||
|
||||
if (opt.description.isNotBlank()) {
|
||||
val desc = text(opt.description, UiStyle.Colors.weak())
|
||||
val desc = text(opt.description, SessionUiStyle.Colors.hint())
|
||||
desc.alignmentX = Component.LEFT_ALIGNMENT
|
||||
desc.addMouseListener(press)
|
||||
col.add(desc)
|
||||
|
||||
+3
-3
@@ -52,8 +52,8 @@ class TodoListPanel(
|
||||
this.style = style
|
||||
prior.font = style.smallFont
|
||||
later.font = style.smallFont
|
||||
prior.foreground = UiStyle.Colors.weak()
|
||||
later.foreground = UiStyle.Colors.weak()
|
||||
prior.foreground = SessionUiStyle.Colors.hint()
|
||||
later.foreground = SessionUiStyle.Colors.hint()
|
||||
rows.forEachIndexed { index, row -> row.update(items[index], style) }
|
||||
syncHidden()
|
||||
}
|
||||
@@ -136,7 +136,7 @@ class TodoListPanel(
|
||||
text.font = style.regularFont
|
||||
text.foreground = when {
|
||||
!done -> style.editorForeground
|
||||
else -> UiStyle.Colors.weak()
|
||||
else -> SessionUiStyle.Colors.hint()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ class TodoParts(
|
||||
private fun todoParts(): TodoParts {
|
||||
val glyph = JBLabel(SessionViewIcons.checklist)
|
||||
val title = JBLabel(KiloBundle.message("session.part.todo.title"))
|
||||
val sub = JBLabel().apply { foreground = UiStyle.Colors.weak() }
|
||||
val sub = JBLabel().apply { foreground = SessionUiStyle.Colors.hint() }
|
||||
val header = PartHeader().apply {
|
||||
leading(glyph)
|
||||
left(title)
|
||||
|
||||
+3
-3
@@ -129,7 +129,7 @@ abstract class BaseSearchToolView(
|
||||
changed = setForeground(parts.glyph, color(item)) || changed
|
||||
changed = setText(parts.title, toolTitle(item)) || changed
|
||||
changed = setForeground(parts.title, titleColor(item)) || changed
|
||||
changed = setForeground(parts.sub, UiStyle.Colors.weak()) || changed
|
||||
changed = setForeground(parts.sub, SessionUiStyle.Colors.hint()) || changed
|
||||
changed = syncTargets() || changed
|
||||
changed = setText(parts.state, stateText(item)) || changed
|
||||
changed = setForeground(parts.state, color(item)) || changed
|
||||
@@ -148,7 +148,7 @@ abstract class BaseSearchToolView(
|
||||
val text = values.getOrNull(index) ?: ""
|
||||
changed = setVisible(label, text.isNotBlank()) || changed
|
||||
changed = setTargetText(label, text) || changed
|
||||
changed = setForeground(label, UiStyle.Colors.fg()) || changed
|
||||
changed = setForeground(label, SessionUiStyle.Colors.foreground()) || changed
|
||||
}
|
||||
return changed
|
||||
}
|
||||
@@ -176,7 +176,7 @@ abstract class BaseSearchToolView(
|
||||
return body.applyStyle(style)
|
||||
}
|
||||
|
||||
private fun bodyColor() = if (item.state == ToolExecState.ERROR) UiStyle.Colors.errorLabelForeground() else UiStyle.Colors.fg()
|
||||
private fun bodyColor() = if (item.state == ToolExecState.ERROR) UiStyle.Colors.errorLabelForeground() else SessionUiStyle.Colors.foreground()
|
||||
|
||||
private fun bodyMaxHeight(): Int {
|
||||
val body = parts.content ?: return 0
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ package ai.kilocode.client.session.views.tool
|
||||
import ai.kilocode.client.plugin.KiloBundle
|
||||
import ai.kilocode.client.session.model.Tool
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import ai.kilocode.client.ui.layout.Stack
|
||||
import com.intellij.openapi.Disposable
|
||||
@@ -23,7 +24,7 @@ import javax.swing.JComponent
|
||||
@RequiresEdt
|
||||
internal fun diffOverflowPanel(open: () -> Unit): JComponent {
|
||||
val message = JBLabel(KiloBundle.message("diff.overflow.message")).apply {
|
||||
foreground = UiStyle.Colors.weak()
|
||||
foreground = SessionUiStyle.Colors.hint()
|
||||
}
|
||||
val link = HyperlinkLabel(KiloBundle.message("diff.overflow.open")).apply {
|
||||
addHyperlinkListener { open() }
|
||||
|
||||
+3
-3
@@ -63,7 +63,7 @@ class EditToolView(
|
||||
)
|
||||
private val diffAnchor = hoverPlaceholder(diff)
|
||||
private val filesTag = JBLabel().apply {
|
||||
foreground = UiStyle.Colors.weak()
|
||||
foreground = SessionUiStyle.Colors.hint()
|
||||
font = JBFont.small()
|
||||
isVisible = false
|
||||
}
|
||||
@@ -233,7 +233,7 @@ class EditToolView(
|
||||
val path = if (count > 1) null else editPath(item)
|
||||
changed = setFileTarget(parts, path, if (path == null) "" else tail(path)) || changed
|
||||
changed = setForeground(parts.title, titleColor(item)) || changed
|
||||
changed = setForeground(parts.link, UiStyle.Colors.fg()) || changed
|
||||
changed = setForeground(parts.link, SessionUiStyle.Colors.foreground()) || changed
|
||||
changed = setText(parts.state, stateText(item)) || changed
|
||||
changed = setForeground(parts.state, color(item)) || changed
|
||||
syncDiffAction(count)
|
||||
@@ -287,7 +287,7 @@ class EditToolView(
|
||||
// calls rebuild and sets its signature), so a follow-up update() here would be a no-op.
|
||||
val panel = popup.mount(item)
|
||||
popup.applyStyle(style)
|
||||
return HeaderPopupBody(panel, owner, style.editorBackground, SessionUiStyle.View.Popup.WIDE_MAX_WIDTH)
|
||||
return HeaderPopupBody(panel, owner, SessionUiStyle.Colors.codeBlockBackground(), SessionUiStyle.View.Popup.WIDE_MAX_WIDTH)
|
||||
}
|
||||
|
||||
override fun dumpLabel() = "EditToolView#$contentId(${labelText()})"
|
||||
|
||||
+3
-3
@@ -184,7 +184,7 @@ class PatchBody(
|
||||
@RequiresEdt
|
||||
private fun header(file: EditFileChange): JComponent {
|
||||
val link = FileLinkLabel(openFile).apply {
|
||||
foreground = UiStyle.Colors.fg()
|
||||
foreground = SessionUiStyle.Colors.foreground()
|
||||
font = style.transcriptFont
|
||||
setTarget(file.path, tail(file.path))
|
||||
isVisible = true
|
||||
@@ -207,8 +207,8 @@ class PatchBody(
|
||||
md.applyStyle(style)
|
||||
md.font = style.editorFont
|
||||
md.foreground = style.editorForeground
|
||||
md.background = style.editorBackground
|
||||
md.preBg = style.editorBackground
|
||||
md.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
md.preBg = SessionUiStyle.Colors.codeBlockBackground()
|
||||
md.codeFont = style.editorFamily
|
||||
md.component.border = JBUI.Borders.empty()
|
||||
rows.getOrNull(views.indexOf(md))?.let { installGutter(md, it) }
|
||||
|
||||
+3
-3
@@ -121,8 +121,8 @@ class ReadToolView(
|
||||
changed = setText(parts.title, title(item)) || changed
|
||||
changed = syncSubtitle() || changed
|
||||
changed = setForeground(parts.title, titleColor(item)) || changed
|
||||
changed = setForeground(parts.sub, UiStyle.Colors.fg()) || changed
|
||||
changed = setForeground(parts.link, UiStyle.Colors.fg()) || changed
|
||||
changed = setForeground(parts.sub, SessionUiStyle.Colors.foreground()) || changed
|
||||
changed = setForeground(parts.link, SessionUiStyle.Colors.foreground()) || changed
|
||||
changed = setText(parts.state, stateText(item)) || changed
|
||||
changed = setForeground(parts.state, color(item)) || changed
|
||||
parts.text?.let { changed = setForeground(it, bodyColor()) || changed }
|
||||
@@ -146,7 +146,7 @@ class ReadToolView(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun bodyColor() = if (item.state == ToolExecState.ERROR) UiStyle.Colors.errorLabelForeground() else UiStyle.Colors.fg()
|
||||
private fun bodyColor() = if (item.state == ToolExecState.ERROR) UiStyle.Colors.errorLabelForeground() else SessionUiStyle.Colors.foreground()
|
||||
|
||||
private fun bodyMaxHeight(): Int {
|
||||
val text = parts.text ?: return 0
|
||||
|
||||
+4
-4
@@ -174,7 +174,7 @@ class ShellToolView(
|
||||
changed = setText(parts.title, title(item)) || changed
|
||||
changed = setText(parts.sub, subtitle(item)) || changed
|
||||
changed = setForeground(parts.title, titleColor(item)) || changed
|
||||
changed = setForeground(parts.sub, UiStyle.Colors.weak()) || changed
|
||||
changed = setForeground(parts.sub, SessionUiStyle.Colors.hint()) || changed
|
||||
changed = setText(parts.state, stateText(item)) || changed
|
||||
changed = setForeground(parts.state, color(item)) || changed
|
||||
return changed
|
||||
@@ -198,13 +198,13 @@ class ShellToolView(
|
||||
md.applyStyle(style)
|
||||
md.font = style.transcriptFont
|
||||
md.foreground = style.editorForeground
|
||||
md.background = style.editorBackground
|
||||
md.preBg = style.editorBackground
|
||||
md.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
md.preBg = SessionUiStyle.Colors.codeBlockBackground()
|
||||
md.codeFont = style.editorFamily
|
||||
md.component.border = JBUI.Borders.empty()
|
||||
md.set(popupMd(formatCommand(cmd)))
|
||||
padPopup(md.component)
|
||||
return HeaderPopupBody(md.component, md, style.editorBackground, SessionUiStyle.View.Popup.WIDE_MAX_WIDTH)
|
||||
return HeaderPopupBody(md.component, md, SessionUiStyle.Colors.codeBlockBackground(), SessionUiStyle.View.Popup.WIDE_MAX_WIDTH)
|
||||
}
|
||||
|
||||
override fun dumpLabel() = "ShellToolView#$contentId(${labelText()})"
|
||||
|
||||
+5
-5
@@ -221,7 +221,7 @@ class TaskToolView(
|
||||
private var item = tool
|
||||
val icon = JBLabel()
|
||||
val title = JBLabel()
|
||||
val sub = JBLabel().apply { foreground = UiStyle.Colors.weak() }
|
||||
val sub = JBLabel().apply { foreground = SessionUiStyle.Colors.hint() }
|
||||
val panel = JPanel(BorderLayout(UiStyle.Gap.md(), 0)).apply {
|
||||
isOpaque = false
|
||||
add(icon, BorderLayout.WEST)
|
||||
@@ -265,7 +265,7 @@ private class TaskBody(glyph: JBLabel) {
|
||||
val panel = object : JPanel(BorderLayout()) {
|
||||
override fun updateUI() {
|
||||
super.updateUI()
|
||||
background = SessionUiStyle.View.Surface.bgColor()
|
||||
background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
border = taskBodyBorder(glyph)
|
||||
}
|
||||
}.apply {
|
||||
@@ -286,8 +286,8 @@ private class TaskBodyScroll(val body: TaskBody) : JBScrollPane(body.panel) {
|
||||
override fun updateUI() {
|
||||
super.updateUI()
|
||||
border = JBUI.Borders.empty()
|
||||
background = SessionUiStyle.View.Surface.bgColor()
|
||||
viewport?.background = SessionUiStyle.View.Surface.bgColor()
|
||||
background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
viewport?.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ private class TaskRows : Stack(StackAxis.VERTICAL, UiStyle.Gap.sm()), Scrollable
|
||||
private fun rowTitleColor(tool: Tool) = if (tool.state == ToolExecState.ERROR) {
|
||||
UiStyle.Colors.errorLabelForeground()
|
||||
} else {
|
||||
UiStyle.Colors.weak()
|
||||
SessionUiStyle.Colors.hint()
|
||||
}
|
||||
|
||||
private fun taskBodyBorder(glyph: JBLabel) = run {
|
||||
|
||||
+3
-2
@@ -5,6 +5,7 @@ import ai.kilocode.client.diff.installDiffGutter
|
||||
import ai.kilocode.client.session.model.Tool
|
||||
import ai.kilocode.client.session.ui.selection.SessionSelection
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.md.MdCodeBlockFactory
|
||||
import ai.kilocode.client.ui.md.MdCodeBlockOptions
|
||||
import ai.kilocode.client.ui.md.MdView
|
||||
@@ -88,8 +89,8 @@ class ToolMarkdownBody(
|
||||
md.applyStyle(style)
|
||||
md.font = font(style)
|
||||
md.foreground = style.editorForeground
|
||||
md.background = style.editorBackground
|
||||
md.preBg = style.editorBackground
|
||||
md.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
md.preBg = SessionUiStyle.Colors.codeBlockBackground()
|
||||
md.codeFont = style.editorFamily
|
||||
md.component.border = JBUI.Borders.empty()
|
||||
chrome(md)
|
||||
|
||||
+15
-15
@@ -125,7 +125,7 @@ class FileLinkLabel(
|
||||
init {
|
||||
isVisible = false
|
||||
isFocusable = false
|
||||
foreground = UiStyle.Colors.fg()
|
||||
foreground = SessionUiStyle.Colors.foreground()
|
||||
cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
|
||||
setRequestFocusEnabled(false)
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
@@ -191,7 +191,7 @@ class ToolBody private constructor(
|
||||
|
||||
var foreground: Color
|
||||
@RequiresEdt
|
||||
get() = area?.foreground ?: ed?.foreground ?: UiStyle.Colors.fg()
|
||||
get() = area?.foreground ?: ed?.foreground ?: SessionUiStyle.Colors.foreground()
|
||||
@RequiresEdt
|
||||
set(value) {
|
||||
area?.foreground = value
|
||||
@@ -306,8 +306,8 @@ class ToolBody private constructor(
|
||||
caret.isSelectionVisible = true
|
||||
lineWrap = wrap
|
||||
wrapStyleWord = wrap
|
||||
foreground = if (tool.state == ToolExecState.ERROR) UiStyle.Colors.errorLabelForeground() else UiStyle.Colors.fg()
|
||||
background = SessionUiStyle.View.Surface.bgColor()
|
||||
foreground = if (tool.state == ToolExecState.ERROR) UiStyle.Colors.errorLabelForeground() else SessionUiStyle.Colors.foreground()
|
||||
background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
border = JBUI.Borders.empty(
|
||||
JBUI.scale(SessionUiStyle.View.Layout.VERTICAL_PADDING),
|
||||
JBUI.scale(SessionUiStyle.View.Layout.HORIZONTAL_PADDING),
|
||||
@@ -327,8 +327,8 @@ class ToolBody private constructor(
|
||||
JBUI.scale(SessionUiStyle.View.Layout.HORIZONTAL_PADDING),
|
||||
).takeIf { scrolls }
|
||||
isOpaque = true
|
||||
background = SessionUiStyle.View.Surface.bgColor()
|
||||
viewport.background = SessionUiStyle.View.Surface.bgColor()
|
||||
background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
viewport.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
horizontalScrollBarPolicy = if (scrolls) {
|
||||
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
|
||||
} else {
|
||||
@@ -370,9 +370,9 @@ private class ToolField(value: String, private var style: SessionEditorStyle, pr
|
||||
ed.setBorder(JBUI.Borders.empty())
|
||||
ed.scrollPane.border = JBUI.Borders.empty()
|
||||
ed.scrollPane.viewportBorder = JBUI.Borders.empty()
|
||||
ed.backgroundColor = SessionUiStyle.View.Surface.bgColor()
|
||||
ed.scrollPane.background = SessionUiStyle.View.Surface.bgColor()
|
||||
ed.scrollPane.viewport.background = SessionUiStyle.View.Surface.bgColor()
|
||||
ed.backgroundColor = SessionUiStyle.Colors.codeBlockBackground()
|
||||
ed.scrollPane.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
ed.scrollPane.viewport.background = SessionUiStyle.Colors.codeBlockBackground()
|
||||
ed.settings.isUseSoftWraps = false
|
||||
ed.settings.isAdditionalPageAtBottom = false
|
||||
ed.scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
|
||||
@@ -404,14 +404,14 @@ internal fun toolParts(
|
||||
): ToolParts {
|
||||
val glyph = JBLabel()
|
||||
val title = clip(JBLabel())
|
||||
val sub = clip(JBLabel()).apply { foreground = UiStyle.Colors.weak() }
|
||||
val sub = clip(JBLabel()).apply { foreground = SessionUiStyle.Colors.hint() }
|
||||
val link = clip(FileLinkLabel(openFile))
|
||||
val slot = Stack.fitHorizontal(SessionUiStyle.View.Header.gap()).apply {
|
||||
minimumSize = Dimension(0, minimumSize.height)
|
||||
next(sub)
|
||||
next(link)
|
||||
}
|
||||
val state = clip(JBLabel()).apply { foreground = UiStyle.Colors.weak() }
|
||||
val state = clip(JBLabel()).apply { foreground = SessionUiStyle.Colors.hint() }
|
||||
val header = PartHeader().apply {
|
||||
leading(glyph)
|
||||
left(title)
|
||||
@@ -426,10 +426,10 @@ internal fun toolParts(
|
||||
internal fun searchParts(count: Int): ToolParts {
|
||||
val glyph = JBLabel()
|
||||
val title = clip(JBLabel())
|
||||
val sub = clip(JBLabel()).apply { foreground = UiStyle.Colors.weak() }
|
||||
val sub = clip(JBLabel()).apply { foreground = SessionUiStyle.Colors.hint() }
|
||||
val targets = List(count) {
|
||||
clip(JBLabel()).apply {
|
||||
foreground = UiStyle.Colors.fg()
|
||||
foreground = SessionUiStyle.Colors.foreground()
|
||||
}
|
||||
}
|
||||
val link = clip(FileLinkLabel())
|
||||
@@ -438,7 +438,7 @@ internal fun searchParts(count: Int): ToolParts {
|
||||
next(sub)
|
||||
next(link)
|
||||
}
|
||||
val state = clip(JBLabel()).apply { foreground = UiStyle.Colors.weak() }
|
||||
val state = clip(JBLabel()).apply { foreground = SessionUiStyle.Colors.hint() }
|
||||
val target = Stack.fitHorizontal(SessionUiStyle.View.Header.gap()).apply {
|
||||
minimumSize = Dimension(0, minimumSize.height)
|
||||
targets.forEach { next(it) }
|
||||
@@ -568,7 +568,7 @@ internal fun color(tool: Tool) = when (tool.state) {
|
||||
internal fun titleColor(tool: Tool) = if (tool.state == ToolExecState.ERROR) {
|
||||
UiStyle.Colors.errorLabelForeground()
|
||||
} else {
|
||||
UiStyle.Colors.fg()
|
||||
SessionUiStyle.Colors.foreground()
|
||||
}
|
||||
|
||||
internal fun stateText(tool: Tool) = when (tool.state) {
|
||||
|
||||
+1
-1
@@ -181,7 +181,7 @@ class ToolView(
|
||||
return body.applyStyle(style)
|
||||
}
|
||||
|
||||
private fun bodyColor() = if (item.state == ToolExecState.ERROR) UiStyle.Colors.errorLabelForeground() else UiStyle.Colors.fg()
|
||||
private fun bodyColor() = if (item.state == ToolExecState.ERROR) UiStyle.Colors.errorLabelForeground() else SessionUiStyle.Colors.foreground()
|
||||
|
||||
private fun bodyMaxHeight(): Int {
|
||||
val body = parts.content ?: return 0
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ class SessionUiLayoutTest : SessionUiTestBase() {
|
||||
}
|
||||
|
||||
fun `test session surfaces use session background from initial render`() {
|
||||
val bg = SessionEditorStyle.current().editorBackground
|
||||
val bg = SessionUiStyle.Colors.sessionBackground()
|
||||
val root = find<SessionRootPanel>(ui)
|
||||
val pane = scrollComponent() as JBScrollPane
|
||||
|
||||
|
||||
+10
-3
@@ -819,9 +819,16 @@ class SessionMessageListPanelTest : BasePlatformTestCase() {
|
||||
message.paint(graphics)
|
||||
graphics.dispose()
|
||||
|
||||
val line = SessionUiStyle.View.Outline.color().rgb
|
||||
assertEquals(line, Color(image.getRGB(point.x + box.width / 2, point.y), true).rgb)
|
||||
assertEquals(line, Color(image.getRGB(point.x + box.width / 2, point.y + box.height - 1), true).rgb)
|
||||
// The bubble is a filled code-block surface; it only draws an outline when that fill matches
|
||||
// the session background. Probing the box edges verifies it paints at the wrapped coordinates.
|
||||
val bg = SessionUiStyle.View.Prompt.bgColor(SessionEditorStyle.current())
|
||||
val expected = if (bg.rgb == SessionUiStyle.Colors.sessionBackground().rgb) {
|
||||
SessionUiStyle.View.Outline.color().rgb
|
||||
} else {
|
||||
bg.rgb
|
||||
}
|
||||
assertEquals(expected, Color(image.getRGB(point.x + box.width / 2, point.y), true).rgb)
|
||||
assertEquals(expected, Color(image.getRGB(point.x + box.width / 2, point.y + box.height - 1), true).rgb)
|
||||
}
|
||||
|
||||
fun `test created ContentDelta is not double applied`() {
|
||||
|
||||
+4
-2
@@ -1,5 +1,6 @@
|
||||
package ai.kilocode.client.session.ui
|
||||
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.ui.UiStyle
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.testFramework.fixtures.BasePlatformTestCase
|
||||
@@ -31,11 +32,12 @@ class SessionRootPanelTest : BasePlatformTestCase() {
|
||||
assertFalse(root.blocker.isVisible)
|
||||
}
|
||||
|
||||
fun `test blocker is opaque and uses panel background`() {
|
||||
fun `test blocker is opaque and uses session background`() {
|
||||
val root = SessionRootPanel()
|
||||
|
||||
assertTrue(root.blocker.isOpaque)
|
||||
assertEquals(UiStyle.Colors.bg(), root.blocker.background)
|
||||
assertEquals(UiStyle.Colors.bg(), SessionUiStyle.Colors.sessionBackground())
|
||||
assertEquals(SessionUiStyle.Colors.sessionBackground(), root.blocker.background)
|
||||
}
|
||||
|
||||
fun `test root layout fills all immediate children`() {
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ class SessionHeaderPanelTest : SessionControllerTestBase() {
|
||||
assertEquals("cache read 75", panel.cacheReadText())
|
||||
assertEquals("1/2 todos complete", panel.todoText())
|
||||
assertTrue(panel.todoVisible())
|
||||
assertEquals(style.editorBackground, panel.background)
|
||||
assertEquals(SessionUiStyle.Colors.sessionBackground(), panel.background)
|
||||
assertEquals(
|
||||
List(panel.foregrounds().size) { style.editorForeground },
|
||||
panel.foregrounds(),
|
||||
@@ -288,7 +288,7 @@ class SessionHeaderPanelTest : SessionControllerTestBase() {
|
||||
|
||||
panel.applyStyle(style)
|
||||
|
||||
assertEquals(style.editorBackground, panel.background)
|
||||
assertEquals(SessionUiStyle.Colors.sessionBackground(), panel.background)
|
||||
assertEquals(
|
||||
List(panel.foregrounds().size) { style.editorForeground },
|
||||
panel.foregrounds(),
|
||||
|
||||
+1
-1
@@ -239,7 +239,7 @@ class ShellToolViewTest : BasePlatformTestCase() {
|
||||
assertEquals(style.transcriptFont.name, view.subtitleFont().name)
|
||||
assertEquals(style.transcriptFont.size, view.subtitleFont().size)
|
||||
assertFalse(view.subtitleFont().isBold)
|
||||
assertEquals(UiStyle.Colors.weak().rgb, view.subtitleForeground().rgb)
|
||||
assertEquals(SessionUiStyle.Colors.hint().rgb, view.subtitleForeground().rgb)
|
||||
assertTrue(view.stateFont().size < style.editorSize)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ class TaskToolViewTest : BasePlatformTestCase() {
|
||||
fun `test child tool titles use target color`() {
|
||||
val view = view(task(children = listOf(child("c1", "read"), child("c2", "grep", ToolExecState.ERROR))))
|
||||
|
||||
assertColor(UiStyle.Colors.weak(), titleColor(view, 0))
|
||||
assertColor(SessionUiStyle.Colors.hint(), titleColor(view, 0))
|
||||
assertColor(UiStyle.Colors.errorLabelForeground(), titleColor(view, 1))
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -8,6 +8,7 @@ import ai.kilocode.client.session.model.Tool
|
||||
import ai.kilocode.client.session.model.ToolExecState
|
||||
import ai.kilocode.client.session.model.toolKind
|
||||
import ai.kilocode.client.session.ui.ModifiedFilesView
|
||||
import ai.kilocode.client.session.ui.style.SessionEditorStyle
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.session.views.tool.EditToolView
|
||||
import ai.kilocode.rpc.dto.DiffFileDto
|
||||
@@ -144,14 +145,22 @@ class TurnViewTest : BasePlatformTestCase() {
|
||||
assertFalse(mv.isOpaque)
|
||||
}
|
||||
|
||||
fun `test user message uses standard outline color`() {
|
||||
fun `test user message fills a prompt surface`() {
|
||||
val mv = MessageView(msg("u1", "user"), openFile)
|
||||
mv.setSize(120, 48)
|
||||
val image = BufferedImage(120, 48, BufferedImage.TYPE_INT_ARGB)
|
||||
|
||||
mv.paint(image.createGraphics())
|
||||
|
||||
assertEquals(SessionUiStyle.View.Outline.color().rgb, image.getRGB(60, 0))
|
||||
// The user bubble is a filled code-block surface; it only outlines when the fill matches the
|
||||
// session background (no contrast).
|
||||
val bg = SessionUiStyle.View.Prompt.bgColor(SessionEditorStyle.current())
|
||||
val expected = if (bg.rgb == SessionUiStyle.Colors.sessionBackground().rgb) {
|
||||
SessionUiStyle.View.Outline.color().rgb
|
||||
} else {
|
||||
bg.rgb
|
||||
}
|
||||
assertEquals(expected, image.getRGB(60, 0))
|
||||
}
|
||||
|
||||
fun `test assistant message remains borderless`() {
|
||||
|
||||
+33
-7
@@ -4,11 +4,13 @@ import ai.kilocode.client.session.model.Content
|
||||
import ai.kilocode.client.session.ui.style.SessionUiStyle
|
||||
import ai.kilocode.client.session.views.SessionViewIcons
|
||||
import com.intellij.testFramework.fixtures.BasePlatformTestCase
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Color
|
||||
import java.awt.Component
|
||||
import java.awt.event.MouseEvent
|
||||
import java.awt.image.BufferedImage
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.border.Border
|
||||
@@ -88,6 +90,8 @@ class AbstractSessionPartViewTest : BasePlatformTestCase() {
|
||||
}
|
||||
|
||||
fun `test header hover fill differs from outline colors`() {
|
||||
assertEquals(SessionUiStyle.Colors.sessionBackground(), SessionUiStyle.View.Surface.headerBgColor())
|
||||
assertEquals(SessionUiStyle.Colors.sessionBackground(), SessionUiStyle.View.Surface.bgColor())
|
||||
assertNotSameColor(SessionUiStyle.View.Surface.headerHoverBgColor(), SessionUiStyle.View.Outline.hoverColor())
|
||||
assertNotSameColor(SessionUiStyle.View.Surface.headerHoverBgColor(), SessionUiStyle.View.Outline.brightColor())
|
||||
}
|
||||
@@ -108,7 +112,24 @@ class AbstractSessionPartViewTest : BasePlatformTestCase() {
|
||||
assertLine(view.border)
|
||||
}
|
||||
|
||||
private class TestView(content: JLabel, expanded: Boolean = false, expandable: Boolean = true) :
|
||||
fun `test hover tracks nested header child and clears on leave`() {
|
||||
val child = JLabel("link")
|
||||
val header = JPanel(BorderLayout()).apply { add(child, BorderLayout.WEST) }
|
||||
val view = NestedView(header)
|
||||
val row = view.component(0) as JPanel
|
||||
view.setSize(200, 40)
|
||||
view.doLayout()
|
||||
|
||||
// A nested child that is not click-bound (e.g. a file link) must still report hover.
|
||||
enter(child)
|
||||
assertEquals(SessionUiStyle.View.Surface.headerHoverBgColor().rgb, row.background.rgb)
|
||||
|
||||
// Leaving the row through that nested child clears the fill instead of leaving it stuck.
|
||||
exit(child, 10_000, 10_000)
|
||||
assertEquals(SessionUiStyle.View.Surface.headerBgColor().rgb, row.background.rgb)
|
||||
}
|
||||
|
||||
private open class TestView(content: JLabel, expanded: Boolean = false, expandable: Boolean = true) :
|
||||
PrimarySessionPartView(JLabel("header"), content, expanded, expandable) {
|
||||
|
||||
override val contentId = "test"
|
||||
@@ -117,20 +138,25 @@ class AbstractSessionPartViewTest : BasePlatformTestCase() {
|
||||
fun arrowIcon(): Icon = arrow.icon
|
||||
}
|
||||
|
||||
private fun TestView.component(index: Int): Component = components[index]
|
||||
private class NestedView(header: JComponent) : PrimarySessionPartView(header, JLabel("body")) {
|
||||
override val contentId = "nested"
|
||||
override fun update(content: Content) {}
|
||||
}
|
||||
|
||||
private fun enter(component: Component) = event(component, MouseEvent.MOUSE_ENTERED)
|
||||
private fun PrimarySessionPartView.component(index: Int): Component = components[index]
|
||||
|
||||
private fun exit(component: Component) = event(component, MouseEvent.MOUSE_EXITED)
|
||||
private fun enter(component: Component) = event(component, MouseEvent.MOUSE_ENTERED, 1, 1)
|
||||
|
||||
private fun event(component: Component, id: Int) {
|
||||
private fun exit(component: Component, x: Int = 1, y: Int = 1) = event(component, MouseEvent.MOUSE_EXITED, x, y)
|
||||
|
||||
private fun event(component: Component, id: Int, x: Int, y: Int) {
|
||||
component.dispatchEvent(MouseEvent(
|
||||
component,
|
||||
id,
|
||||
System.currentTimeMillis(),
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
x,
|
||||
y,
|
||||
0,
|
||||
false,
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user