From 7af30ed7af81784a4215cd75ca99b12ee3c3f779 Mon Sep 17 00:00:00 2001 From: NessZerra <90105158+Finesssee@users.noreply.github.com> Date: Sat, 19 Sep 2026 15:54:51 +0700 Subject: [PATCH] Protect Claude account labels in privacy mode --- CHANGELOG.md | 3 ++ .../components/ClaudeAccountsMenu.test.tsx | 23 +++++++++++-- .../src/components/ClaudeAccountsMenu.tsx | 18 +++++++--- .../src/components/claudeAccountDisplay.ts | 34 +++++++++++++++++++ 4 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 apps/desktop-tauri/src/components/claudeAccountDisplay.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a1d2ed3f74..9405dd6241 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Added - Grok: Settings and tray **Add account** flow matching Codex/Claude — isolated `grok login --oauth`, save current CLI login, switch, and remove without logging out the active session. +### Fixed +- Claude: when Hide Personal Info is enabled, keep saved account rows distinguishable with stable localized `Account N` labels and matching redacted tooltips. + --- ## [Windows] 0.60.3 - 2026-09-15 diff --git a/apps/desktop-tauri/src/components/ClaudeAccountsMenu.test.tsx b/apps/desktop-tauri/src/components/ClaudeAccountsMenu.test.tsx index b403a87540..a3c4e558e4 100644 --- a/apps/desktop-tauri/src/components/ClaudeAccountsMenu.test.tsx +++ b/apps/desktop-tauri/src/components/ClaudeAccountsMenu.test.tsx @@ -1,4 +1,4 @@ -import { act, fireEvent, render, screen } from "@testing-library/react"; +import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { ClaudeAccount } from "../types/bridge"; @@ -39,12 +39,31 @@ describe("ClaudeAccountsMenu", () => { expect(mocks.claudeAccountSwitch).toHaveBeenCalledWith(second.id); }); - it("masks emails, including tooltips, when hideEmail is enabled", async () => { + it("uses stable opaque account labels and redacts tooltips when hideEmail is enabled", async () => { mocks.claudeAccountsList.mockResolvedValue([first, { ...second, organization: `${second.email}'s Organization` }]); const { container } = render(); await screen.findByText("ClaudeAccountsTitle"); + const labels = container.querySelectorAll(".codex-menu-accounts__email"); + expect(labels[0].firstChild?.textContent).toBe("Account 1"); + expect(labels[0].getAttribute("title")).toBe("Account 1"); + expect(labels[1].firstChild?.textContent).toBe("Account 2"); + expect(labels[1].getAttribute("title")).toBe("Account 2"); expect(container.textContent).not.toContain(first.email); expect(container.innerHTML).not.toContain(second.email); + expect(container.textContent).not.toContain("Personal"); + expect(container.textContent).not.toContain("Organization"); + }); + + it("keeps opaque labels stable when the source reorders accounts", async () => { + const { container } = render(); + await screen.findByText("ClaudeAccountsTitle"); + mocks.claudeAccountsList.mockResolvedValue([second, first]); + await act(async () => window.dispatchEvent(new Event("focus"))); + await waitFor(() => { + const labels = container.querySelectorAll(".codex-menu-accounts__email"); + expect(labels[0].firstChild?.textContent).toBe("Account 2"); + expect(labels[1].firstChild?.textContent).toBe("Account 1"); + }); }); it("shows switch failures and leaves the current account marked active", async () => { diff --git a/apps/desktop-tauri/src/components/ClaudeAccountsMenu.tsx b/apps/desktop-tauri/src/components/ClaudeAccountsMenu.tsx index eccc12b2b5..c15e78ebe3 100644 --- a/apps/desktop-tauri/src/components/ClaudeAccountsMenu.tsx +++ b/apps/desktop-tauri/src/components/ClaudeAccountsMenu.tsx @@ -3,7 +3,10 @@ import { listen } from "@tauri-apps/api/event"; import type { ClaudeAccount } from "../types/bridge"; import { claudeAccountsList, claudeAccountSwitch } from "../lib/tauri"; import { useLocale } from "../hooks/useLocale"; -import { maskEmail } from "./MenuCard"; +import { + buildClaudeAccountOrdinals, + buildPrivateClaudeAccountLabel, +} from "./claudeAccountDisplay"; export default function ClaudeAccountsMenu({ hideEmail, onLayoutChange }: { hideEmail: boolean; @@ -40,6 +43,8 @@ export default function ClaudeAccountsMenu({ hideEmail, onLayoutChange }: { onLayoutChange?.(); }, [accounts.length, error, switched, onLayoutChange]); + const accountOrdinals = buildClaudeAccountOrdinals(accounts); + const switchAccount = async (id: string) => { setBusy(true); setError(null); @@ -67,13 +72,18 @@ export default function ClaudeAccountsMenu({ hideEmail, onLayoutChange }: { {switched &&

{t("ClaudeAccountsSwitched")}

}