mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-01 12:03:52 +00:00
Summary: - Replace the legacy iOS shell with Pro Command, Chat, Agents, and Settings tabs. - Wire iOS chat/session/settings/diagnostics and realtime Talk flows through gateway-backed APIs. - Add gateway/session and shared chat coverage for the new iOS flow. Verification: - git diff --check - node scripts/run-vitest.mjs src/gateway/server.sessions.create.test.ts src/gateway/talk-realtime-relay.test.ts - swift test --filter ChatViewModelTests (apps/shared/OpenClawKit) - xcodebuild build for Nimrod's iPhone succeeded; install succeeded; launch was blocked because the phone was locked Known follow-up: - Preserve traceLevel in sessions.create parent runtime inheritance and keep the changelog credit in the follow-up patch.
82 lines
3.1 KiB
Swift
82 lines
3.1 KiB
Swift
import OpenClawKit
|
|
import OpenClawProtocol
|
|
import SwiftUI
|
|
|
|
extension AgentProTab {
|
|
var usageTotalsCard: some View {
|
|
ProCard(radius: AgentLayout.cardRadius) {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
HStack {
|
|
Text("Totals")
|
|
.font(.headline)
|
|
Spacer()
|
|
ProValuePill(value: "\(self.overview?.usage?.days ?? 31)d", color: OpenClawBrand.accent)
|
|
}
|
|
HStack(spacing: 10) {
|
|
self.detailMetric(label: "Cost", value: self.usageValue)
|
|
self.detailMetric(label: "Tokens", value: self.usageTokenValue)
|
|
self.detailMetric(label: "Cache", value: self.usageCacheValue)
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, OpenClawProMetric.pagePadding)
|
|
}
|
|
|
|
var usageTokenValue: String {
|
|
guard let tokens = self.overview?.usage?.totalTokens else { return "0" }
|
|
return Self.compactNumber(tokens)
|
|
}
|
|
|
|
var usageCacheValue: String {
|
|
guard let cacheStatus = self.normalized(self.overview?.usage?.cacheStatus?["status"]?.value as? String) else {
|
|
return "n/a"
|
|
}
|
|
return cacheStatus
|
|
}
|
|
|
|
var usageDailyList: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
ProSectionHeader(title: "Daily")
|
|
ProCard(padding: 0, radius: AgentLayout.cardRadius) {
|
|
let days = self.overview?.usage?.daily ?? []
|
|
if days.isEmpty {
|
|
self.emptyDetailRow(
|
|
icon: "chart.bar",
|
|
title: "No daily usage yet",
|
|
detail: "The gateway returned totals without daily session cost rows.")
|
|
.padding(14)
|
|
} else {
|
|
VStack(spacing: 0) {
|
|
ForEach(Array(days.prefix(14).enumerated()), id: \.element.date) { index, day in
|
|
self.usageDayRow(day)
|
|
if index < min(days.count, 14) - 1 {
|
|
Divider().padding(.leading, 60)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, OpenClawProMetric.pagePadding)
|
|
}
|
|
}
|
|
|
|
func usageDayRow(_ day: CostUsageDailyEntryLite) -> some View {
|
|
HStack(spacing: 12) {
|
|
ProIconBadge(systemName: "calendar", color: OpenClawBrand.accent)
|
|
VStack(alignment: .leading, spacing: 3) {
|
|
Text(day.date)
|
|
.font(.subheadline.weight(.semibold))
|
|
Text("\(Self.compactNumber(day.totalTokens ?? 0)) tokens")
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
Spacer(minLength: 8)
|
|
Text(Self.currency(day.totalCost ?? 0))
|
|
.font(.caption2.weight(.semibold))
|
|
.foregroundStyle(OpenClawBrand.accent)
|
|
}
|
|
.padding(.vertical, 10)
|
|
.padding(.horizontal, 14)
|
|
}
|
|
}
|