@sriinnu/runic 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +1 -1
  3. package/README.md +47 -29
  4. package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
  5. package/RuniciOS/Views/ProviderDetailView.swift +9 -9
  6. package/RuniciOS/Views/ProviderListView.swift +10 -10
  7. package/RuniciOS/Widgets/LargeWidget.swift +14 -14
  8. package/RuniciOS/Widgets/LiveActivity.swift +19 -19
  9. package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
  10. package/RuniciOS/Widgets/MediumWidget.swift +10 -10
  11. package/RuniciOS/Widgets/RunicWidget.swift +5 -5
  12. package/RuniciOS/Widgets/SmallWidget.swift +9 -9
  13. package/Scripts/package_app.sh +2 -2
  14. package/Sources/Runic/About.swift +3 -3
  15. package/Sources/Runic/AppNotifications.swift +5 -3
  16. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +17 -4
  17. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
  18. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
  19. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
  20. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +47 -14
  21. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
  22. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
  23. package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
  24. package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
  25. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
  26. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
  27. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
  28. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +3 -6
  29. package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
  30. package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
  31. package/Sources/Runic/Core/RunicColors.swift +6 -6
  32. package/Sources/Runic/Core/RunicSpacing.swift +7 -7
  33. package/Sources/Runic/Core/RunicTypography.swift +206 -0
  34. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +10 -36
  35. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +10 -36
  36. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +10 -36
  37. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +10 -36
  38. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +10 -36
  39. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
  40. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +10 -36
  41. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +10 -36
  42. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +10 -36
  43. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +10 -36
  44. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +10 -36
  45. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +10 -36
  46. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +10 -36
  47. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +10 -36
  48. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +10 -36
  49. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +10 -36
  50. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
  51. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +10 -36
  52. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +10 -36
  53. package/Sources/Runic/Core/Stores/SettingsStore.swift +99 -52
  54. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +10 -36
  55. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
  56. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
  57. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
  58. package/Sources/Runic/Core/Stores/UsageStore.swift +234 -167
  59. package/Sources/Runic/Core/Stores/XAITokenStore.swift +10 -36
  60. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +10 -36
  61. package/Sources/Runic/Core/UsageExporter.swift +8 -4
  62. package/Sources/Runic/ProviderRegistry.swift +1 -1
  63. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
  64. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
  65. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
  66. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
  67. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
  68. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
  69. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
  70. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
  71. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
  72. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
  73. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
  74. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
  75. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
  76. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
  77. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
  78. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
  79. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
  80. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
  81. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
  82. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
  83. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
  84. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
  85. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
  86. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
  87. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
  88. package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
  89. package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
  90. package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
  91. package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
  92. package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
  93. package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
  94. package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
  95. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
  96. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
  97. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
  98. package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
  99. package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
  100. package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
  101. package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
  102. package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
  103. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
  104. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
  105. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
  106. package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
  107. package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
  108. package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
  109. package/Sources/Runic/Resources/provider-context-windows.json +20 -0
  110. package/Sources/Runic/RunicApp.swift +46 -36
  111. package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
  112. package/Sources/Runic/UsagePaceText.swift +1 -1
  113. package/Sources/Runic/UsageProgressBar.swift +13 -9
  114. package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
  115. package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
  116. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
  117. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
  118. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
  119. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
  120. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
  121. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
  122. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
  123. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +15 -20
  124. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
  125. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
  126. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
  127. package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
  128. package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
  129. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
  130. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
  131. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
  132. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +198 -58
  133. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
  134. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
  135. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
  136. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
  137. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
  138. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
  139. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +28 -76
  140. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
  141. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
  142. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
  143. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
  144. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
  145. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
  146. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
  147. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
  148. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
  149. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
  150. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
  151. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
  152. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
  153. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
  154. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +108 -33
  155. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
  156. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
  157. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
  158. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
  159. package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
  160. package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
  161. package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
  162. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
  163. package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
  164. package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
  165. package/Sources/RunicCLI/CLIEntry.swift +83 -42
  166. package/Sources/RunicCLI/CLIRenderer.swift +1 -1
  167. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
  168. package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
  169. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
  170. package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
  171. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
  172. package/Sources/RunicClaudeWebProbe/main.swift +1 -1
  173. package/Sources/RunicCore/AlertRuleStore.swift +20 -20
  174. package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
  175. package/Sources/RunicCore/CostUsageModels.swift +1 -1
  176. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
  177. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +80 -71
  178. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
  179. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
  180. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
  181. package/Sources/RunicCore/Logging/OSLogLogHandler.swift +6 -6
  182. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
  183. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
  184. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
  185. package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
  186. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
  187. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
  188. package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
  189. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
  190. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
  191. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
  192. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
  193. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
  194. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +5 -3
  195. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
  196. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
  197. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
  198. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
  199. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
  200. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
  201. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
  202. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
  203. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
  204. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +1 -0
  205. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +12 -1
  206. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
  207. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
  208. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
  209. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
  210. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
  211. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
  212. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
  213. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
  214. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
  215. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
  216. package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +1 -0
  217. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
  218. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
  219. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
  220. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
  221. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +3 -2
  222. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
  223. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
  224. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
  225. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +6 -2
  226. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
  227. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
  228. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
  229. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
  230. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
  231. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +8 -11
  232. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
  233. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
  234. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
  235. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
  236. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +25 -16
  237. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
  238. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
  239. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
  240. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
  241. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
  242. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +27 -16
  243. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
  244. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
  245. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
  246. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
  247. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
  248. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
  249. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
  250. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
  251. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
  252. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
  253. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +51 -30
  254. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
  255. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
  256. package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
  257. package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
  258. package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
  259. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
  260. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
  261. package/Sources/RunicCore/Team/TeamModels.swift +111 -110
  262. package/Sources/RunicCore/Team/TeamStore.swift +126 -109
  263. package/Sources/RunicCore/UsageFetcher.swift +2 -2
  264. package/Sources/RunicCore/UsageFormatter.swift +25 -25
  265. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +141 -29
  266. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +59 -5
  267. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +161 -0
  268. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
  269. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
  270. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
  271. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
  272. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
  273. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
  274. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
  275. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
  276. package/Tests/RunicTests/AppDelegateTests.swift +8 -3
  277. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
  278. package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
  279. package/Tests/RunicTests/CLICostTests.swift +6 -7
  280. package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
  281. package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
  282. package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
  283. package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
  284. package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
  285. package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
  286. package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
  287. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
  288. package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
  289. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
  290. package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
  291. package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
  292. package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
  293. package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
  294. package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
  295. package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
  296. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
  297. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
  298. package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
  299. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
  300. package/Tests/RunicTests/InstallOriginTests.swift +1 -2
  301. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
  302. package/Tests/RunicTests/LiveAccountTests.swift +3 -3
  303. package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
  304. package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
  305. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
  306. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
  307. package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
  308. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
  309. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
  310. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
  311. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
  312. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
  313. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
  314. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
  315. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
  316. package/Tests/RunicTests/PathBuilderTests.swift +14 -15
  317. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
  318. package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
  319. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
  320. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
  321. package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
  322. package/Tests/RunicTests/RunicTests.swift +6 -7
  323. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
  324. package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
  325. package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
  326. package/Tests/RunicTests/StatusMenuTests.swift +63 -22
  327. package/Tests/RunicTests/StatusProbeTests.swift +28 -29
  328. package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
  329. package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
  330. package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
  331. package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
  332. package/Tests/RunicTests/TextParsingTests.swift +1 -2
  333. package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
  334. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
  335. package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
  336. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
  337. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
  338. package/Tests/RunicTests/UsagePaceTests.swift +5 -6
  339. package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
  340. package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
  341. package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
  342. package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
  343. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
  344. package/appcast.xml +36 -37
  345. package/package.json +1 -1
  346. package/runic.png +0 -0
  347. package/runic.svg +15 -0
  348. package/version.env +2 -2
