mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
fix(jetbrains): hide account overlay when not logged in
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Show the logged-out account status in the same rounded panel as the logged-in account overlay, with a "Not logged in" label, hidden picker/balance, and a profile icon to open settings.
|
||||
-1
@@ -149,7 +149,6 @@ class SessionUi(
|
||||
|
||||
account = SessionAccountOverlay(
|
||||
select = { org -> controller.selectOrganization(org) },
|
||||
login = { controller.openProfile() },
|
||||
profile = { controller.openProfile() },
|
||||
)
|
||||
root.addOverlay(account) { pane, child ->
|
||||
|
||||
+8
-87
@@ -18,17 +18,13 @@ import com.intellij.ui.components.JBList
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import java.awt.CardLayout
|
||||
import java.awt.Cursor
|
||||
import java.awt.GridBagConstraints
|
||||
import java.awt.GridBagLayout
|
||||
import java.awt.event.KeyEvent
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.text.DecimalFormat
|
||||
import javax.swing.Box
|
||||
import javax.swing.BoxLayout
|
||||
import javax.swing.JButton
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.KeyStroke
|
||||
@@ -38,38 +34,14 @@ import javax.swing.ScrollPaneConstants
|
||||
/**
|
||||
* Compact account overlay shown in the top-right of the empty session screen.
|
||||
*
|
||||
* Displays logged-out prompt or logged-in account/balance info.
|
||||
* Only visible when logged in. Hidden when not logged in or no profile is available.
|
||||
* Visibility is controlled entirely by [onEvent] — never set [isVisible] externally.
|
||||
*/
|
||||
internal class SessionAccountOverlay(
|
||||
private val select: (String?) -> Unit,
|
||||
private val login: () -> Unit,
|
||||
private val profile: () -> Unit,
|
||||
) : BorderLayoutPanel() {
|
||||
|
||||
companion object {
|
||||
private const val CARD_OUT = "out"
|
||||
private const val CARD_IN = "in"
|
||||
}
|
||||
|
||||
private val loginLabel = JBLabel(KiloBundle.message("profile.notLoggedIn")).apply {
|
||||
foreground = UiStyle.Colors.weak()
|
||||
}
|
||||
private val loginBtn = JButton(KiloBundle.message("profile.action.login")).apply {
|
||||
isOpaque = false
|
||||
addActionListener { login() }
|
||||
}
|
||||
private val outCard = JPanel(GridBagLayout()).apply {
|
||||
isOpaque = false
|
||||
add(loginLabel, GridBagConstraints().apply {
|
||||
gridx = 0; gridy = 0; anchor = GridBagConstraints.WEST
|
||||
})
|
||||
add(loginBtn, GridBagConstraints().apply {
|
||||
gridx = 0; gridy = 1; anchor = GridBagConstraints.CENTER
|
||||
insets = JBUI.insetsTop(UiStyle.Gap.sm())
|
||||
})
|
||||
}
|
||||
|
||||
private val picker = PickerButton().apply {
|
||||
isEnabled = false
|
||||
text = " "
|
||||
@@ -110,27 +82,13 @@ internal class SessionAccountOverlay(
|
||||
addToCenter(row)
|
||||
}
|
||||
|
||||
private val inCard = JPanel(GridBagLayout()).apply {
|
||||
isOpaque = false
|
||||
add(panel, GridBagConstraints().apply {
|
||||
gridx = 0; gridy = 0; fill = GridBagConstraints.HORIZONTAL
|
||||
})
|
||||
}
|
||||
|
||||
private val cardLayout = CardLayout()
|
||||
private val cards = JPanel(cardLayout).apply {
|
||||
isOpaque = false
|
||||
add(outCard, CARD_OUT)
|
||||
add(inCard, CARD_IN)
|
||||
}
|
||||
|
||||
private var choices: List<AccountChoice> = emptyList()
|
||||
private var currentOrgId: String? = null
|
||||
|
||||
init {
|
||||
isOpaque = false
|
||||
isVisible = false
|
||||
addToCenter(cards)
|
||||
addToCenter(panel)
|
||||
}
|
||||
|
||||
fun onEvent(event: SessionControllerEvent.AccountOverlayChanged) {
|
||||
@@ -148,16 +106,13 @@ internal class SessionAccountOverlay(
|
||||
val snap = event.account
|
||||
val prof = snap.profile
|
||||
if (prof == null) {
|
||||
if (!snap.transient) {
|
||||
layout = showCard(CARD_OUT) || layout
|
||||
if (!isVisible) {
|
||||
isVisible = true
|
||||
layout = true
|
||||
}
|
||||
if (!snap.transient && isVisible) {
|
||||
isVisible = false
|
||||
layout = true
|
||||
paint = true
|
||||
}
|
||||
} else {
|
||||
layout = updateLoggedIn(prof, snap.switching, snap.targetOrgId) || layout
|
||||
layout = showCard(CARD_IN) || layout
|
||||
if (!isVisible) {
|
||||
isVisible = true
|
||||
layout = true
|
||||
@@ -169,22 +124,9 @@ internal class SessionAccountOverlay(
|
||||
if (layout || paint) repaint()
|
||||
}
|
||||
|
||||
private fun activeCard(): String? {
|
||||
for (i in 0 until cards.componentCount) {
|
||||
val comp = cards.getComponent(i)
|
||||
if (comp.isVisible) return if (comp === inCard) CARD_IN else CARD_OUT
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun showCard(card: String): Boolean {
|
||||
if (activeCard() == card) return false
|
||||
cardLayout.show(cards, card)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun updateLoggedIn(prof: ai.kilocode.rpc.dto.ProfileDto, switching: Boolean, target: String?): Boolean {
|
||||
var layout = false
|
||||
|
||||
val orgs = prof.organizations
|
||||
val next = listOf(AccountChoice(null, KiloBundle.message("profile.personalAccount"))) +
|
||||
orgs.map { org -> AccountChoice(org.id, org.name) }
|
||||
@@ -216,11 +158,6 @@ internal class SessionAccountOverlay(
|
||||
}
|
||||
if (picker.toolTipText != tip) picker.toolTipText = tip
|
||||
|
||||
if (!picker.isVisible) {
|
||||
picker.isVisible = true
|
||||
layout = true
|
||||
}
|
||||
|
||||
layout = syncBalance(prof) || layout
|
||||
return layout
|
||||
}
|
||||
@@ -333,23 +270,7 @@ internal class SessionAccountOverlay(
|
||||
popup.showUnderneathOf(picker)
|
||||
}
|
||||
|
||||
internal fun loggedInVisible() = isVisible && cards.let {
|
||||
var card = CARD_OUT
|
||||
for (i in 0 until it.componentCount) {
|
||||
val comp = it.getComponent(i)
|
||||
if (comp.isVisible) card = if (comp === inCard) CARD_IN else CARD_OUT
|
||||
}
|
||||
card == CARD_IN
|
||||
}
|
||||
|
||||
internal fun loggedOutVisible() = isVisible && cards.let {
|
||||
for (i in 0 until it.componentCount) {
|
||||
val comp = it.getComponent(i)
|
||||
if (comp.isVisible) return@let comp === outCard
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
internal fun loggedInVisible() = isVisible
|
||||
internal fun accountTitle(): String? = picker.text?.removeSuffix(" ▾")?.ifBlank { null }
|
||||
internal fun pickerEnabled() = picker.isEnabled
|
||||
internal fun pickerVisible() = picker.isVisible
|
||||
|
||||
+23
-12
@@ -15,15 +15,12 @@ import com.intellij.icons.AllIcons
|
||||
class SessionAccountOverlayTest : SessionControllerTestBase() {
|
||||
|
||||
private lateinit var panel: SessionAccountOverlay
|
||||
private val selected = mutableListOf<String?>()
|
||||
private var loginCalls = 0
|
||||
private var profileCalls = 0
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
panel = SessionAccountOverlay(
|
||||
select = { org -> selected.add(org) },
|
||||
login = { loginCalls++ },
|
||||
select = { },
|
||||
profile = { profileCalls++ },
|
||||
)
|
||||
}
|
||||
@@ -56,13 +53,12 @@ class SessionAccountOverlayTest : SessionControllerTestBase() {
|
||||
private fun org(id: String, name: String, role: String = "MEMBER") =
|
||||
ProfileOrganizationDto(id = id, name = name, role = role)
|
||||
|
||||
// --- test 1: logged-out state shows login prompt ---
|
||||
// --- test 1: logged-out state hides the overlay entirely ---
|
||||
|
||||
fun `test logged out state is visible with login button`() {
|
||||
fun `test logged out state hides overlay`() {
|
||||
show(snap(null))
|
||||
|
||||
assertTrue(panel.isVisible)
|
||||
assertTrue(panel.loggedOutVisible())
|
||||
assertFalse(panel.isVisible)
|
||||
}
|
||||
|
||||
// --- test 2: logged-in personal account shows picker title ---
|
||||
@@ -113,18 +109,22 @@ class SessionAccountOverlayTest : SessionControllerTestBase() {
|
||||
// --- test 4: programmatic update does not call select callback ---
|
||||
|
||||
fun `test programmatic update does not call select callback`() {
|
||||
val selected = mutableListOf<String?>()
|
||||
val p = SessionAccountOverlay(
|
||||
select = { org -> selected.add(org) },
|
||||
profile = {},
|
||||
)
|
||||
val acme = org("org_1", "Acme")
|
||||
val prof = profile(
|
||||
email = "user@example.com",
|
||||
organizations = listOf(acme),
|
||||
currentOrgId = null,
|
||||
)
|
||||
// Show with personal account selected
|
||||
show(snap(prof))
|
||||
edt { p.onEvent(SessionControllerEvent.AccountOverlayChanged.Show(snap(prof))) }
|
||||
selected.clear()
|
||||
|
||||
// Show again with same profile - no user selection
|
||||
show(snap(prof))
|
||||
edt { p.onEvent(SessionControllerEvent.AccountOverlayChanged.Show(snap(prof))) }
|
||||
|
||||
assertEquals(0, selected.size)
|
||||
}
|
||||
@@ -226,7 +226,7 @@ class SessionAccountOverlayTest : SessionControllerTestBase() {
|
||||
)
|
||||
show(transientSnap)
|
||||
|
||||
// Should remain visible and logged-in, not flash to logged-out
|
||||
// Should remain visible and logged-in, not flash to hidden
|
||||
assertTrue(panel.isVisible)
|
||||
assertTrue(panel.loggedInVisible())
|
||||
}
|
||||
@@ -317,4 +317,15 @@ class SessionAccountOverlayTest : SessionControllerTestBase() {
|
||||
assertTrue(panel.balanceVisible())
|
||||
assertSame(icon, panel.balanceIcon())
|
||||
}
|
||||
|
||||
// --- test 16: non-transient null profile after login hides overlay ---
|
||||
|
||||
fun `test non-transient null profile after login hides overlay`() {
|
||||
show(snap(profile(email = "user@example.com")))
|
||||
assertTrue(panel.isVisible)
|
||||
|
||||
show(snap(null))
|
||||
|
||||
assertFalse(panel.isVisible)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user