Files
oai-swift/oAI/Views/Screens/CreditsView.swift
T
runeandClaude Sonnet 5 377e783a17 Mark Apple Intelligence provider as Beta throughout the UI
Apple's Foundation Models framework is still under active development
(built against macOS 27 beta) and likely to stay rough for a while —
small 4K context window, occasional generation errors, no tool
support yet. Surface that clearly wherever it appears:
- Model name: "Apple On-Device (Beta)" (shows in header/model picker)
- Model description: notes the beta status and what to expect
- Settings -> General: "⚠️ Beta — ..." disclaimer under the Apple
  Intelligence section, same style as the existing Paperless-NGX beta
  note
- Credits panel: same disclaimer for the Apple Intelligence entry

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 10:21:41 +02:00

191 lines
6.3 KiB
Swift

//
// CreditsView.swift
// oAI
//
// Account credits and balance
//
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
// Copyright (C) 2026 Rune Olsen
//
// This file is part of oAI.
//
// oAI is licensed under the PolyForm Noncommercial License 1.0.0.
// You may use, study, modify, and share it for any noncommercial
// purpose. Commercial use — including selling oAI or any part of
// it, standalone or bundled into another product or service —
// requires a separate commercial license from the copyright holder.
//
// See the LICENSE file or
// <https://polyformproject.org/licenses/noncommercial/1.0.0> for
// the full license text. For commercial licensing, contact Rune
// Olsen via <https://oai.pm>.
import SwiftUI
struct CreditsView: View {
let provider: Settings.Provider
@Environment(\.dismiss) var dismiss
@State private var credits: Credits?
@State private var isLoading = false
@State private var errorMessage: String?
var body: some View {
NavigationStack {
VStack(spacing: 24) {
// Provider icon
Image(systemName: provider.iconName)
.font(.system(size: 60))
.foregroundColor(Color.providerColor(provider))
Text(provider.displayName)
.font(.title2)
.fontWeight(.semibold)
Divider()
// Credits info based on provider
VStack(spacing: 16) {
switch provider {
case .openrouter:
openRouterCreditsView
case .anthropic:
Text("Anthropic Balance")
.font(.headline)
Text("Check your balance at:")
.font(.caption)
.foregroundColor(.secondary)
Link("console.anthropic.com", destination: URL(string: "https://console.anthropic.com")!)
.font(.body)
case .openai:
Text("OpenAI Balance")
.font(.headline)
Text("Check your usage at:")
.font(.caption)
.foregroundColor(.secondary)
Link("platform.openai.com", destination: URL(string: "https://platform.openai.com/usage")!)
.font(.body)
case .ollama:
Text("Ollama (Local)")
.font(.headline)
Text("Running locally — no credits needed!")
.font(.body)
.foregroundColor(.secondary)
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 40))
.foregroundColor(.green)
.padding(.top)
case .appleOnDevice:
Text("Apple Intelligence")
.font(.headline)
Text("On-device and free — no credits or API key needed.")
.font(.body)
.foregroundColor(.secondary)
Text("⚠️ Beta — Apple's Foundation Models framework is still in active development.")
.font(.caption)
.foregroundColor(.secondary)
Image(systemName: "apple.logo")
.font(.system(size: 40))
.foregroundColor(.secondary)
.padding(.top)
}
}
Spacer()
}
.padding()
.navigationTitle("Credits")
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Done") { dismiss() }
}
}
}
.task {
await fetchCredits()
}
}
// MARK: - OpenRouter Credits
@ViewBuilder
private var openRouterCreditsView: some View {
Text("OpenRouter Credits")
.font(.headline)
if isLoading {
ProgressView("Loading...")
.padding()
} else if let error = errorMessage {
Text(error)
.font(.caption)
.foregroundColor(.red)
.padding()
Button("Retry") {
Task { await fetchCredits() }
}
} else if let credits = credits {
VStack(spacing: 12) {
CreditRow(label: "Remaining", value: credits.balanceDisplay, highlight: true)
Divider()
if let limit = credits.limit {
CreditRow(label: "Total Credits", value: String(format: "$%.2f", limit))
}
if let usage = credits.usage {
CreditRow(label: "Used", value: String(format: "$%.2f", usage))
}
}
} else {
Text("No credit data available")
.font(.caption)
.foregroundColor(.secondary)
}
}
private func fetchCredits() async {
guard provider == .openrouter else { return }
guard let apiProvider = ProviderRegistry.shared.getCurrentProvider() else {
errorMessage = "No API key configured"
return
}
isLoading = true
errorMessage = nil
do {
credits = try await apiProvider.getCredits()
isLoading = false
} catch {
errorMessage = error.localizedDescription
isLoading = false
}
}
}
struct CreditRow: View {
let label: LocalizedStringKey
let value: String
var highlight: Bool = false
var body: some View {
HStack {
Text(label)
.foregroundColor(highlight ? .primary : .secondary)
.fontWeight(highlight ? .semibold : .regular)
Spacer()
Text(value)
.font(highlight ? .title2.monospacedDigit() : .body.monospacedDigit())
.fontWeight(highlight ? .bold : .medium)
.foregroundColor(highlight ? .green : .primary)
}
}
}
#Preview {
CreditsView(provider: .openrouter)
}