diff --git a/.changeset/jetbrains-session-colors.md b/.changeset/jetbrains-session-colors.md new file mode 100644 index 00000000000..85848f9a3ad --- /dev/null +++ b/.changeset/jetbrains-session-colors.md @@ -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. diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt index 28c4cd1750d..8d61d958267 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt @@ -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() } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/scroll/SessionScroll.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/scroll/SessionScroll.kt index cf7d6d43609..23df8d171ac 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/scroll/SessionScroll.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/scroll/SessionScroll.kt @@ -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 diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ConnectionPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ConnectionPanel.kt index b97edac917f..a89e16c16ba 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ConnectionPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ConnectionPanel.kt @@ -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) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/LoadingPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/LoadingPanel.kt index e7c20d74956..d106abe082f 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/LoadingPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/LoadingPanel.kt @@ -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() diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ModifiedFilesView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ModifiedFilesView.kt index 4d01bfa9010..fd47dcbfbb1 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ModifiedFilesView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ModifiedFilesView.kt @@ -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()) } } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ProgressPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ProgressPanel.kt index 3f076254065..87452dca888 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ProgressPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/ProgressPanel.kt @@ -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() 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 e1273bdfc11..347a680a6e8 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 @@ -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() diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/RevertProgress.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/RevertProgress.kt index f698aee023f..99a09023a0f 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/RevertProgress.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/RevertProgress.kt @@ -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() diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanel.kt index 8e6edd1ed6d..145710b49de 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/SessionMessageListPanel.kt @@ -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) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/SessionRootPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/SessionRootPanel.kt index ba03b2f1bb4..fe411c6da50 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/SessionRootPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/SessionRootPanel.kt @@ -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() + } + } } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt index b5b3d3ebc3b..239debec5c0 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/attachment/AttachmentCard.kt @@ -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) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/BranchChangesBadge.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/BranchChangesBadge.kt index 12764745e39..af251be7399 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/BranchChangesBadge.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/BranchChangesBadge.kt @@ -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 { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/ContextBar.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/ContextBar.kt index 67939dacead..67f933b2c4a 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/ContextBar.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/ContextBar.kt @@ -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) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/SessionHeaderPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/SessionHeaderPanel.kt index 88dc42bfee4..62d78a31e34 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/SessionHeaderPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/header/SessionHeaderPanel.kt @@ -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) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/mode/ModePickerRenderer.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/mode/ModePickerRenderer.kt index 615b86603b9..f9a6cd91fa3 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/mode/ModePickerRenderer.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/mode/ModePickerRenderer.kt @@ -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) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/model/ModelDetailsPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/model/ModelDetailsPanel.kt index b6541f267a6..9c1dba4d309 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/model/ModelDetailsPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/model/ModelDetailsPanel.kt @@ -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) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt index 9a3af597cf7..5b6b5f069db 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/style/SessionUiStyle.kt @@ -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() } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/CompactionView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/CompactionView.kt index 244d5c94481..ad654fa31ff 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/CompactionView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/CompactionView.kt @@ -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()) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt index 710c0f93351..1eb8575ecbf 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/MessageView.kt @@ -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() diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/ReasoningView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/ReasoningView.kt index c0d87158393..f4c818495e3 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/ReasoningView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/ReasoningView.kt @@ -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) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/TextView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/TextView.kt index 72c17a3466f..352035f33c9 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/TextView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/TextView.kt @@ -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() diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/AbstractSessionPartView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/AbstractSessionPartView.kt index 23ba2b11197..66fc4973076 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/AbstractSessionPartView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/AbstractSessionPartView.kt @@ -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() + private val watched = linkedSetOf() 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 diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/BaseQuestionView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/BaseQuestionView.kt index 113a1c7b924..37f1f333051 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/BaseQuestionView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/BaseQuestionView.kt @@ -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 } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/GenericView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/GenericView.kt index 7f535048a70..4995d5155a0 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/GenericView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/base/GenericView.kt @@ -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 diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/permission/PermissionView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/permission/PermissionView.kt index 91f299579fb..5187a3286cd 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/permission/PermissionView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/permission/PermissionView.kt @@ -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() diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/question/QuestionResultView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/question/QuestionResultView.kt index 6cfa9164eba..04aee6d6a64 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/question/QuestionResultView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/question/QuestionResultView.kt @@ -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 diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/question/QuestionView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/question/QuestionView.kt index 8f103fa4dcf..27120015224 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/question/QuestionView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/question/QuestionView.kt @@ -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) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/todo/TodoListPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/todo/TodoListPanel.kt index 63e2317606b..f1d3c2837b1 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/todo/TodoListPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/todo/TodoListPanel.kt @@ -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() } } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/todo/TodoWriteView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/todo/TodoWriteView.kt index 4db6e24d2ca..fb74d99badf 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/todo/TodoWriteView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/todo/TodoWriteView.kt @@ -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) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/BaseSearchToolView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/BaseSearchToolView.kt index e4dcb2d9151..d63104b6961 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/BaseSearchToolView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/BaseSearchToolView.kt @@ -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 diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/DiffOverflow.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/DiffOverflow.kt index 273b36d59a9..f96581a3c76 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/DiffOverflow.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/DiffOverflow.kt @@ -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() } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/EditToolView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/EditToolView.kt index 831bfb6ea19..4cfdea57072 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/EditToolView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/EditToolView.kt @@ -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()})" diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/PatchBody.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/PatchBody.kt index 98c0fd67bbc..34da3efa6b0 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/PatchBody.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/PatchBody.kt @@ -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) } diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ReadToolView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ReadToolView.kt index 9ac9d158bea..b728a324df5 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ReadToolView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ReadToolView.kt @@ -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 diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ShellToolView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ShellToolView.kt index 2bfca469314..8c4fad41f1d 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ShellToolView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ShellToolView.kt @@ -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()})" diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/TaskToolView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/TaskToolView.kt index 89b76161bb6..294330e4159 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/TaskToolView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/TaskToolView.kt @@ -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 { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolMarkdownBody.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolMarkdownBody.kt index 24ec73db2ad..1b9dc350323 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolMarkdownBody.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolMarkdownBody.kt @@ -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) diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolSupport.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolSupport.kt index 3eabce5da94..e30cb2f4059 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolSupport.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolSupport.kt @@ -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) { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolView.kt index 15ba583933b..e4af898d671 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolView.kt @@ -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 diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionUiLayoutTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionUiLayoutTest.kt index 9a3a19bc4d9..7bf61eb9f75 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionUiLayoutTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionUiLayoutTest.kt @@ -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(ui) val pane = scrollComponent() as JBScrollPane 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 8f369199893..34f87d240c1 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 @@ -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`() { diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionRootPanelTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionRootPanelTest.kt index 74af7d69600..cb8ab6dbc20 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionRootPanelTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/SessionRootPanelTest.kt @@ -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`() { diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/header/SessionHeaderPanelTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/header/SessionHeaderPanelTest.kt index f0f48196007..f68b703234f 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/header/SessionHeaderPanelTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/ui/header/SessionHeaderPanelTest.kt @@ -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(), diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/ShellToolViewTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/ShellToolViewTest.kt index 03cd9d4d2ba..39208862720 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/ShellToolViewTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/ShellToolViewTest.kt @@ -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) } diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TaskToolViewTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TaskToolViewTest.kt index 8b70bcf35a4..7238941de5a 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TaskToolViewTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TaskToolViewTest.kt @@ -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)) } diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TurnViewTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TurnViewTest.kt index 4b2e38e1fcf..2a16a5b9a07 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TurnViewTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/TurnViewTest.kt @@ -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`() { diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/base/AbstractSessionPartViewTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/base/AbstractSessionPartViewTest.kt index 6fb2cde06f5..92dd6039837 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/base/AbstractSessionPartViewTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/base/AbstractSessionPartViewTest.kt @@ -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, ))