蜂鸟Pro v2.0.1 - 基础框架版本 (待完善)

## 当前状态
- 插件界面已完成重命名 (cursorpro → hummingbird)
- 双账号池 UI 已实现 (Auto/Pro 卡片)
- 后端已切换到 MySQL 数据库
- 添加了 Cursor 官方用量 API 文档

## 已知问题 (待修复)
1. 激活时检查账号导致无账号时激活失败
2. 未启用无感换号时不应获取账号
3. 账号用量模块不显示 (seamless 未启用时应隐藏)
4. 积分显示为 0 (后端未正确返回)
5. Auto/Pro 双密钥逻辑混乱,状态不同步
6. 账号添加后无自动分析功能

## 下一版本计划
- 重构数据模型,优化账号状态管理
- 实现 Cursor API 自动分析账号
- 修复激活流程,不依赖账号
- 启用无感时才分配账号
- 完善账号用量实时显示

## 文件说明
- docs/系统设计文档.md - 完整架构设计
- cursor 官方用量接口.md - Cursor API 文档
- 参考计费/ - Vibeviewer 开源项目参考

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ccdojox-crypto
2025-12-18 11:21:52 +08:00
parent f310ca7b97
commit 73a71f198f
202 changed files with 19142 additions and 252 deletions

View File

@@ -0,0 +1,26 @@
// swift-tools-version:5.10
import PackageDescription
let package = Package(
name: "VibeviewerLoginUI",
platforms: [
.macOS(.v14)
],
products: [
.library(name: "VibeviewerLoginUI", targets: ["VibeviewerLoginUI"]),
],
dependencies: [
.package(path: "../VibeviewerShareUI")
],
targets: [
.target(
name: "VibeviewerLoginUI",
dependencies: [
"VibeviewerShareUI"
]
),
.testTarget(name: "VibeviewerLoginUITests", dependencies: ["VibeviewerLoginUI"])
]
)

View File

@@ -0,0 +1,52 @@
import SwiftUI
import WebKit
struct CookieWebView: NSViewRepresentable {
let onCookieCaptured: (String) -> Void
func makeNSView(context: Context) -> WKWebView {
let config = WKWebViewConfiguration()
let webView = WKWebView(frame: .zero, configuration: config)
webView.navigationDelegate = context.coordinator
if let url =
URL(
string: "https://authenticator.cursor.sh/"
)
{
webView.load(URLRequest(url: url))
}
return webView
}
func updateNSView(_ nsView: WKWebView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(onCookieCaptured: self.onCookieCaptured)
}
final class Coordinator: NSObject, WKNavigationDelegate {
let onCookieCaptured: (String) -> Void
init(onCookieCaptured: @escaping (String) -> Void) {
self.onCookieCaptured = onCookieCaptured
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if webView.url?.absoluteString.hasSuffix("/dashboard") == true {
self.captureCursorCookies(from: webView)
}
}
private func captureCursorCookies(from webView: WKWebView) {
webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
let relevant = cookies.filter { cookie in
let domain = cookie.domain.lowercased()
return domain.contains("cursor.com")
}
guard !relevant.isEmpty else { return }
let headerString = relevant.map { "\($0.name)=\($0.value)" }.joined(separator: "; ")
self.onCookieCaptured(headerString)
}
}
}
}

View File

@@ -0,0 +1,12 @@
import SwiftUI
private struct LoginWindowManagerKey: EnvironmentKey {
static let defaultValue: LoginWindowManager = .shared
}
public extension EnvironmentValues {
var loginWindowManager: LoginWindowManager {
get { self[LoginWindowManagerKey.self] }
set { self[LoginWindowManagerKey.self] = newValue }
}
}

View File

@@ -0,0 +1,38 @@
import AppKit
import SwiftUI
public final class LoginWindowManager {
public static let shared = LoginWindowManager()
private var controller: LoginWindowController?
public func show(onCookieCaptured: @escaping (String) -> Void) {
if let controller {
controller.showWindow(nil)
controller.window?.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
return
}
let controller = LoginWindowController(onCookieCaptured: { [weak self] cookie in
onCookieCaptured(cookie)
self?.close()
})
self.controller = controller
controller.window?.center()
controller.showWindow(nil)
controller.window?.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
if let hosting = controller.contentViewController as? NSHostingController<CursorLoginView> {
hosting.rootView = CursorLoginView(onCookieCaptured: { cookie in
onCookieCaptured(cookie)
self.close()
}, onClose: { [weak self] in
self?.close()
})
}
}
public func close() {
self.controller?.close()
self.controller = nil
}
}

View File

@@ -0,0 +1,20 @@
import AppKit
import SwiftUI
final class LoginWindowController: NSWindowController, NSWindowDelegate {
private var onCookieCaptured: ((String) -> Void)?
convenience init(onCookieCaptured: @escaping (String) -> Void) {
let vc = NSHostingController(rootView: CursorLoginView(onCookieCaptured: { cookie in
onCookieCaptured(cookie)
}, onClose: {}))
let window = NSWindow(contentViewController: vc)
window.title = "Cursor 登录"
window.setContentSize(NSSize(width: 900, height: 680))
window.styleMask = [.titled, .closable, .miniaturizable, .resizable]
window.isReleasedWhenClosed = false
self.init(window: window)
self.onCookieCaptured = onCookieCaptured
self.window?.delegate = self
}
}

View File

@@ -0,0 +1,16 @@
import SwiftUI
@MainActor
struct CursorLoginView: View {
let onCookieCaptured: (String) -> Void
let onClose: () -> Void
var body: some View {
VStack(spacing: 0) {
CookieWebView(onCookieCaptured: { cookie in
self.onCookieCaptured(cookie)
self.onClose()
})
}
}
}

View File

@@ -0,0 +1,8 @@
@testable import VibeviewerLoginUI
import XCTest
final class VibeviewerLoginUITests: XCTestCase {
func testExample() {
XCTAssertTrue(true)
}
}