@@ -11,10 +11,10 @@ struct EfficiencyMetricsMenuView: View {
11
11
 
12
12
  var label: String {
13
13
  switch self {
14
- case .model: return "Model"
15
- case .tokensPerRequest: return "Tokens/Req"
16
- case .costPerRequest: return "Cost/Req"
17
- case .cacheHitRate: return "Cache Hit %"
14
+ case .model: "Model"
15
+ case .tokensPerRequest: "Tokens/Req"
16
+ case .costPerRequest: "Cost/Req"
17
+ case .cacheHitRate: "Cache Hit %"
18
18
  }
19
19
  }
20
20
  }
@@ -65,12 +65,12 @@ struct EfficiencyMetricsMenuView: View {
65
65
 
66
66
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
67
67
  Text("Efficiency Metrics")
68
- .font(.headline)
68
+ .font(RunicFont.headline)
69
69
  .fontWeight(.semibold)
70
70
 
71
71
  if model.isEmpty {
72
72
  Text("No efficiency metrics available.")
73
- .font(.footnote)
73
+ .font(RunicFont.footnote)
74
74
  .foregroundStyle(.secondary)
75
75
  } else {
76
76
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
@@ -134,27 +134,27 @@ struct EfficiencyMetricsMenuView: View {
134
134
  HStack(spacing: RunicSpacing.lg) {
135
135
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
136
136
  Text("Total Requests")
137
- .font(.caption2)
137
+ .font(RunicFont.caption2)
138
138
  .foregroundStyle(.secondary)
139
139
  Text("\(Self.totalRequests(model))")
140
- .font(.caption)
140
+ .font(RunicFont.caption)
141
141
  .fontWeight(.medium)
142
142
  }
143
143
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
144
144
  Text("Avg Tokens/Req")
145
- .font(.caption2)
145
+ .font(RunicFont.caption2)
146
146
  .foregroundStyle(.secondary)
147
147
  Text(UsageFormatter.tokenCountString(Self.averageTokensPerRequest(model)))
148
- .font(.caption)
148
+ .font(RunicFont.caption)
149
149
  .fontWeight(.medium)
150
150
  }
151
151
  if Self.hasAnyCost(model) {
152
152
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
153
153
  Text("Avg Cost/Req")
154
- .font(.caption2)
154
+ .font(RunicFont.caption2)
155
155
  .foregroundStyle(.secondary)
156
156
  Text(UsageFormatter.usdString(Self.averageCostPerRequest(model)))
157
- .font(.caption)
157
+ .font(RunicFont.caption)
158
158
  .fontWeight(.medium)
159
159
  }
160
160
  }
@@ -181,7 +181,7 @@ struct EfficiencyMetricsMenuView: View {
181
181
  ascending: Bool) -> [ModelMetrics]
182
182
  {
183
183
  let metrics = summaries.map { ModelMetrics(summary: $0) }
184
- let sorted = metrics.sorted { lhs, rhs in
184
+ return metrics.sorted { lhs, rhs in
185
185
  let result: Bool
186
186
  switch sortBy {
187
187
  case .model:
@@ -197,7 +197,6 @@ struct EfficiencyMetricsMenuView: View {
197
197
  }
198
198
  return ascending ? result : !result
199
199
  }
200
- return sorted
201
200
  }
202
201
 
203
202
  private static func totalRequests(_ metrics: [ModelMetrics]) -> Int {
@@ -237,12 +236,12 @@ private struct SortableColumnHeader: View {
237
236
  } label: {
238
237
  HStack(spacing: RunicSpacing.xxs) {
239
238
  Text(self.title)
240
- .font(.caption)
239
+ .font(RunicFont.caption)
241
240
  .foregroundStyle(.secondary)
242
241
  .fontWeight(.medium)
243
242
  if self.currentSort == self.column {
244
243
  Image(systemName: self.ascending ? "chevron.up" : "chevron.down")
245
- .font(.caption2)
244
+ .font(RunicFont.caption2)
246
245
  .foregroundStyle(.secondary)
247
246
  }
248
247
  }
@@ -259,34 +258,34 @@ private struct MetricsRow: View {
259
258
  var body: some View {
260
259
  HStack(spacing: 0) {
261
260
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
262
- Text(UsageFormatter.modelDisplayName(metrics.modelName))
263
- .font(.caption)
261
+ Text(UsageFormatter.modelDisplayName(self.metrics.modelName))
262
+ .font(RunicFont.caption)
264
263
  .lineLimit(1)
265
264
  .truncationMode(.tail)
266
265
  if let context = UsageFormatter.modelContextLabel(for: metrics.modelName) {
267
266
  Text(context)
268
- .font(.caption2)
267
+ .font(RunicFont.caption2)
269
268
  .foregroundStyle(.secondary)
270
269
  .lineLimit(1)
271
270
  .truncationMode(.tail)
272
271
  }
273
272
  }
274
273
  .frame(width: 140, alignment: .leading)
275
- Text(UsageFormatter.tokenCountString(Int(metrics.tokensPerRequest)))
276
- .font(.caption)
274
+ Text(UsageFormatter.tokenCountString(Int(self.metrics.tokensPerRequest)))
275
+ .font(RunicFont.caption)
277
276
  .frame(width: 90, alignment: .leading)
278
277
  if let cost = metrics.costPerRequest {
279
278
  Text(UsageFormatter.usdString(cost))
280
- .font(.caption)
279
+ .font(RunicFont.caption)
281
280
  .frame(width: 80, alignment: .leading)
282
281
  } else {
283
282
  Text("—")
284
- .font(.caption)
283
+ .font(RunicFont.caption)
285
284
  .foregroundStyle(.secondary)
286
285
  .frame(width: 80, alignment: .leading)
287
286
  }
288
- Text(String(format: "%.1f%%", metrics.cacheHitRate * 100))
289
- .font(.caption)
287
+ Text(String(format: "%.1f%%", self.metrics.cacheHitRate * 100))
288
+ .font(RunicFont.caption)
290
289
  .foregroundStyle(self.cacheHitColor)
291
290
  .frame(width: 80, alignment: .leading)
292
291
  }
@@ -301,12 +300,12 @@ private struct MetricsRow: View {
301
300
  }
302
301
 
303
302
  private var cacheHitColor: Color {
304
- if metrics.cacheHitRate > 0.5 {
305
- return Color(red: 0.46, green: 0.75, blue: 0.36)
306
- } else if metrics.cacheHitRate > 0.2 {
307
- return Color(red: 0.94, green: 0.74, blue: 0.26)
303
+ if self.metrics.cacheHitRate > 0.5 {
304
+ Color(red: 0.46, green: 0.75, blue: 0.36)
305
+ } else if self.metrics.cacheHitRate > 0.2 {
306
+ Color(red: 0.94, green: 0.74, blue: 0.26)
308
307
  } else {
309
- return .secondary
308
+ .secondary
310
309
  }
311
310
  }
312
311
  }
@@ -26,32 +26,32 @@ struct HeroTodayStatView: View {
26
26
  VStack(alignment: .leading, spacing: 0) {
27
27
  HStack(alignment: .firstTextBaseline, spacing: RunicSpacing.xxs) {
28
28
  Text(UsageFormatter.tokenCountString(self.tokenCount))
29
- .font(.system(size: 26, weight: .bold, design: .rounded))
29
+ .font(RunicFont.system(size: 26, weight: .bold))
30
30
  .foregroundStyle(MenuHighlightStyle.primary(self.isHighlighted))
31
31
  Text("today")
32
- .font(.system(.subheadline, design: .rounded))
32
+ .font(RunicFont.subheadline)
33
33
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
34
34
  }
35
35
  }
36
36
  Spacer()
37
37
  if let cost = self.costUSD, cost > 0 {
38
38
  Text(UsageFormatter.usdString(cost))
39
- .font(.system(.subheadline, design: .rounded))
39
+ .font(RunicFont.subheadline)
40
40
  .fontWeight(.medium)
41
41
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
42
42
  .padding(.horizontal, RunicSpacing.xs)
43
43
  .padding(.vertical, RunicSpacing.xxxs)
44
44
  .background(
45
45
  Capsule(style: .continuous)
46
- .fill(Color(nsColor: .controlBackgroundColor).opacity(RunicColors.Opacity.light))
47
- )
46
+ .fill(Color(nsColor: .controlBackgroundColor).opacity(RunicColors.Opacity.light)))
48
47
  }
49
48
  }
50
49
  .padding(.horizontal, MenuCardMetrics.horizontalPadding)
51
50
  .padding(.vertical, RunicSpacing.xxs)
52
51
  .frame(minWidth: self.width, maxWidth: .infinity, alignment: .leading)
53
52
  .accessibilityElement(children: .ignore)
54
- .accessibilityLabel("Today usage: \(UsageFormatter.tokenCountString(self.tokenCount)) tokens\(self.costUSD.map { ", \(UsageFormatter.usdString($0))" } ?? "")")
53
+ .accessibilityLabel(
54
+ "Today usage: \(UsageFormatter.tokenCountString(self.tokenCount)) tokens\(self.costUSD.map { ", \(UsageFormatter.usdString($0))" } ?? "")")
55
55
  }
56
56
  }
57
57
 
@@ -89,7 +89,8 @@ struct GlassmorphismStatCardsView: View {
89
89
  .padding(.vertical, RunicSpacing.xxs)
90
90
  .frame(minWidth: self.width, maxWidth: .infinity, alignment: .leading)
91
91
  .accessibilityElement(children: .ignore)
92
- .accessibilityLabel("Peak hour \(self.peakHourLabel), \(self.peakHourTokens). This week \(self.weekTotalTokens)")
92
+ .accessibilityLabel(
93
+ "Peak hour \(self.peakHourLabel), \(self.peakHourTokens). This week \(self.weekTotalTokens)")
93
94
  }
94
95
  }
95
96
 
@@ -104,18 +105,18 @@ private struct GlassStatCard: View {
104
105
  var body: some View {
105
106
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
106
107
  Text(self.title)
107
- .font(.system(.caption2, design: .rounded))
108
+ .font(RunicFont.caption2)
108
109
  .fontWeight(.medium)
109
110
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
110
111
  MiniSparklineView(data: self.sparkline, color: self.sparkColor)
111
112
  .frame(height: 24)
112
113
  Text(self.value)
113
- .font(.system(.title3, design: .rounded))
114
+ .font(RunicFont.title3)
114
115
  .fontWeight(.bold)
115
116
  .foregroundStyle(MenuHighlightStyle.primary(self.isHighlighted))
116
117
  if let detail {
117
118
  Text(detail)
118
- .font(.system(.caption2, design: .rounded))
119
+ .font(RunicFont.caption2)
119
120
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
120
121
  }
121
122
  }
@@ -123,12 +124,10 @@ private struct GlassStatCard: View {
123
124
  .frame(maxWidth: .infinity, alignment: .leading)
124
125
  .background(
125
126
  RoundedRectangle(cornerRadius: RunicCornerRadius.lg, style: .continuous)
126
- .fill(.ultraThinMaterial)
127
- )
127
+ .fill(.ultraThinMaterial))
128
128
  .overlay(
129
129
  RoundedRectangle(cornerRadius: RunicCornerRadius.lg, style: .continuous)
130
- .stroke(Color(nsColor: .separatorColor).opacity(RunicColors.Opacity.medium), lineWidth: 0.5)
131
- )
130
+ .stroke(Color(nsColor: .separatorColor).opacity(RunicColors.Opacity.medium), lineWidth: 0.5))
132
131
  .shadow(color: .black.opacity(0.08), radius: 4, y: 2)
133
132
  .glassShimmer()
134
133
  }
@@ -147,7 +146,9 @@ private struct MiniSparklineView: View {
147
146
  // Filled gradient area under the line
148
147
  Path { path in
149
148
  path.move(to: CGPoint(x: points[0].x, y: geo.size.height))
150
- for point in points { path.addLine(to: point) }
149
+ for point in points {
150
+ path.addLine(to: point)
151
+ }
151
152
  path.addLine(to: CGPoint(x: points.last!.x, y: geo.size.height))
152
153
  path.closeSubpath()
153
154
  }
@@ -159,7 +160,9 @@ private struct MiniSparklineView: View {
159
160
  // Line stroke
160
161
  Path { path in
161
162
  path.move(to: points[0])
162
- for point in points.dropFirst() { path.addLine(to: point) }
163
+ for point in points.dropFirst() {
164
+ path.addLine(to: point)
165
+ }
163
166
  }
164
167
  .stroke(self.color, style: StrokeStyle(lineWidth: 1.5, lineCap: .round, lineJoin: .round))
165
168
  } else if self.data.count == 1, maxVal > 0 {
@@ -199,7 +202,7 @@ struct UpdatedTimestampView: View {
199
202
  TimelineView(.periodic(from: .now, by: 5)) { context in
200
203
  let text = Self.relativeString(from: self.updatedAt, to: context.date)
201
204
  Text(text)
202
- .font(.system(.caption2, design: .rounded))
205
+ .font(RunicFont.caption2)
203
206
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted).opacity(0.7))
204
207
  }
205
208
  .padding(.horizontal, MenuCardMetrics.horizontalPadding)
@@ -38,19 +38,19 @@ struct HourlyActivityChartMenuView: View {
38
38
  // Header
39
39
  HStack(alignment: .firstTextBaseline) {
40
40
  Text("Today by Hour")
41
- .font(.system(.subheadline, design: .rounded))
41
+ .font(RunicFont.subheadline)
42
42
  .fontWeight(.semibold)
43
43
  Spacer()
44
44
  if let peak = model.peakHour {
45
45
  Text("Peak \(peak.label)")
46
- .font(.system(.caption, design: .rounded))
46
+ .font(RunicFont.caption)
47
47
  .foregroundStyle(.secondary)
48
48
  }
49
49
  }
50
50
 
51
51
  if model.bars.allSatisfy({ $0.totalTokens == 0 }) {
52
52
  Text("No usage recorded today.")
53
- .font(.system(.footnote, design: .rounded))
53
+ .font(RunicFont.footnote)
54
54
  .foregroundStyle(.secondary)
55
55
  .frame(height: 80)
56
56
  } else {
@@ -70,7 +70,7 @@ struct HourlyActivityChartMenuView: View {
70
70
  .foregroundStyle(Color(nsColor: .labelColor).opacity(0.3))
71
71
  .annotation(position: .top, spacing: 4) {
72
72
  Text(UsageFormatter.tokenCountString(bar.totalTokens))
73
- .font(.system(.caption2, design: .rounded))
73
+ .font(RunicFont.caption2)
74
74
  .fontWeight(.medium)
75
75
  .padding(.horizontal, 6)
76
76
  .padding(.vertical, 2)
@@ -85,7 +85,7 @@ struct HourlyActivityChartMenuView: View {
85
85
  AxisValueLabel {
86
86
  if let hour = value.as(Int.self) {
87
87
  Text(Self.hourAxisLabel(hour))
88
- .font(.system(.caption2, design: .rounded))
88
+ .font(RunicFont.caption2)
89
89
  .foregroundStyle(Color(nsColor: .tertiaryLabelColor))
90
90
  }
91
91
  }
@@ -98,7 +98,7 @@ struct HourlyActivityChartMenuView: View {
98
98
  AxisValueLabel {
99
99
  if let tokens = value.as(Int.self) {
100
100
  Text(UsageFormatter.tokenCountString(tokens))
101
- .font(.system(.caption2, design: .rounded))
101
+ .font(RunicFont.caption2)
102
102
  .foregroundStyle(Color(nsColor: .tertiaryLabelColor))
103
103
  }
104
104
  }
@@ -120,27 +120,27 @@ struct HourlyActivityChartMenuView: View {
120
120
  HStack(spacing: RunicSpacing.md) {
121
121
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
122
122
  Text("Today")
123
- .font(.system(.caption2, design: .rounded))
123
+ .font(RunicFont.caption2)
124
124
  .foregroundStyle(.tertiary)
125
125
  Text(UsageFormatter.tokenCountString(model.totalTokens))
126
- .font(.system(.caption, design: .rounded))
126
+ .font(RunicFont.caption)
127
127
  .fontWeight(.medium)
128
128
  }
129
129
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
130
130
  Text("Active hours")
131
- .font(.system(.caption2, design: .rounded))
131
+ .font(RunicFont.caption2)
132
132
  .foregroundStyle(.tertiary)
133
133
  Text("\(model.activeHours)")
134
- .font(.system(.caption, design: .rounded))
134
+ .font(RunicFont.caption)
135
135
  .fontWeight(.medium)
136
136
  }
137
137
  if model.totalRequests > 0 {
138
138
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
139
139
  Text("Requests")
140
- .font(.system(.caption2, design: .rounded))
140
+ .font(RunicFont.caption2)
141
141
  .foregroundStyle(.tertiary)
142
142
  Text("\(model.totalRequests)")
143
- .font(.system(.caption, design: .rounded))
143
+ .font(RunicFont.caption)
144
144
  .fontWeight(.medium)
145
145
  }
146
146
  }
@@ -182,12 +182,12 @@ struct HourlyActivityChartMenuView: View {
182
182
  let tokens = tokensByHour[hour] ?? 0
183
183
  let isPeak = tokens > 0 && tokens == maxTokens
184
184
  let bar = HourBar(id: hour, hour: hour, totalTokens: tokens, isPeak: isPeak)
185
- if isPeak && tokens > 0 { peakHour = bar }
185
+ if isPeak, tokens > 0 { peakHour = bar }
186
186
  return bar
187
187
  }
188
188
 
189
189
  let totalTokens = tokensByHour.values.reduce(0, +)
190
- let activeHours = tokensByHour.values.filter { $0 > 0 }.count
190
+ let activeHours = tokensByHour.values.count(where: { $0 > 0 })
191
191
  let totalRequests = requestsByHour.values.reduce(0, +)
192
192
 
193
193
  return HourlyModel(
@@ -7,27 +7,24 @@ import SwiftUI
7
7
  @MainActor
8
8
  struct InlineUsageChartView: View {
9
9
  enum TimeRange: String, CaseIterable {
10
- case oneHour = "1h"
11
- case sixHours = "6h"
12
- case oneDay = "1d"
10
+ case threeDays = "3d"
13
11
  case sevenDays = "7d"
14
12
  case thirtyDays = "30d"
13
+ case quarter = "90d"
14
+ case year = "1y"
15
15
 
16
16
  var cutoffInterval: TimeInterval {
17
17
  switch self {
18
- case .oneHour: return -3600
19
- case .sixHours: return -21600
20
- case .oneDay: return -86400
21
- case .sevenDays: return -604800
22
- case .thirtyDays: return -2592000
18
+ case .threeDays: -259_200
19
+ case .sevenDays: -604_800
20
+ case .thirtyDays: -2_592_000
21
+ case .quarter: -7_776_000
22
+ case .year: -31_536_000
23
23
  }
24
24
  }
25
25
 
26
26
  var usesHourlyData: Bool {
27
- switch self {
28
- case .oneHour, .sixHours, .oneDay: return true
29
- default: return false
30
- }
27
+ false
31
28
  }
32
29
  }
33
30
 
@@ -40,15 +37,13 @@ struct InlineUsageChartView: View {
40
37
  let dailySummaries: [UsageLedgerDailySummary]
41
38
  let hourlySummaries: [UsageLedgerHourlySummary]
42
39
  let width: CGFloat
43
- @State private var selectedRange: TimeRange = .oneDay
40
+ @State private var selectedRange: TimeRange = .sevenDays
44
41
  @Environment(\.menuItemHighlighted) private var isHighlighted
45
42
 
46
43
  private static let lineColor = RunicColors.chartColor(at: 0)
47
44
 
48
45
  var body: some View {
49
- let points = self.selectedRange.usesHourlyData
50
- ? Self.hourlyPoints(from: self.hourlySummaries, range: self.selectedRange)
51
- : Self.dailyPoints(from: self.dailySummaries, range: self.selectedRange)
46
+ let points = Self.dailyPoints(from: self.dailySummaries, range: self.selectedRange)
52
47
 
53
48
  VStack(alignment: .leading, spacing: RunicSpacing.xs) {
54
49
  // Time range picker
@@ -65,7 +60,7 @@ struct InlineUsageChartView: View {
65
60
  HStack {
66
61
  Spacer()
67
62
  Text("No data")
68
- .font(.system(.caption2, design: .rounded))
63
+ .font(RunicFont.caption2)
69
64
  .foregroundStyle(.tertiary)
70
65
  Spacer()
71
66
  }
@@ -101,7 +96,7 @@ struct InlineUsageChartView: View {
101
96
  AxisValueLabel {
102
97
  if let tokens = value.as(Int.self) {
103
98
  Text(UsageFormatter.tokenCountString(tokens))
104
- .font(.system(size: 8, design: .rounded))
99
+ .font(RunicFont.system(size: 8))
105
100
  .foregroundStyle(Color(nsColor: .tertiaryLabelColor))
106
101
  }
107
102
  }
@@ -110,7 +105,7 @@ struct InlineUsageChartView: View {
110
105
  .chartXAxis {
111
106
  AxisMarks(values: .automatic(desiredCount: 4)) { _ in
112
107
  AxisValueLabel(format: self.xAxisFormat)
113
- .font(.system(size: 8, design: .rounded))
108
+ .font(RunicFont.system(size: 8))
114
109
  .foregroundStyle(Color(nsColor: .tertiaryLabelColor))
115
110
  }
116
111
  }
@@ -120,7 +115,7 @@ struct InlineUsageChartView: View {
120
115
  // Peak badge
121
116
  if let peak = points.max(by: { $0.tokens < $1.tokens }), peak.tokens > 0 {
122
117
  Text("Peak \(UsageFormatter.tokenCountString(peak.tokens))")
123
- .font(.system(size: 8, weight: .medium, design: .rounded))
118
+ .font(RunicFont.system(size: 8, weight: .medium))
124
119
  .foregroundStyle(.secondary)
125
120
  .padding(.horizontal, 4)
126
121
  .padding(.vertical, 1)
@@ -14,8 +14,8 @@ struct MenuCardEntranceModifier: ViewModifier {
14
14
  .scaleEffect(self.appeared ? 1 : 0.97, anchor: .top)
15
15
  .onAppear {
16
16
  withAnimation(
17
- RunicAnimation.cardEntrance.delay(Double(self.index) * RunicAnimation.cardEntranceStagger)
18
- ) {
17
+ RunicAnimation.cardEntrance.delay(Double(self.index) * RunicAnimation.cardEntranceStagger))
18
+ {
19
19
  self.appeared = true
20
20
  }
21
21
  }
@@ -41,8 +41,7 @@ struct GlassShimmerModifier: ViewModifier {
41
41
  .frame(width: geo.size.width, height: geo.size.height)
42
42
  .allowsHitTesting(false)
43
43
  }
44
- .clipShape(RoundedRectangle(cornerRadius: RunicCornerRadius.lg, style: .continuous))
45
- )
44
+ .clipShape(RoundedRectangle(cornerRadius: RunicCornerRadius.lg, style: .continuous)))
46
45
  .onAppear {
47
46
  withAnimation(.easeInOut(duration: 2.0).delay(0.5)) {
48
47
  self.shimmerPhase = 1.2
@@ -61,29 +61,7 @@ struct ProviderSidebarMenuCardView<Content: View>: View {
61
61
  let iconProvider: (UsageProvider, CGFloat) -> NSImage
62
62
  let weeklyRemainingProvider: (UsageProvider) -> Double?
63
63
  let onSelect: (UsageProvider) -> Void
64
- let content: (CGFloat) -> Content
65
-
66
- init(
67
- providers: [UsageProvider],
68
- selected: UsageProvider?,
69
- totalWidth: CGFloat,
70
- showIcons: Bool,
71
- iconSize: ProviderSwitcherIconSize,
72
- iconProvider: @escaping (UsageProvider, CGFloat) -> NSImage,
73
- weeklyRemainingProvider: @escaping (UsageProvider) -> Double?,
74
- onSelect: @escaping (UsageProvider) -> Void,
75
- @ViewBuilder content: @escaping (CGFloat) -> Content)
76
- {
77
- self.providers = providers
78
- self.selected = selected
79
- self.totalWidth = totalWidth
80
- self.showIcons = showIcons
81
- self.iconSize = iconSize
82
- self.iconProvider = iconProvider
83
- self.weeklyRemainingProvider = weeklyRemainingProvider
84
- self.onSelect = onSelect
85
- self.content = content
86
- }
64
+ @ViewBuilder let content: (CGFloat) -> Content
87
65
 
88
66
  var body: some View {
89
67
  let style = MenuCardSidebarMetrics.style(for: self.providers.count, iconSize: self.iconSize)
@@ -124,7 +102,8 @@ private struct ProviderSidebarRailView: View {
124
102
  @Environment(\.menuItemHighlighted) private var isHighlighted
125
103
 
126
104
  private var railBackground: LinearGradient {
127
- let base = Color(nsColor: .controlTextColor).opacity(self.isHighlighted ? RunicColors.Opacity.subtle : RunicColors.Opacity.nano)
105
+ let base = Color(nsColor: .controlTextColor)
106
+ .opacity(self.isHighlighted ? RunicColors.Opacity.subtle : RunicColors.Opacity.nano)
128
107
  return LinearGradient(
129
108
  colors: [base, base.opacity(0.6)],
130
109
  startPoint: .top,
@@ -164,7 +143,8 @@ private struct ProviderSidebarRailView: View {
164
143
  }
165
144
 
166
145
  Rectangle()
167
- .fill(Color(nsColor: .separatorColor).opacity(self.isHighlighted ? RunicColors.Opacity.emphasis : RunicColors.Opacity.strong))
146
+ .fill(Color(nsColor: .separatorColor)
147
+ .opacity(self.isHighlighted ? RunicColors.Opacity.emphasis : RunicColors.Opacity.strong))
168
148
  .frame(width: 1)
169
149
  .padding(.top, MenuCardMetrics.headerTopPadding + 4)
170
150
  .padding(.bottom, MenuCardMetrics.tailPadding)
@@ -80,7 +80,7 @@ struct MenuCardSkeletonView: View {
80
80
  /// Computes a repeating phase (0...1) from the timeline date for the sliding gradient.
81
81
  private func shimmerPhase(date: Date) -> Double {
82
82
  let seconds = date.timeIntervalSinceReferenceDate
83
- let period: Double = 1.5
83
+ let period = 1.5
84
84
  return (seconds.truncatingRemainder(dividingBy: period)) / period
85
85
  }
86
86