BigMarc a7bf45b466 Add management window; sales-focused README; FounderGem LLC
- Management app: sidebar window with Dashboard (usage insights), Devices
  (slot usage, deactivate this Mac), Writing Modes (custom + app-aware map),
  Dictionary, Snippets, Shortcuts, AI Model, Privacy, General, License, About.
- New local stores: Preferences (launch-at-login, appearance), UsageStats
  (privacy-friendly local insights), CustomModesStore, SnippetStore.
- License model now tracks device name + activation usage/limit for the
  Devices screen.
- README rewritten as a conversion-focused product page (no internal cost/setup).
- Legal entity set to FounderGem LLC across README, LICENSE, Info.plist, About.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:28:23 +02:00

92 lines
4.0 KiB
Swift

import SwiftUI
struct DevicesView: View {
@EnvironmentObject private var app: AppState
@EnvironmentObject private var license: LicenseManager
var body: some View {
DetailScaffold(
title: "Devices",
subtitle: "Manage the Macs your license is active on."
) {
if let record = license.record {
usageSummary(record)
thisDevice(record)
otherDevices(record)
} else {
Card {
Label("No active license on this Mac", systemImage: "key.slash")
.font(.headline)
Text("Activate a license to manage devices. The free plan runs on one Mac.")
.foregroundStyle(.secondary)
Button("Enter a license key") { app.showMainWindow(select: .license) }
.buttonStyle(.borderedProminent)
}
}
}
}
private func usageSummary(_ r: LicenseRecord) -> some View {
Card {
HStack(alignment: .firstTextBaseline) {
Text("\(max(r.activationUsage, 1)) of \(r.activationLimit)")
.font(.system(.title, design: .rounded).weight(.semibold))
Text("devices in use").foregroundStyle(.secondary)
Spacer()
Text(license.tier.displayName).font(.subheadline).foregroundStyle(GG.emerald)
}
ProgressView(value: Double(max(r.activationUsage, 1)), total: Double(max(r.activationLimit, 1)))
.tint(GG.emerald)
Text("Your \(license.tier.displayName) plan covers up to \(r.activationLimit) Mac\(r.activationLimit == 1 ? "" : "s").")
.font(.caption).foregroundStyle(.secondary)
}
}
private func thisDevice(_ r: LicenseRecord) -> some View {
Card {
HStack(spacing: 12) {
Image(systemName: "laptopcomputer")
.font(.title2).foregroundStyle(GG.emerald)
.frame(width: 34)
VStack(alignment: .leading, spacing: 2) {
HStack(spacing: 8) {
Text(r.deviceName).font(.headline)
Text("This Mac")
.font(.caption2.weight(.semibold))
.padding(.horizontal, 7).padding(.vertical, 2)
.background(GG.emerald.opacity(0.15), in: Capsule())
.foregroundStyle(GG.emerald)
}
Text("Activated \(r.activatedAt.formatted(date: .abbreviated, time: .omitted)) · validated \(r.lastValidated.formatted(.relative(presentation: .named)))")
.font(.caption).foregroundStyle(.secondary)
}
Spacer()
Button(role: .destructive) {
Task { await app.license.deactivateThisDevice() }
} label: {
if license.isWorking { ProgressView().controlSize(.small) }
else { Text("Deactivate") }
}
.disabled(license.isWorking)
}
if let err = license.lastError {
Label(err, systemImage: "exclamationmark.triangle").font(.caption).foregroundStyle(.orange)
}
}
}
private func otherDevices(_ r: LicenseRecord) -> some View {
Card {
Text("Other devices").font(.headline)
Text("Deactivating a Mac here frees a slot instantly. To rename or remove a Mac you no longer have access to, open the license portal.")
.font(.callout).foregroundStyle(.secondary)
HStack {
Link("Open license portal", destination: AppConfig.modelPortalURL)
Spacer()
Text("Add a Mac: install GrammaGem there and paste the same key.")
.font(.caption).foregroundStyle(.secondary)
}
}
}
}