Skip to content

feat: add Cursor account and resizable floating quota support - #139

Open
QinShushu1 wants to merge 1 commit into
Lampese:mainfrom
QinShushu1:cursor-support
Open

feat: add Cursor account and resizable floating quota support#139
QinShushu1 wants to merge 1 commit into
Lampese:mainfrom
QinShushu1:cursor-support

Conversation

@QinShushu1

Copy link
Copy Markdown

Disclaimer

This is an unofficial community contribution. It is not affiliated with, endorsed by, or supported by the original Codex Switcher maintainers, OpenAI, or Cursor. The Cursor integration is intended only for accounts owned by the user and must be used in accordance with each provider's terms. It opens the official Cursor application for sign-in, reads Cursor's local session database in read-only mode, sends the short-lived session only to Cursor-owned endpoints, and does not persist Cursor passwords or access tokens.

The upstream repository currently declares no software license. This fork and pull request are provided only for upstream review through GitHub's fork and pull-request workflow. They do not claim or grant independent permission to redistribute the upstream source or compiled binaries. No binary release has been published from this fork.

AI-Assisted Development Disclosure

The Cursor integration in this fork was primarily implemented by OpenAI's GPT-5.6 SOL. Human maintainers provide the product requirements, scope decisions, review, and final acceptance.

Summary

  • add a Cursor Login tab to the existing Add Account modal while preserving the native interaction and styling
  • detect the signed-in Cursor desktop account without storing Cursor credentials
  • read Cursor quota data from Cursor-owned endpoints
  • map apiPercentUsed to Third-party models and autoPercentUsed to First-party models
  • reuse the existing tray popup as an always-on-top, draggable, natively resizable floating panel
  • allow users to select visible Codex/Cursor accounts and optionally show reset times
  • keep Codex 5h/7d labels separate from Cursor billing-cycle quotas
  • add Windows support for redirected Cursor profile data while preferring the standard path

Privacy Review

  • no local account email addresses were found in the source or diff
  • no user-specific absolute paths were found
  • no GitHub tokens, OpenAI keys, or JWT-like secrets were found
  • the commit uses a GitHub noreply author address

Verification

  • Rust: 45 passed, 0 failed
  • Frontend tests: 3 passed, 0 failed
  • TypeScript and Vite production build: passed
  • cargo fmt --check: passed
  • git diff --check: passed
  • unsigned Tauri production build: passed
  • Windows UI: Cursor account detection and both quota labels verified against the Cursor usage page
  • Windows UI: Third-party models correctly showed remaining quota derived from Cursor's Other Models used percentage
  • Windows UI: the floating panel remained draggable and accepted native edge resizing while preserving the existing layout

The local Windows installers are unsigned and were used only for verification; they have not been attached to this PR or published as a release.

Copilot AI lite review requested due to automatic review settings August 30, 2026 05:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It introduces a new provider auth/session integration plus UI/window behavior changes that warrant careful human review (and there are a few reliability/perf issues to address).

Pull request overview

Adds an unofficial Cursor provider integration alongside Codex accounts, and extends the existing tray popup into a draggable, natively resizable “floating panel” controlled by persisted app settings.

Changes:

  • Introduces Cursor desktop account detection + quota fetching (read-only local session DB + Cursor endpoints).
  • Adds floating panel settings (pin behavior, visible account filtering, optional reset-time display) and wires them into the main UI + tray UI.
  • Makes the tray window resizable and keeps it visible on blur when “floating” is enabled.
