Show which Claude account is logged in
"Claude Code / MAX 20X" says nothing about which account the plan belongs to, which matters when switching between a personal and a company login. - Read oauthAccount.emailAddress from ~/.claude.json. Nothing in the shell's own data carries account identity — the usage JSON under ~/.local/state/omarchy/agents/usage/claude.json has limits and token counts but no email — so it comes from Claude Code's own config. The FileView is watched, so switching accounts updates without restarting the shell. - Append it to the hero meta line, after the tier: "MAX 20X · you@example.com". - Put the local part in the bar beside the icon, so the account is visible without opening the panel. A WidgetButton rather than a Text with a MouseArea: WidgetButton registers itself in bar.clickTargets, and while a panel is open KeyboardPanel's full-screen overlay forwards bar-strip clicks only to registered targets — an unregistered handler opens the panel and then dismisses it with the same click. Mirrors the icon's three buttons. Only the Claude provider gets either; codex and fireworks store their auth elsewhere and would need their own readers rather than showing a misleading email. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,8 @@ import qs.Ui
|
||||
|
||||
Panel {
|
||||
id: root
|
||||
moduleName: "omarchy.agents"
|
||||
ipcTarget: "omarchy.agents"
|
||||
moduleName: "pastilhas.agents"
|
||||
ipcTarget: "pastilhas.agents"
|
||||
manageIpc: false
|
||||
|
||||
readonly property color foreground: bar ? bar.foreground : Color.foreground
|
||||
@@ -178,12 +178,52 @@ Panel {
|
||||
|
||||
// The plan you pay for, under the name of the tool it pays for. Limits live
|
||||
// in their own section; the hero just says what this is.
|
||||
// Email of the account Claude Code is currently logged in as, read from
|
||||
// ~/.claude.json (oauthAccount.emailAddress). Nothing in the shell's own data
|
||||
// carries it — the usage JSON under
|
||||
// ~/.local/state/omarchy/agents/usage/claude.json has limits and token counts
|
||||
// but no account identity — so it comes straight from Claude Code's config.
|
||||
//
|
||||
// Watched, so switching accounts updates the panel without a shell restart.
|
||||
// Only meaningful for the Claude provider; codex and fireworks store their
|
||||
// auth elsewhere and get nothing appended.
|
||||
property string claudeAccountEmail: ""
|
||||
|
||||
FileView {
|
||||
path: Quickshell.env("HOME") + "/.claude.json"
|
||||
watchChanges: true
|
||||
printErrors: false
|
||||
onFileChanged: reload()
|
||||
onLoadFailed: root.claudeAccountEmail = ""
|
||||
onLoaded: {
|
||||
try {
|
||||
var parsed = JSON.parse(text())
|
||||
var account = parsed && parsed.oauthAccount ? parsed.oauthAccount : {}
|
||||
root.claudeAccountEmail = String(account.emailAddress || "")
|
||||
} catch (e) {
|
||||
root.claudeAccountEmail = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function heroMeta(p) {
|
||||
if (!p) return ""
|
||||
if (String(p.usageStatusText || "") !== "") return p.usageStatusText
|
||||
var tier = String(p.tierLabel || "")
|
||||
if (tier === "") return "Subscription"
|
||||
return tier.charAt(0).toUpperCase() + tier.slice(1)
|
||||
|
||||
var base = ""
|
||||
if (String(p.usageStatusText || "") !== "") {
|
||||
base = p.usageStatusText
|
||||
} else {
|
||||
var tier = String(p.tierLabel || "")
|
||||
base = tier === "" ? "Subscription" : tier.charAt(0).toUpperCase() + tier.slice(1)
|
||||
}
|
||||
|
||||
// Which account the plan belongs to matters when you switch between a
|
||||
// personal and a company login, and "MAX 20X" alone doesn't say.
|
||||
if (String(p.providerId || "") === "claude" && root.claudeAccountEmail !== "") {
|
||||
return base + " · " + root.claudeAccountEmail
|
||||
}
|
||||
|
||||
return base
|
||||
}
|
||||
|
||||
// Local calendar date, recomputed from nowMs so a panel left open across
|
||||
@@ -298,8 +338,17 @@ Panel {
|
||||
// is invisible, so the icon appears the moment the first scan finds usage and
|
||||
// stays away entirely on a machine that has never run either CLI.
|
||||
visible: providers.length > 0
|
||||
implicitWidth: button.implicitWidth
|
||||
implicitHeight: button.implicitHeight
|
||||
implicitWidth: barContent.implicitWidth
|
||||
implicitHeight: barContent.implicitHeight
|
||||
|
||||
// Local part of the logged-in Claude account, for the bar. Switching between a
|
||||
// personal and a company login is otherwise invisible until the panel is
|
||||
// opened. Empty for the other providers, which store their auth elsewhere.
|
||||
readonly property string accountShortName: {
|
||||
if (!provider || String(provider.providerId || "") !== "claude") return ""
|
||||
var at = claudeAccountEmail.indexOf("@")
|
||||
return at > 0 ? claudeAccountEmail.substring(0, at) : claudeAccountEmail
|
||||
}
|
||||
|
||||
onProviderIndexChanged: if (panelFlick) panelFlick.contentY = 0
|
||||
onOpenedChanged: if (opened) {
|
||||
@@ -335,16 +384,47 @@ Panel {
|
||||
function next(): string { root.selectProvider(root.providerIndex + 1); return "ok" }
|
||||
}
|
||||
|
||||
BarIconButton {
|
||||
id: button
|
||||
// Account label beside the icon, matching the tailnet label on the Offscale
|
||||
// widget. BarIconButton is icon-only by design (labelVisible: false,
|
||||
// fixedWidth: slotSize, and its `text` feeds a glyph rather than a label), so
|
||||
// the name sits next to it in a Row rather than inside it.
|
||||
Row {
|
||||
id: barContent
|
||||
anchors.fill: parent
|
||||
bar: root.bar
|
||||
text: ""
|
||||
active: root.alarming
|
||||
onPressed: function(buttonCode) {
|
||||
if (buttonCode === Qt.RightButton) root.launchAgent()
|
||||
else if (buttonCode === Qt.MiddleButton) root.selectProvider(root.providerIndex + 1)
|
||||
else root.toggle()
|
||||
spacing: Style.space(4)
|
||||
|
||||
BarIconButton {
|
||||
id: button
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
bar: root.bar
|
||||
text: ""
|
||||
active: root.alarming
|
||||
onPressed: function(buttonCode) {
|
||||
if (buttonCode === Qt.RightButton) root.launchAgent()
|
||||
else if (buttonCode === Qt.MiddleButton) root.selectProvider(root.providerIndex + 1)
|
||||
else root.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
// A WidgetButton, not a Text with a MouseArea: WidgetButton registers itself
|
||||
// in bar.clickTargets, and while a panel is open KeyboardPanel's full-screen
|
||||
// overlay forwards bar-strip clicks only to registered targets. An
|
||||
// unregistered handler would open the panel and have the same click dismiss
|
||||
// it. Mirrors the icon's three buttons so the widget reads as one target.
|
||||
WidgetButton {
|
||||
id: accountLabel
|
||||
visible: text !== ""
|
||||
bar: root.bar
|
||||
text: root.accountShortName
|
||||
// A step below the icon font: it is a label, not a glyph, and the bar is
|
||||
// crowded.
|
||||
fontSize: Style.font.bodySmall
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onPressed: function(buttonCode) {
|
||||
if (buttonCode === Qt.RightButton) root.launchAgent()
|
||||
else if (buttonCode === Qt.MiddleButton) root.selectProvider(root.providerIndex + 1)
|
||||
else root.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user