Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions Clipy/Sources/Managers/MenuManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2486,20 +2486,22 @@ class BoardManPanel: NSPanel {
licenseLimitsLabel = limitsLabel

let licenseKey = NSTextField(frame: .zero)
licenseKey.placeholderString = "XXXX-XXXX-XXXX-XXXX"
licenseKey.placeholderString = "License activation is not connected yet"
licenseKey.font = NSFont.monospacedSystemFont(ofSize: 11, weight: .regular)
licenseKey.toolTip = "Founder codes activate locally. Production licenses still require signed tokens."
licenseKey.isEnabled = false
licenseKey.toolTip = "License activation is a disabled preview in this build."
contentView.addSubview(licenseKey)
licenseKeyField = licenseKey

let activate = NSButton(title: "Activate", target: self, action: #selector(activateLicenseRequested(_:)))
activate.font = NSFont.systemFont(ofSize: 11, weight: .medium)
activate.bezelStyle = .rounded
activate.toolTip = "Activates an internal founder code locally when it matches."
activate.isEnabled = false
activate.toolTip = "Activation is not connected yet."
contentView.addSubview(activate)
licenseActivateButton = activate

let activationStatus = NSTextField(labelWithString: "Internal dogfood: founder codes activate locally. Unknown codes stay locked.")
let activationStatus = NSTextField(labelWithString: "Activation is not connected yet. Free remains the default runtime entitlement.")
activationStatus.font = NSFont.systemFont(ofSize: 11)
activationStatus.textColor = .secondaryLabelColor
activationStatus.lineBreakMode = .byWordWrapping
Expand Down Expand Up @@ -2528,7 +2530,7 @@ class BoardManPanel: NSPanel {
contentView.addSubview(lockedProControl)
licenseProLockedControlView = lockedProControl

let licenseNote = NSTextField(labelWithString: "Internal dogfood only. Production licensing still requires signed token activation.")
let licenseNote = NSTextField(labelWithString: "Static preview only. 1 license = 1 PC. No backend activation, device binding, or license storage is connected.")
licenseNote.font = NSFont.systemFont(ofSize: 11)
licenseNote.textColor = .secondaryLabelColor
licenseNote.lineBreakMode = .byWordWrapping
Expand Down Expand Up @@ -4045,12 +4047,8 @@ class BoardManPanel: NSPanel {
}

@objc private func activateLicenseRequested(_ sender: NSButton) {
let request = LicenseActivationRequest(
licenseKey: licenseKeyField?.stringValue ?? ""
)
let response = StubLicenseActivationClient().activate(request)
licenseActivationStatusLabel?.stringValue = "\(licenseActivationStatusTitle(response.status)): \(response.message)"
licenseActivationStatusLabel?.textColor = response.status == .activated ? .systemGreen : (response.status == .invalidInput || response.status == .unsupported ? .systemRed : .secondaryLabelColor)
licenseActivationStatusLabel?.stringValue = "Activation is not connected yet. Free remains the default runtime entitlement."
licenseActivationStatusLabel?.textColor = .secondaryLabelColor
refreshLicenseSummary()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import Cocoa

final class CPYBetaPreferenceViewController: NSViewController {
private let licenseField = NSSecureTextField(string: "")
private let validationLabel = BoardManPreferenceUI.label("Awaiting activation", size: 12, color: BoardManPreferenceUI.secondaryText)
private let validationLabel = BoardManPreferenceUI.label("Not connected yet", size: 12, color: BoardManPreferenceUI.secondaryText)
private let planLabel = BoardManPreferenceUI.label("Free Plan", size: 28, weight: .bold)
private let statusPill = BoardManPreferenceUI.label("Free", size: 12, weight: .semibold)
private let currentPlanValue = BoardManPreferenceUI.label("Free Plan", size: 12, weight: .semibold)
private let statusValue = BoardManPreferenceUI.label("Free", size: 12, weight: .semibold)
private let lastVerifiedValue = BoardManPreferenceUI.label("Not verified / Offline", size: 12, weight: .semibold, color: BoardManPreferenceUI.red)
private let deviceStatus = BoardManPreferenceUI.label("Not activated", size: 12, color: BoardManPreferenceUI.secondaryText)

override func loadView() {
Expand All @@ -29,38 +32,45 @@ final class CPYBetaPreferenceViewController: NSViewController {
statusPill.frame = NSRect(x: 20, y: 118, width: 70, height: 24)
BoardManPreferenceUI.prepare(statusPill, color: BoardManPreferenceUI.card, radius: 12, border: BoardManPreferenceUI.borderSubtle)
currentPlan.addSubview(statusPill)
currentPlan.addSubview(infoRow("clock", "Last verified:", "Offline", accent: true, y: 78))
currentPlan.addSubview(infoRow("laptopcomputer", "Device activated:", "Not activated", y: 44))
currentPlan.addSubview(statusRow("tag", "Current Plan:", currentPlanValue, y: 92))
currentPlan.addSubview(statusRow("checkmark.seal", "Status:", statusValue, y: 66))
currentPlan.addSubview(statusRow("clock", "Last verified:", lastVerifiedValue, y: 40))
currentPlan.addSubview(statusRow("laptopcomputer", "Device activated:", deviceStatus, y: 14))
let upgrade = BoardManPreferenceUI.secondaryButton("Upgrade to Pro ›")
upgrade.target = self
upgrade.action = #selector(openBuyPro)
upgrade.frame = NSRect(x: 200, y: 150, width: 130, height: 38)
upgrade.layer?.borderColor = BoardManPreferenceUI.red.withAlphaComponent(0.8).cgColor
currentPlan.addSubview(upgrade)
currentPlan.addSubview(BoardManPreferenceUI.label("ⓘ Pro unlocks advanced customization and unlimited usage.", size: 11, color: BoardManPreferenceUI.secondaryText).positioned(x: 20, y: 18, w: 310, h: 18))
view.addSubview(currentPlan)

let activation = card(x: 395, y: 320, w: 405, h: 250, title: "2. License Key Activation", icon: "key")
licenseField.placeholderString = "Enter your license key"
licenseField.placeholderString = "License activation is not connected yet"
licenseField.font = NSFont.systemFont(ofSize: 14)
licenseField.textColor = BoardManPreferenceUI.primaryText
licenseField.backgroundColor = BoardManPreferenceUI.field
licenseField.isEnabled = false
licenseField.toolTip = "License activation is a disabled preview in this build."
licenseField.frame = NSRect(x: 20, y: 165, width: 365, height: 38)
activation.addSubview(licenseField)
let activate = BoardManPreferenceUI.primaryButton("Activate License")
activate.target = self
activate.action = #selector(activateLicense)
activate.isEnabled = false
activate.toolTip = "Activation is not connected yet."
activate.frame = NSRect(x: 20, y: 115, width: 165, height: 38)
activation.addSubview(activate)
let paste = BoardManPreferenceUI.secondaryButton("Paste from Clipboard")
paste.target = self
paste.action = #selector(pasteLicense)
paste.isEnabled = false
paste.toolTip = "License input is disabled until activation is implemented."
paste.frame = NSRect(x: 195, y: 115, width: 190, height: 38)
activation.addSubview(paste)
activation.addSubview(BoardManPreferenceUI.label("Validation status:", size: 13, color: BoardManPreferenceUI.secondaryText).positioned(x: 20, y: 78, w: 130, h: 20))
validationLabel.frame = NSRect(x: 150, y: 78, width: 220, height: 20)
activation.addSubview(validationLabel)
let empty = BoardManPreferenceUI.label("No license has been activated on this Mac.", size: 13, color: BoardManPreferenceUI.secondaryText)
let empty = BoardManPreferenceUI.label("No license has been activated. Activation is not connected in this build.", size: 13, color: BoardManPreferenceUI.secondaryText)
empty.alignment = .center
empty.frame = NSRect(x: 20, y: 18, width: 365, height: 44)
BoardManPreferenceUI.prepare(empty, color: BoardManPreferenceUI.field, radius: BoardManPreferenceUI.Radius.control, border: BoardManPreferenceUI.borderSubtle)
Expand All @@ -69,10 +79,10 @@ final class CPYBetaPreferenceViewController: NSViewController {

let device = card(x: 820, y: 320, w: 330, h: 250, title: "3. Device Binding", icon: "laptopcomputer")
device.addSubview(row("Device name:", Host.current().localizedName ?? "This Mac", y: 165))
device.addSubview(row("Masked Device ID:", maskedDeviceID(), y: 132))
device.addSubview(row("Masked Device ID:", "Not activated", y: 132))
device.addSubview(row("Activated at:", "—", y: 99))
device.addSubview(BoardManPreferenceUI.label("Each Pro license can be activated on one Mac at a time.", size: 13, color: BoardManPreferenceUI.secondaryText).positioned(x: 20, y: 58, w: 285, h: 38))
let deactivate = BoardManPreferenceUI.secondaryButton("🔒 Deactivate this device")
device.addSubview(BoardManPreferenceUI.label("1 license = 1 PC. Device binding is not connected in this build.", size: 13, color: BoardManPreferenceUI.secondaryText).positioned(x: 20, y: 58, w: 285, h: 38))
let deactivate = BoardManPreferenceUI.secondaryButton("Deactivate this device")
deactivate.isEnabled = false
deactivate.frame = NSRect(x: 20, y: 20, width: 290, height: 30)
device.addSubview(deactivate)
Expand All @@ -91,7 +101,7 @@ final class CPYBetaPreferenceViewController: NSViewController {
cta.layer?.borderColor = BoardManPreferenceUI.red.withAlphaComponent(0.9).cgColor
cta.addSubview(BoardManPreferenceUI.icon("diamond.fill", size: 34).positioned(x: 143, y: 190, w: 44, h: 36))
cta.addSubview(BoardManPreferenceUI.label("Unlock the full power of Board-Man", size: 17, weight: .bold).positioned(x: 45, y: 150, w: 250, h: 26))
cta.addSubview(BoardManPreferenceUI.label("1 license = 1 PC\n✓ Lifetime access for one Mac", size: 14, color: BoardManPreferenceUI.primaryText).positioned(x: 60, y: 95, w: 220, h: 46))
cta.addSubview(BoardManPreferenceUI.label("1 license = 1 PC\nPro unlocks advanced controls", size: 14, color: BoardManPreferenceUI.primaryText).positioned(x: 60, y: 95, w: 220, h: 46))
let buy = BoardManPreferenceUI.primaryButton("Buy Board-Man Pro ›")
buy.target = self
buy.action = #selector(openBuyPro)
Expand All @@ -111,8 +121,13 @@ final class CPYBetaPreferenceViewController: NSViewController {
planLabel.stringValue = active ? "Board-Man Pro" : "Free Plan"
statusPill.stringValue = active ? "Active" : "Free"
statusPill.layer?.backgroundColor = (active ? BoardManPreferenceUI.redSoft : BoardManPreferenceUI.card).cgColor
currentPlanValue.stringValue = active ? "Board-Man Pro" : "Free Plan"
statusValue.stringValue = active ? "Active" : "Free"
lastVerifiedValue.stringValue = active ? "Verified" : "Not verified / Offline"
lastVerifiedValue.textColor = active ? .systemGreen : BoardManPreferenceUI.red
deviceStatus.stringValue = active ? "Activated" : "Not activated"
validationLabel.stringValue = active ? "Activated" : "Awaiting activation"
deviceStatus.textColor = active ? .systemGreen : BoardManPreferenceUI.secondaryText
validationLabel.stringValue = active ? "Activated" : "Not connected yet"
validationLabel.textColor = active ? .systemGreen : BoardManPreferenceUI.secondaryText
}

Expand All @@ -133,11 +148,12 @@ final class CPYBetaPreferenceViewController: NSViewController {
return row
}

private func infoRow(_ symbol: String, _ left: String, _ right: String, accent: Bool = false, y: CGFloat) -> NSView {
private func statusRow(_ symbol: String, _ left: String, _ value: NSTextField, y: CGFloat) -> NSView {
let row = NSView(frame: NSRect(x: 20, y: y, width: 310, height: 20))
row.addSubview(BoardManPreferenceUI.icon(symbol, size: 13, color: BoardManPreferenceUI.secondaryText).positioned(x: 0, y: 0, w: 18, h: 18))
row.addSubview(BoardManPreferenceUI.label(left, size: 12, color: BoardManPreferenceUI.secondaryText).positioned(x: 28, y: 0, w: 120, h: 20))
row.addSubview(BoardManPreferenceUI.label(right, size: 12, weight: .semibold, color: accent ? BoardManPreferenceUI.red : BoardManPreferenceUI.secondaryText).positioned(x: 155, y: 0, w: 140, h: 20))
row.addSubview(BoardManPreferenceUI.label(left, size: 12, color: BoardManPreferenceUI.secondaryText).positioned(x: 28, y: 0, w: 115, h: 20))
value.frame = NSRect(x: 145, y: 0, width: 160, height: 20)
row.addSubview(value)
return row
}

Expand All @@ -156,23 +172,14 @@ final class CPYBetaPreferenceViewController: NSViewController {
view.addSubview(BoardManPreferenceUI.label("Free is useful. Pro removes daily limits and unlocks advanced customization.", size: 13, color: BoardManPreferenceUI.secondaryText).positioned(x: 30, y: 562, w: 520, h: 18))
}

private func maskedDeviceID() -> String {
let id = LocalDeviceIdentityService.shared.deviceID().replacingOccurrences(of: "-", with: "")
guard id.count > 8 else { return "••••" }
return "BMN-••••-" + String(id.suffix(4)).uppercased()
}

@objc private func pasteLicense() {
if let copied = NSPasteboard.general.string(forType: .string) {
licenseField.stringValue = copied.trimmingCharacters(in: .whitespacesAndNewlines)
}
validationLabel.stringValue = "Activation is not connected yet."
validationLabel.textColor = BoardManPreferenceUI.secondaryText
}

@objc private func activateLicense() {
let response = StubLicenseActivationClient().activate(LicenseActivationRequest(licenseKey: licenseField.stringValue, localDeviceID: LocalDeviceIdentityService.shared.deviceID()))
validationLabel.stringValue = response.message
validationLabel.textColor = response.status == .activated ? .systemGreen : BoardManPreferenceUI.red
refreshPlanState()
validationLabel.stringValue = "Activation is not connected yet."
validationLabel.textColor = BoardManPreferenceUI.secondaryText
}

@objc private func openBuyPro() {
Expand Down
Loading