File summaries
File Description
src/types/index.ts Adds shared frontend types for Cursor account info and persisted app settings.
src/TrayMenu.tsx Displays Cursor as an additional “account”, supports floating panel filtering and reset-time display, adds drag region.
src/components/AddAccountModal.tsx Adds a Cursor Login tab and supports cancel/complete flows for Cursor sign-in detection.
src/App.tsx Adds Floating Panel settings UI and hooks for Cursor login + settings persistence.
src-tauri/src/web.rs Exposes Cursor commands + app settings getter for the web invoke router.
src-tauri/src/types.rs Extends settings with floating panel options; adds CursorAccountInfo type.
src-tauri/src/tray.rs Makes tray window resizable and changes blur/toggle behavior based on floating setting.
src-tauri/src/lib.rs Registers new Cursor + settings-related Tauri commands.
src-tauri/src/commands/window.rs Implements get_app_settings and set_floating_panel_settings commands.
src-tauri/src/commands/mod.rs Adds the new cursor command module export.
src-tauri/src/commands/cursor.rs Implements Cursor session reading, login wait/cancel, and usage fetching/mapping.
src-tauri/src/auth/mod.rs Formatting-only change to the auth lock declaration.
src-tauri/Cargo.toml Adds rusqlite dependency (bundled SQLite) for Cursor session DB reads.
src-tauri/Cargo.lock Locks new rusqlite-related transitive dependencies.
README.md Documents the unofficial Cursor integration and AI-assisted development disclosure.
Review details

Suppressed comments (1)

src/TrayMenu.tsx:285

  • Same as initial load: a failure in get_app_settings currently aborts manual refresh and leaves the tray in an error state even if accounts/usage are otherwise available. Make settings load non-fatal during refresh as well.
      const [list, cursor, settings] = await Promise.all([
        invokeBackend<AccountInfo[]>("list_accounts"),
        invokeBackend<CursorAccountInfo | null>("cursor_account").catch(() => null),
        invokeBackend<AppSettings>("get_app_settings"),
      ]);
  • Files reviewed: 14/15 changed files
  • Comments generated: 6
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/App.tsx
Comment on lines +276 to +283
const loadProviderSettings = useCallback(async () => {
const [settings, cursor] = await Promise.all([
invokeBackend<AppSettings>("get_app_settings"),
invokeBackend<CursorAccountInfo | null>("cursor_account").catch(() => null),
]);
setFloatingSettings(settings);
setCursorAccount(cursor);
}, []);
Comment thread src/App.tsx
Comment on lines +1586 to +1602
<button
onClick={() => {
setIsNavMenuOpen(false);
setIsTimedWarmupOpen(false);
setIsFloatingSettingsOpen((prev) => !prev);
}}
className="flex w-full items-center justify-between gap-2 rounded-lg px-3 py-2 text-left text-sm transition-colors hover:bg-gray-100 dark:text-white dark:hover:bg-neutral-900"
>
<span>Floating Panel</span>
<span className={`rounded-md px-1.5 py-0.5 text-[11px] font-medium ${
floatingSettings?.floating_panel_enabled
? "bg-emerald-50 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300"
: "bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400"
}`}>
{floatingSettings?.floating_panel_enabled ? "On" : "Off"}
</span>
</button>
Comment thread src/TrayMenu.tsx
Comment on lines +237 to +241
const [list, cursor, settings] = await Promise.all([
invokeBackend<AccountInfo[]>("list_accounts"),
invokeBackend<CursorAccountInfo | null>("cursor_account").catch(() => null),
invokeBackend<AppSettings>("get_app_settings"),
]);
Comment thread src-tauri/src/tray.rs
Comment on lines 126 to +130
window.on_window_event(move |event| {
if let WindowEvent::Focused(false) = event {
if let Some(window) = app_handle.get_webview_window(TRAY_WINDOW) {
let _ = window.hide();
let pinned = crate::auth::load_app_settings()
.map(|settings| settings.floating_panel_enabled)
.unwrap_or(false);
Comment on lines +44 to +46
if let Ok(account) = fetch_cursor_account().await {
return Ok(account);
}
Comment thread src/TrayMenu.tsx
Comment on lines +378 to 380
const handleSwitch = useCallback(async (account: TrayAccount) => {
if (account.provider === "cursor") return;
if (account.is_active) {
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.

2 participants