diff --git a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift index f00e426..c76669e 100644 --- a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift +++ b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift @@ -128,10 +128,14 @@ final class RemoteStore: ObservableObject { let cal = Calendar.current let today = cal.startOfDay(for: Date()) let activity = (0..<40).map { i -> ActivityDay in - ActivityDay(day: cal.date(byAdding: .day, value: -i, to: today)!, count: (i * 7) % 6) + let count = (i * 7) % 6 + // Sample tokens roughly track messages so the preview grid shades by usage. + ActivityDay(day: cal.date(byAdding: .day, value: -i, to: today)!, + count: count, tokens: count * 8_500 + (i * 137) % 4_000) } dashboard = DashboardSnapshot( - counts: DashboardCounts(projects: 2, chats: 5, activeChats: 2, messages: 142, activeDays: 9), + counts: DashboardCounts( + projects: 2, chats: 5, activeChats: 2, messages: 142, activeDays: 9, tokens: 1_284_000), activity: activity, projects: [ WireProject(id: p1, name: "nucleic", defaultBranch: "main", sessionCount: 3, activeCount: 2), diff --git a/NucleicRemote/NucleicRemote/Views/HomeView.swift b/NucleicRemote/NucleicRemote/Views/HomeView.swift index 696e20b..5f06fd7 100644 --- a/NucleicRemote/NucleicRemote/Views/HomeView.swift +++ b/NucleicRemote/NucleicRemote/Views/HomeView.swift @@ -18,6 +18,12 @@ struct HomeView: View { Dictionary(store.dashboard.activity.map { (Calendar.current.startOfDay(for: $0.day), $0.count) }, uniquingKeysWith: +) } + /// Tokens used per day — the activity grid's primary intensity signal (falls back to message + /// count for days without recorded usage). Mirrors the Mac home. + private var tokensByDay: [Date: Int] { + Dictionary(store.dashboard.activity.map { (Calendar.current.startOfDay(for: $0.day), $0.tokens) }, + uniquingKeysWith: +) + } /// Current activity streak with streak freezes folded in — the run of consecutive active /// days, where a banked freeze (one earned per five days) bridges a missed day. Shared with @@ -55,7 +61,9 @@ struct HomeView: View { VStack(alignment: .leading, spacing: 10) { Text("Activity").font(.headline) - ActivityGrid(activityByDay: activityByDay, frozenDays: streak.frozenDays) + ActivityGrid( + activityByDay: activityByDay, tokensByDay: tokensByDay, + frozenDays: streak.frozenDays) } .frame(maxWidth: .infinity, alignment: .leading) .card() @@ -88,6 +96,7 @@ struct HomeView: View { StatCard(value: running.count, label: "In progress", icon: "circle.lefthalf.filled") StatCard(value: counts.activeChats, label: "Active", icon: "bolt") StatCard(value: counts.messages, label: "Messages", icon: "paperplane") + StatCard(value: TokenCount.abbreviated(counts.tokens), label: "Tokens", icon: "number") } } } @@ -119,13 +128,26 @@ private struct InProgressSessions: View { } private struct StatCard: View { - let value: Int + let value: String let label: String let icon: String + + init(value: Int, label: String, icon: String) { + self.value = "\(value)" + self.label = label + self.icon = icon + } + + init(value: String, label: String, icon: String) { + self.value = value + self.label = label + self.icon = icon + } + var body: some View { VStack(alignment: .leading, spacing: 4) { Image(systemName: icon).foregroundStyle(Palette.accent) - Text("\(value)").font(.title2.weight(.semibold).monospacedDigit()) + Text(value).font(.title2.weight(.semibold).monospacedDigit()) Text(label).font(.caption).foregroundStyle(.secondary) } .frame(maxWidth: .infinity, alignment: .leading) @@ -172,6 +194,9 @@ struct StreakBadge: View { /// GitHub-style activity grid: columns = weeks (old→new), rows = Sun…Sat. struct ActivityGrid: View { let activityByDay: [Date: Int] + /// Tokens used per day — the primary intensity signal; days absent here fall back to their + /// message count (mirrors the Mac grid via `ActivityShading`). + var tokensByDay: [Date: Int] = [:] /// Missed days a streak freeze kept alive — drawn in the frozen blue rather than the /// empty-day gray (mirrors the Mac grid). var frozenDays: Set = [] @@ -193,8 +218,9 @@ struct ActivityGrid: View { let startOfWeek = cal.date(byAdding: .day, value: -(weekday - 1), to: today)! let gridStart = cal.date(byAdding: .day, value: -7 * (weeks - 1), to: startOfWeek)! // Brightness relative to *this user's* activity, outliers excluded so a few marathon - // days don't crush the everyday range. Computed once for the whole grid (mirrors Mac). - let scale = ActivityScale(activityByDay: activityByDay) + // days don't crush the everyday range. Shades by tokens-per-day, falling back to message + // count for days without recorded usage. Computed once for the whole grid (mirrors Mac). + let shading = ActivityShading(tokensByDay: tokensByDay, messagesByDay: activityByDay) return HStack(alignment: .top, spacing: gap) { ForEach(0..