Skip to content

Improve profile management and account switching UX#2

Open
TanjimReza wants to merge 6 commits intokitze:mainfrom
TanjimReza:main
Open

Improve profile management and account switching UX#2
TanjimReza wants to merge 6 commits intokitze:mainfrom
TanjimReza:main

Conversation

@TanjimReza
Copy link
Copy Markdown

@TanjimReza TanjimReza commented May 9, 2026

  1. Add visible edit/delete controls for saved profiles
  2. Improve account switching feedback with live status, spinner, and disabled overlapping actions
  3. Prevent manual ~/.codex login changes from overwriting the wrong saved profile
  4. Document the multi-account setup flow using codex login
Screenshot_.2026-05-09.at.00.46.38.mp4

Summary by CodeRabbit

  • New Features

    • Added profile deletion from the account context menu.
  • Documentation

    • Clarified that deleting a profile removes associated data but preserves local files.

Review Change Stack

TanjimReza and others added 4 commits May 8, 2026 23:52
…ements. Implemented guard clauses to prevent actions when account switching is in progress. Updated menu rendering logic and adjusted layout for better usability.
feat: profile modification, switching state enhancement
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Warning

Rate limit exceeded

@TanjimReza has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 37 minutes and 11 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1dd5db34-7020-4e3f-9da4-16af23071d78

📥 Commits

Reviewing files that changed from the base of the PR and between 892d8c6 and c9800ec.

📒 Files selected for processing (2)
  • README.md
  • Sources/CodexMaxx/main.swift
📝 Walkthrough

Walkthrough

Added profile deletion with a two-phase account switching model featuring identity-matching to prevent inadvertent file overwrites. UsageController is now an ObservableObject with concurrency guards. Storage layer supports profile deletion with safe conditional file copying based on matching identities between accounts. Menu UI expanded with delete buttons, loading indicators, and state feedback.

Changes

Profile Deletion & Account Switching Safety

Layer / File(s) Summary
Feature Documentation
README.md
Added "Profile deletion from the account context menu" to features list. Clarified privacy section: deleting a CodexMaxx profile removes its copy and metadata, but ~/.codex is unaffected.
Observable Object & State
Sources/CodexMaxx/main.swift
UsageController converted to ObservableObject with @Published properties (accounts, updatedAt, isRefreshing, lastError, switchingAccountName, statusMessage) and computed canStartAccountAction to block overlapping operations.
Profile Storage & Identity Matching
Sources/CodexMaxx/main.swift
CodexProfileStore.deleteProfile(named:) removes profile directory and clears config.active if needed. Added identity helpers (profilesHaveMatchingIdentity, profileIdentity(at:), email(fromIDToken:)) that parse auth.json tokens to determine account identity for safe file-copying decisions during switches.
Controller Switching & Deletion
Sources/CodexMaxx/main.swift
switchToAccount(named:) refactored into startSwitching(to:) (sets status/state) and finishSwitching(to:) (performs switch, refreshes, clears flag). Added deleteAccount(named:) async method. CodexAccountUsage.withActive(_:) helper added. During switch, file copying is now conditional on matching account identity.
AppDelegate Control Flow & Concurrency Guards
Sources/CodexMaxx/main.swift
Refresh, add, and switch actions guarded behind canStartAccountAction. render(updateMenu:) signature extended to conditionally rebuild menu. Added deleteAccount(_:) flow with NSAlert confirmation. Menu wiring passes onDelete handler; refresh and add items enabled only when actions can start.
Menu UI & Account Row Rendering
Sources/CodexMaxx/main.swift
HostingMenuView frame increased to 340×238. MenuContent now observes UsageController and renders switching-progress UI. AccountRow accepts switchingAccountName, actionsDisabled, onDelete inputs; renders loading indicators during switch, edit/delete buttons with disabled state, and context menu with destructive delete action. Menu width set to 340.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A rabbit hops through the garden of code,
Deleting profiles like weeds from the road,
With identity checks and switching so fine,
The safety nets glimmer in each careful line.
🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main changes: profile deletion functionality and improved account switching with better UX (spinner, status feedback, action prevention).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
Sources/CodexMaxx/main.swift (1)

1076-1086: 💤 Low value

Duplicated JWT id_token email decoding.

This helper duplicates the logic in CodexReconciledState.email(from:) at lines 1523–1533 (same base64url normalization, padding loop, and https://api.openai.com/profile fallback). Consider extracting a single shared helper (e.g., CodexIDToken.email(from:)) so both call sites stay in sync if the token shape ever changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/CodexMaxx/main.swift` around lines 1076 - 1086, The JWT email
decoding logic is duplicated between private static func email(fromIDToken:) and
CodexReconciledState.email(from:); extract that shared logic into a single
helper (e.g., a new static method CodexIDToken.email(from: String?) or similar)
that performs base64url normalization, padding, JSON decoding, and the
"https://api.openai.com/profile" fallback, then replace both email(fromIDToken:)
and CodexReconciledState.email(from:) to call the new helper so both call sites
use the same implementation and keep behavior in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/CodexMaxx/main.swift`:
- Around line 1048-1054: The current profilesHaveMatchingIdentity(_:_:) returns
true when either profileIdentity(at:) fails, which lets a corrupt/unreadable
live ~/.codex trigger copySwitchedFiles(from: liveCodexHome, to: currentHome);
change the guard so that if either lhsIdentity or rhsIdentity is nil the
function returns false (i.e., only return lhsIdentity == rhsIdentity when both
identities are present). Update the implementation of
profilesHaveMatchingIdentity to treat missing/unreadable identities as
non-matching to prevent the copy-back in ambiguous cases.
- Around line 821-832: The success statusMessage set in finishSwitching(to:) is
never cleared, causing a stale "Switched to ..." message to linger; fix it by
(1) resetting statusMessage = nil at the start of refresh() (and/or
startSwitching(to:)) to ensure refreshes clear stale state, and (2) in
finishSwitching(to:) schedule an asynchronous auto-clear (e.g. spawn a Task that
awaits a short delay then on the MainActor sets self.statusMessage = nil) so the
success message is visible briefly then removed automatically.

---

Nitpick comments:
In `@Sources/CodexMaxx/main.swift`:
- Around line 1076-1086: The JWT email decoding logic is duplicated between
private static func email(fromIDToken:) and CodexReconciledState.email(from:);
extract that shared logic into a single helper (e.g., a new static method
CodexIDToken.email(from: String?) or similar) that performs base64url
normalization, padding, JSON decoding, and the "https://api.openai.com/profile"
fallback, then replace both email(fromIDToken:) and
CodexReconciledState.email(from:) to call the new helper so both call sites use
the same implementation and keep behavior in sync.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0681015e-c925-44ba-97b6-0d1bb8d84bdd

📥 Commits

Reviewing files that changed from the base of the PR and between 15b0ed6 and 892d8c6.

📒 Files selected for processing (2)
  • README.md
  • Sources/CodexMaxx/main.swift

Comment on lines +821 to +832
func finishSwitching(to name: String) async {
do {
try CodexProfileStore.switchToProfile(named: name)
await self.refresh()
self.statusMessage = "Switched to \(self.displayName(for: name))"
} catch {
await self.refresh()
self.lastError = error.localizedDescription
self.statusMessage = nil
}
self.switchingAccountName = nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Success statusMessage lingers indefinitely.

statusMessage is set to "Switched to ..." on success and is never cleared by refresh() or any timer, so the green checkmark + message keeps showing in the menu across subsequent refreshes (and even hours later) until another switch starts or the app restarts. Consider clearing it after a short delay, or resetting it at the top of refresh() / startSwitching(to:) so stale state doesn't accumulate.

🧹 One possible fix — auto-clear after a short delay
     func finishSwitching(to name: String) async {
         do {
             try CodexProfileStore.switchToProfile(named: name)
             await self.refresh()
-            self.statusMessage = "Switched to \(self.displayName(for: name))"
+            let message = "Switched to \(self.displayName(for: name))"
+            self.statusMessage = message
+            Task { [weak self] in
+                try? await Task.sleep(nanoseconds: 4_000_000_000)
+                await MainActor.run {
+                    if self?.statusMessage == message { self?.statusMessage = nil }
+                }
+            }
         } catch {
             await self.refresh()
             self.lastError = error.localizedDescription
             self.statusMessage = nil
         }
         self.switchingAccountName = nil
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/CodexMaxx/main.swift` around lines 821 - 832, The success
statusMessage set in finishSwitching(to:) is never cleared, causing a stale
"Switched to ..." message to linger; fix it by (1) resetting statusMessage = nil
at the start of refresh() (and/or startSwitching(to:)) to ensure refreshes clear
stale state, and (2) in finishSwitching(to:) schedule an asynchronous auto-clear
(e.g. spawn a Task that awaits a short delay then on the MainActor sets
self.statusMessage = nil) so the success message is visible briefly then removed
automatically.

Comment thread Sources/CodexMaxx/main.swift
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant