@sriinnu/runic 1.2.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 (342) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +8 -1
  3. package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
  4. package/RuniciOS/Views/ProviderDetailView.swift +9 -9
  5. package/RuniciOS/Views/ProviderListView.swift +10 -10
  6. package/RuniciOS/Widgets/LargeWidget.swift +14 -14
  7. package/RuniciOS/Widgets/LiveActivity.swift +19 -19
  8. package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
  9. package/RuniciOS/Widgets/MediumWidget.swift +10 -10
  10. package/RuniciOS/Widgets/RunicWidget.swift +5 -5
  11. package/RuniciOS/Widgets/SmallWidget.swift +9 -9
  12. package/Sources/Runic/About.swift +3 -3
  13. package/Sources/Runic/AppNotifications.swift +5 -3
  14. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +7 -3
  15. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
  16. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
  17. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
  18. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +18 -13
  19. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
  20. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
  21. package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
  22. package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
  23. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
  24. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
  25. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
  26. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +1 -1
  27. package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
  28. package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
  29. package/Sources/Runic/Core/RunicColors.swift +6 -6
  30. package/Sources/Runic/Core/RunicSpacing.swift +7 -7
  31. package/Sources/Runic/Core/RunicTypography.swift +206 -0
  32. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +1 -3
  33. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +1 -3
  34. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +1 -3
  35. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +1 -3
  36. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +1 -3
  37. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
  38. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +1 -3
  39. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +1 -3
  40. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +1 -3
  41. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +1 -3
  42. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +1 -3
  43. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +1 -3
  44. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +1 -3
  45. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +1 -3
  46. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +1 -3
  47. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +1 -3
  48. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
  49. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +1 -3
  50. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +1 -3
  51. package/Sources/Runic/Core/Stores/SettingsStore.swift +89 -51
  52. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +1 -3
  53. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
  54. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
  55. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
  56. package/Sources/Runic/Core/Stores/UsageStore.swift +187 -161
  57. package/Sources/Runic/Core/Stores/XAITokenStore.swift +1 -3
  58. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +1 -2
  59. package/Sources/Runic/Core/UsageExporter.swift +8 -4
  60. package/Sources/Runic/ProviderRegistry.swift +1 -1
  61. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
  62. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
  63. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
  64. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
  65. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
  66. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
  67. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
  68. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
  69. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
  70. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
  71. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
  72. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
  73. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
  74. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
  75. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
  76. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
  77. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
  78. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
  79. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
  80. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
  81. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
  82. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
  83. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
  84. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
  85. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
  86. package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
  87. package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
  88. package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
  89. package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
  90. package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
  91. package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
  92. package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
  93. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
  94. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
  95. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
  96. package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
  97. package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
  98. package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
  99. package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
  100. package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
  101. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
  102. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
  103. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
  104. package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
  105. package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
  106. package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
  107. package/Sources/Runic/RunicApp.swift +46 -36
  108. package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
  109. package/Sources/Runic/UsagePaceText.swift +1 -1
  110. package/Sources/Runic/UsageProgressBar.swift +13 -9
  111. package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
  112. package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
  113. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
  114. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
  115. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
  116. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
  117. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
  118. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
  119. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
  120. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +12 -10
  121. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
  122. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
  123. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
  124. package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
  125. package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
  126. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
  127. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
  128. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
  129. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +18 -15
  130. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
  131. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
  132. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
  133. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
  134. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
  135. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
  136. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +22 -17
  137. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
  138. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
  139. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
  140. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
  141. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
  142. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
  143. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
  144. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
  145. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
  146. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
  147. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
  148. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
  149. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
  150. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
  151. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +44 -39
  152. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
  153. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
  154. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
  155. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
  156. package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
  157. package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
  158. package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
  159. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
  160. package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
  161. package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
  162. package/Sources/RunicCLI/CLIEntry.swift +83 -42
  163. package/Sources/RunicCLI/CLIRenderer.swift +1 -1
  164. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
  165. package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
  166. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
  167. package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
  168. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
  169. package/Sources/RunicClaudeWebProbe/main.swift +1 -1
  170. package/Sources/RunicCore/AlertRuleStore.swift +20 -20
  171. package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
  172. package/Sources/RunicCore/CostUsageModels.swift +1 -1
  173. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
  174. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +73 -75
  175. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
  176. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
  177. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
  178. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
  179. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
  180. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
  181. package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
  182. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
  183. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
  184. package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
  185. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
  186. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
  187. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
  188. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
  189. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
  190. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +3 -3
  191. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
  192. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
  193. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
  194. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
  195. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
  196. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
  197. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
  198. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
  199. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
  200. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +1 -1
  201. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
  202. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
  203. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
  204. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
  205. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
  206. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
  207. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
  208. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
  209. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
  210. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
  211. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
  212. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
  213. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
  214. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
  215. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +2 -2
  216. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
  217. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
  218. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
  219. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +5 -2
  220. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
  221. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
  222. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
  223. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
  224. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
  225. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +7 -7
  226. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
  227. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
  228. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
  229. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
  230. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +23 -10
  231. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
  232. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
  233. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
  234. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
  235. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
  236. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +26 -13
  237. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
  238. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
  239. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
  240. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
  241. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
  242. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
  243. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
  244. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
  245. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
  246. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
  247. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +48 -21
  248. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
  249. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
  250. package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
  251. package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
  252. package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
  253. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
  254. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
  255. package/Sources/RunicCore/Team/TeamModels.swift +111 -110
  256. package/Sources/RunicCore/Team/TeamStore.swift +126 -109
  257. package/Sources/RunicCore/UsageFetcher.swift +2 -2
  258. package/Sources/RunicCore/UsageFormatter.swift +25 -25
  259. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +27 -13
  260. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +11 -3
  261. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +5 -2
  262. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
  263. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
  264. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
  265. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
  266. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
  267. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
  268. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
  269. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
  270. package/Tests/RunicTests/AppDelegateTests.swift +8 -3
  271. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
  272. package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
  273. package/Tests/RunicTests/CLICostTests.swift +6 -7
  274. package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
  275. package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
  276. package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
  277. package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
  278. package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
  279. package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
  280. package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
  281. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
  282. package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
  283. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
  284. package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
  285. package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
  286. package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
  287. package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
  288. package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
  289. package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
  290. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
  291. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
  292. package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
  293. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
  294. package/Tests/RunicTests/InstallOriginTests.swift +1 -2
  295. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
  296. package/Tests/RunicTests/LiveAccountTests.swift +3 -3
  297. package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
  298. package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
  299. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
  300. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
  301. package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
  302. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
  303. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
  304. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
  305. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
  306. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
  307. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
  308. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
  309. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
  310. package/Tests/RunicTests/PathBuilderTests.swift +14 -15
  311. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
  312. package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
  313. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
  314. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
  315. package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
  316. package/Tests/RunicTests/RunicTests.swift +6 -7
  317. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
  318. package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
  319. package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
  320. package/Tests/RunicTests/StatusMenuTests.swift +63 -22
  321. package/Tests/RunicTests/StatusProbeTests.swift +28 -29
  322. package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
  323. package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
  324. package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
  325. package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
  326. package/Tests/RunicTests/TextParsingTests.swift +1 -2
  327. package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
  328. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
  329. package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
  330. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
  331. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
  332. package/Tests/RunicTests/UsagePaceTests.swift +5 -6
  333. package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
  334. package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
  335. package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
  336. package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
  337. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
  338. package/appcast.xml +36 -76
  339. package/package.json +1 -1
  340. package/runic.png +0 -0
  341. package/runic.svg +15 -0
  342. package/version.env +2 -2
@@ -4,19 +4,19 @@ import SwiftUI
4
4
 
5
5
  enum MenuContentMetrics {
6
6
  /// Horizontal padding for menu content
7
- static let horizontalPadding: CGFloat = RunicSpacing.sm // 12
7
+ static let horizontalPadding: CGFloat = RunicSpacing.sm // 12
8
8
 
9
9
  /// Vertical padding for menu content
10
- static let verticalPadding: CGFloat = RunicSpacing.xs // 8
10
+ static let verticalPadding: CGFloat = RunicSpacing.xs // 8
11
11
 
12
12
  /// Spacing between menu sections
13
- static let sectionSpacing: CGFloat = RunicSpacing.xs // 8
13
+ static let sectionSpacing: CGFloat = RunicSpacing.xs // 8
14
14
 
15
15
  /// Spacing between menu entries
16
- static let entrySpacing: CGFloat = RunicSpacing.xxs // 4
16
+ static let entrySpacing: CGFloat = RunicSpacing.xxs // 4
17
17
 
18
18
  /// Minimum menu width
19
- static let minWidth: CGFloat = 260
19
+ static let minWidth: CGFloat = 290
20
20
  }
21
21
 
22
22
  @MainActor
@@ -52,6 +52,7 @@ struct MenuContent: View {
52
52
  .padding(.horizontal, MenuContentMetrics.horizontalPadding)
53
53
  .padding(.vertical, MenuContentMetrics.verticalPadding)
54
54
  .frame(minWidth: MenuContentMetrics.minWidth, alignment: .leading)
55
+ .runicTypography()
55
56
  }
56
57
 
57
58
  @ViewBuilder
@@ -60,11 +61,11 @@ struct MenuContent: View {
60
61
  case let .text(text, style):
61
62
  switch style {
62
63
  case .headline:
63
- Text(text).font(.headline)
64
+ Text(text).font(RunicFont.headline)
64
65
  case .primary:
65
66
  Text(text)
66
67
  case .secondary:
67
- Text(text).foregroundStyle(.secondary).font(.footnote)
68
+ Text(text).foregroundStyle(.secondary).font(RunicFont.footnote)
68
69
  }
69
70
  case let .action(title, action):
70
71
  Button {
@@ -89,7 +90,9 @@ struct MenuContent: View {
89
90
  }
90
91
  }
91
92
 
92
- private func iconName(for action: MenuDescriptor.MenuAction) -> String? { action.systemImageName }
93
+ private func iconName(for action: MenuDescriptor.MenuAction) -> String? {
94
+ action.systemImageName
95
+ }
93
96
 
94
97
  private func perform(_ action: MenuDescriptor.MenuAction) {
95
98
  switch action {
@@ -1,5 +1,5 @@
1
- import RunicCore
2
1
  import Foundation
2
+ import RunicCore
3
3
 
4
4
  @MainActor
5
5
  struct MenuDescriptor {
@@ -351,7 +351,9 @@ private enum AccountFormatter {
351
351
  return cleaned.isEmpty ? text : cleaned
352
352
  }
353
353
 
354
- static func email(_ text: String) -> String { text }
354
+ static func email(_ text: String) -> String {
355
+ text
356
+ }
355
357
  }
356
358
 
357
359
  extension MenuDescriptor.MenuAction {
@@ -1,14 +1,7 @@
1
1
  import SwiftUI
2
2
 
3
- private struct MenuItemHighlightedKey: EnvironmentKey {
4
- static let defaultValue = false
5
- }
6
-
7
3
  extension EnvironmentValues {
8
- var menuItemHighlighted: Bool {
9
- get { self[MenuItemHighlightedKey.self] }
10
- set { self[MenuItemHighlightedKey.self] = newValue }
11
- }
4
+ @Entry var menuItemHighlighted: Bool = false
12
5
  }
13
6
 
14
7
  enum MenuHighlightStyle {
@@ -20,16 +20,18 @@ struct ModelBreakdownMenuView: View {
20
20
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
21
21
  if model.items.isEmpty {
22
22
  Text("No model data.")
23
- .font(.footnote)
23
+ .font(RunicFont.footnote)
24
24
  .foregroundStyle(.secondary)
25
25
  } else {
26
26
  // MARK: - Title
27
+
27
28
  Text("Models")
28
- .font(.caption)
29
+ .font(RunicFont.caption)
29
30
  .fontWeight(.semibold)
30
31
  .foregroundStyle(.primary)
31
32
 
32
33
  // MARK: - Donut chart
34
+
33
35
  Chart {
34
36
  ForEach(model.chartItems) { item in
35
37
  SectorMark(
@@ -45,6 +47,7 @@ struct ModelBreakdownMenuView: View {
45
47
  .accessibilityLabel(Self.chartAccessibilityLabel(model: model))
46
48
 
47
49
  // MARK: - Model list
50
+
48
51
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
49
52
  ForEach(model.chartItems) { item in
50
53
  Self.modelRow(item: item)
@@ -52,28 +55,31 @@ struct ModelBreakdownMenuView: View {
52
55
  }
53
56
 
54
57
  // MARK: - Overflow note
58
+
55
59
  if model.overflowCount > 0 {
56
60
  Text("and \(model.overflowCount) more")
57
- .font(.caption2)
61
+ .font(RunicFont.caption2)
58
62
  .foregroundStyle(.tertiary)
59
63
  }
60
64
 
61
65
  // MARK: - Cache hit rate
66
+
62
67
  if let cacheRate = model.cacheHitRateText {
63
68
  Divider()
64
69
  HStack(spacing: RunicSpacing.xxs) {
65
70
  Text("Cache hit rate")
66
- .font(.caption)
71
+ .font(RunicFont.caption)
67
72
  .foregroundStyle(.secondary)
68
73
  Spacer(minLength: RunicSpacing.xxs)
69
74
  Text(cacheRate)
70
- .font(.caption)
75
+ .font(RunicFont.caption)
71
76
  .fontWeight(.medium)
72
77
  .foregroundStyle(.primary)
73
78
  }
74
79
  }
75
80
 
76
81
  // MARK: - Total summary
82
+
77
83
  Divider()
78
84
  Self.totalRow(model: model)
79
85
  }
@@ -91,27 +97,27 @@ struct ModelBreakdownMenuView: View {
91
97
  .fill(item.color)
92
98
  .frame(width: RunicSpacing.chartLegendDot, height: RunicSpacing.chartLegendDot)
93
99
  Text(item.displayName)
94
- .font(.caption)
100
+ .font(RunicFont.caption)
95
101
  .foregroundStyle(.primary)
96
102
  .lineLimit(1)
97
103
  .truncationMode(.tail)
98
104
  Spacer(minLength: RunicSpacing.xxs)
99
105
  VStack(alignment: .trailing, spacing: 0) {
100
106
  Text("\(UsageFormatter.tokenCountString(item.totalTokens)) tokens")
101
- .font(.system(.caption2, design: .rounded))
107
+ .font(RunicFont.caption2)
102
108
  .foregroundStyle(.secondary)
103
109
  if let context = UsageFormatter.modelContextLabel(for: item.model) {
104
110
  Text(context)
105
- .font(.system(.caption2, design: .rounded))
111
+ .font(RunicFont.caption2)
106
112
  .foregroundStyle(.tertiary)
107
113
  }
108
114
  HStack(spacing: RunicSpacing.xxxs) {
109
115
  Text("\(item.requestCount) req\(item.requestCount == 1 ? "" : "s")")
110
- .font(.system(.caption2, design: .rounded))
116
+ .font(RunicFont.caption2)
111
117
  .foregroundStyle(.tertiary)
112
118
  if let cost = item.costText {
113
119
  Text(cost)
114
- .font(.system(.caption2, design: .rounded))
120
+ .font(RunicFont.caption2)
115
121
  .foregroundStyle(.tertiary)
116
122
  }
117
123
  }
@@ -122,21 +128,21 @@ struct ModelBreakdownMenuView: View {
122
128
  private static func totalRow(model: ModelModel) -> some View {
123
129
  HStack {
124
130
  Text("Total")
125
- .font(.caption)
131
+ .font(RunicFont.caption)
126
132
  .fontWeight(.medium)
127
133
  .foregroundStyle(.primary)
128
134
  Spacer(minLength: RunicSpacing.xxs)
129
135
  VStack(alignment: .trailing, spacing: 0) {
130
136
  Text("\(UsageFormatter.tokenCountString(model.grandTotalTokens)) tokens")
131
- .font(.system(.caption2, design: .rounded))
137
+ .font(RunicFont.caption2)
132
138
  .foregroundStyle(.secondary)
133
139
  HStack(spacing: RunicSpacing.xxxs) {
134
140
  Text("\(model.grandTotalRequests) requests")
135
- .font(.system(.caption2, design: .rounded))
141
+ .font(RunicFont.caption2)
136
142
  .foregroundStyle(.tertiary)
137
143
  if let cost = model.grandTotalCostText {
138
144
  Text(cost)
139
- .font(.system(.caption2, design: .rounded))
145
+ .font(RunicFont.caption2)
140
146
  .foregroundStyle(.tertiary)
141
147
  }
142
148
  }
@@ -16,8 +16,8 @@ struct OverviewMenuView: View {
16
16
  let todayTokens: Int
17
17
  let brandColor: Color
18
18
  let resetDescription: String?
19
- let windowLabel: String? // e.g. "5h" or "Weekly"
20
- let topModelContext: String? // e.g. "200K ctx"
19
+ let windowLabel: String? // e.g. "5h" or "Weekly"
20
+ let topModelContext: String? // e.g. "200K ctx"
21
21
  }
22
22
 
23
23
  struct DailyPoint: Identifiable {
@@ -37,6 +37,7 @@ struct OverviewMenuView: View {
37
37
  var body: some View {
38
38
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
39
39
  // MARK: - Hero header
40
+
40
41
  HStack(alignment: .center, spacing: RunicSpacing.xs) {
41
42
  // Ring indicator showing overall usage
42
43
  ZStack {
@@ -55,9 +56,9 @@ struct OverviewMenuView: View {
55
56
 
56
57
  VStack(alignment: .leading, spacing: 0) {
57
58
  Text(UsageFormatter.tokenCountString(self.totalTodayTokens))
58
- .font(.system(size: 22, weight: .bold, design: .rounded))
59
+ .font(RunicFont.system(size: 22, weight: .bold))
59
60
  Text("\(self.summaries.count) of \(self.totalProviders) active")
60
- .font(.system(.caption2, design: .rounded))
61
+ .font(RunicFont.caption2)
61
62
  .foregroundStyle(.tertiary)
62
63
  }
63
64
 
@@ -65,10 +66,10 @@ struct OverviewMenuView: View {
65
66
 
66
67
  VStack(alignment: .trailing, spacing: 2) {
67
68
  Text("today")
68
- .font(.system(.caption2, design: .rounded))
69
+ .font(RunicFont.caption2)
69
70
  .foregroundStyle(.tertiary)
70
71
  Text("\(Int(self.averageUsedPercent))% avg")
71
- .font(.system(.caption, design: .rounded))
72
+ .font(RunicFont.caption)
72
73
  .fontWeight(.medium)
73
74
  .foregroundStyle(.secondary)
74
75
  }
@@ -77,9 +78,10 @@ struct OverviewMenuView: View {
77
78
  Divider().opacity(0.5)
78
79
 
79
80
  // MARK: - Provider cards
81
+
80
82
  if self.summaries.isEmpty {
81
83
  Text("No active providers.")
82
- .font(.system(.footnote, design: .rounded))
84
+ .font(RunicFont.footnote)
83
85
  .foregroundStyle(.secondary)
84
86
  .frame(maxWidth: .infinity, alignment: .center)
85
87
  .padding(.vertical, RunicSpacing.md)
@@ -92,12 +94,13 @@ struct OverviewMenuView: View {
92
94
  }
93
95
 
94
96
  // MARK: - Combined 7-day chart
97
+
95
98
  if !self.chartPoints.isEmpty {
96
99
  Divider().opacity(0.5)
97
100
 
98
101
  HStack {
99
102
  Text("7-day activity")
100
- .font(.system(.caption2, design: .rounded))
103
+ .font(RunicFont.caption2)
101
104
  .foregroundStyle(.tertiary)
102
105
  Spacer()
103
106
  // Mini legend dots
@@ -109,7 +112,7 @@ struct OverviewMenuView: View {
109
112
  }
110
113
  if self.summaries.count > 4 {
111
114
  Text("+\(self.summaries.count - 4)")
112
- .font(.system(size: 8, design: .rounded))
115
+ .font(RunicFont.system(size: 8))
113
116
  .foregroundStyle(.quaternary)
114
117
  }
115
118
  }
@@ -133,7 +136,7 @@ struct OverviewMenuView: View {
133
136
  AxisValueLabel {
134
137
  if let tokens = value.as(Int.self) {
135
138
  Text(UsageFormatter.tokenCountString(tokens))
136
- .font(.system(size: 8, design: .rounded))
139
+ .font(RunicFont.system(size: 8))
137
140
  .foregroundStyle(Color(nsColor: .tertiaryLabelColor))
138
141
  }
139
142
  }
@@ -142,7 +145,7 @@ struct OverviewMenuView: View {
142
145
  .chartXAxis {
143
146
  AxisMarks(values: .automatic(desiredCount: 7)) { _ in
144
147
  AxisValueLabel(format: .dateTime.weekday(.narrow))
145
- .font(.system(size: 8, weight: .medium, design: .rounded))
148
+ .font(RunicFont.system(size: 8, weight: .medium))
146
149
  .foregroundStyle(Color(nsColor: .tertiaryLabelColor))
147
150
  }
148
151
  }
@@ -182,7 +185,7 @@ private struct ProviderRow: View {
182
185
 
183
186
  // Name
184
187
  Text(self.summary.name)
185
- .font(.system(.caption, design: .rounded))
188
+ .font(RunicFont.caption)
186
189
  .fontWeight(.medium)
187
190
  .frame(width: 58, alignment: .leading)
188
191
  .lineLimit(1)
@@ -224,14 +227,14 @@ private struct ProviderRow: View {
224
227
 
225
228
  // Percentage
226
229
  Text("\(Int(self.summary.usedPercent))%")
227
- .font(.system(size: 9, weight: .semibold, design: .rounded))
230
+ .font(RunicFont.system(size: 9, weight: .semibold))
228
231
  .foregroundStyle(self.summary.usedPercent > 80 ? .primary : .secondary)
229
232
  .frame(width: 28, alignment: .trailing)
230
233
 
231
234
  // Today's tokens (if any)
232
235
  if self.summary.todayTokens > 0 {
233
236
  Text(UsageFormatter.tokenCountString(self.summary.todayTokens))
234
- .font(.system(size: 8, design: .rounded))
237
+ .font(RunicFont.system(size: 8))
235
238
  .foregroundStyle(.tertiary)
236
239
  .frame(width: 32, alignment: .trailing)
237
240
  }
@@ -267,7 +270,7 @@ private struct InfoPill: View {
267
270
 
268
271
  var body: some View {
269
272
  Text(self.text)
270
- .font(.system(size: 8, weight: .medium, design: .rounded))
273
+ .font(RunicFont.system(size: 8, weight: .medium))
271
274
  .foregroundStyle(.tertiary)
272
275
  .padding(.horizontal, 4)
273
276
  .padding(.vertical, 1)
@@ -20,16 +20,18 @@ struct ProjectBreakdownMenuView: View {
20
20
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
21
21
  if model.items.isEmpty {
22
22
  Text("No project data.")
23
- .font(.footnote)
23
+ .font(RunicFont.footnote)
24
24
  .foregroundStyle(.secondary)
25
25
  } else {
26
26
  // MARK: - Title
27
+
27
28
  Text("Projects")
28
- .font(.caption)
29
+ .font(RunicFont.caption)
29
30
  .fontWeight(.semibold)
30
31
  .foregroundStyle(.primary)
31
32
 
32
33
  // MARK: - Horizontal bar chart
34
+
33
35
  Chart {
34
36
  ForEach(model.chartItems) { item in
35
37
  BarMark(
@@ -43,7 +45,7 @@ struct ProjectBreakdownMenuView: View {
43
45
  .chartYAxis {
44
46
  AxisMarks { _ in
45
47
  AxisValueLabel()
46
- .font(.caption2)
48
+ .font(RunicFont.caption2)
47
49
  .foregroundStyle(Color(nsColor: .secondaryLabelColor))
48
50
  }
49
51
  }
@@ -53,6 +55,7 @@ struct ProjectBreakdownMenuView: View {
53
55
  .accessibilityLabel(Self.chartAccessibilityLabel(model: model))
54
56
 
55
57
  // MARK: - Project list
58
+
56
59
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
57
60
  ForEach(model.chartItems) { item in
58
61
  Self.projectRow(item: item)
@@ -60,13 +63,15 @@ struct ProjectBreakdownMenuView: View {
60
63
  }
61
64
 
62
65
  // MARK: - Overflow note
66
+
63
67
  if model.overflowCount > 0 {
64
68
  Text("and \(model.overflowCount) more")
65
- .font(.caption2)
69
+ .font(RunicFont.caption2)
66
70
  .foregroundStyle(.tertiary)
67
71
  }
68
72
 
69
73
  // MARK: - Total summary
74
+
70
75
  Divider()
71
76
  Self.totalRow(model: model)
72
77
  }
@@ -84,24 +89,24 @@ struct ProjectBreakdownMenuView: View {
84
89
  .fill(item.color)
85
90
  .frame(width: RunicSpacing.chartLegendDot, height: RunicSpacing.chartLegendDot)
86
91
  Text(item.name)
87
- .font(.caption)
92
+ .font(RunicFont.caption)
88
93
  .foregroundStyle(.primary)
89
94
  .lineLimit(1)
90
95
  .truncationMode(.middle)
91
96
  Spacer(minLength: RunicSpacing.xxs)
92
97
  VStack(alignment: .trailing, spacing: 0) {
93
98
  Text("\(UsageFormatter.tokenCountString(item.totalTokens)) tokens")
94
- .font(.system(.caption2, design: .rounded))
99
+ .font(RunicFont.caption2)
95
100
  .foregroundStyle(.secondary)
96
101
  HStack(spacing: RunicSpacing.xxxs) {
97
102
  if let cost = item.costText {
98
103
  Text(cost)
99
- .font(.system(.caption2, design: .rounded))
104
+ .font(RunicFont.caption2)
100
105
  .foregroundStyle(.tertiary)
101
106
  }
102
107
  if item.modelCount > 0 {
103
108
  Text("\(item.modelCount) model\(item.modelCount == 1 ? "" : "s")")
104
- .font(.system(.caption2, design: .rounded))
109
+ .font(RunicFont.caption2)
105
110
  .foregroundStyle(.tertiary)
106
111
  }
107
112
  }
@@ -112,17 +117,17 @@ struct ProjectBreakdownMenuView: View {
112
117
  private static func totalRow(model: ProjectModel) -> some View {
113
118
  HStack {
114
119
  Text("Total")
115
- .font(.caption)
120
+ .font(RunicFont.caption)
116
121
  .fontWeight(.medium)
117
122
  .foregroundStyle(.primary)
118
123
  Spacer(minLength: RunicSpacing.xxs)
119
124
  VStack(alignment: .trailing, spacing: 0) {
120
125
  Text("\(UsageFormatter.tokenCountString(model.grandTotalTokens)) tokens")
121
- .font(.system(.caption2, design: .rounded))
126
+ .font(RunicFont.caption2)
122
127
  .foregroundStyle(.secondary)
123
128
  if let cost = model.grandTotalCostText {
124
129
  Text(cost)
125
- .font(.system(.caption2, design: .rounded))
130
+ .font(RunicFont.caption2)
126
131
  .foregroundStyle(.tertiary)
127
132
  }
128
133
  }
@@ -14,11 +14,11 @@ struct ProjectBudgetMenuView: View {
14
14
 
15
15
  var statusColor: Color {
16
16
  if self.isOverBudget {
17
- return Color(red: 0.94, green: 0.36, blue: 0.36) // Red
17
+ Color(red: 0.94, green: 0.36, blue: 0.36) // Red
18
18
  } else if self.isNearLimit {
19
- return Color(red: 0.94, green: 0.74, blue: 0.26) // Yellow
19
+ Color(red: 0.94, green: 0.74, blue: 0.26) // Yellow
20
20
  } else {
21
- return Color(red: 0.46, green: 0.75, blue: 0.36) // Green
21
+ Color(red: 0.46, green: 0.75, blue: 0.36) // Green
22
22
  }
23
23
  }
24
24
  }
@@ -44,7 +44,7 @@ struct ProjectBudgetMenuView: View {
44
44
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
45
45
  HStack {
46
46
  Text("Project Budgets")
47
- .font(.headline)
47
+ .font(RunicFont.headline)
48
48
  .fontWeight(.semibold)
49
49
  Spacer()
50
50
  Button {
@@ -52,9 +52,9 @@ struct ProjectBudgetMenuView: View {
52
52
  } label: {
53
53
  HStack(spacing: RunicSpacing.xxs) {
54
54
  Image(systemName: "gearshape")
55
- .font(.caption)
55
+ .font(RunicFont.caption)
56
56
  Text("Configure")
57
- .font(.caption)
57
+ .font(RunicFont.caption)
58
58
  }
59
59
  }
60
60
  .buttonStyle(.plain)
@@ -64,13 +64,13 @@ struct ProjectBudgetMenuView: View {
64
64
  if model.isEmpty {
65
65
  VStack(alignment: .leading, spacing: RunicSpacing.xs) {
66
66
  Text("No budgets configured.")
67
- .font(.footnote)
67
+ .font(RunicFont.footnote)
68
68
  .foregroundStyle(.secondary)
69
69
  Button {
70
70
  self.onOpenPreferences()
71
71
  } label: {
72
72
  Text("Set up budgets in Preferences")
73
- .font(.caption)
73
+ .font(RunicFont.caption)
74
74
  }
75
75
  .buttonStyle(.plain)
76
76
  .foregroundStyle(.blue)
@@ -88,8 +88,8 @@ struct ProjectBudgetMenuView: View {
88
88
  }
89
89
 
90
90
  // Summary
91
- let overBudget = model.filter { $0.isOverBudget }.count
92
- let nearLimit = model.filter { $0.isNearLimit && !$0.isOverBudget }.count
91
+ let overBudget = model.count(where: { $0.isOverBudget })
92
+ let nearLimit = model.count(where: { $0.isNearLimit && !$0.isOverBudget })
93
93
  if overBudget > 0 || nearLimit > 0 {
94
94
  Divider()
95
95
  .padding(.vertical, RunicSpacing.xxs)
@@ -98,20 +98,20 @@ struct ProjectBudgetMenuView: View {
98
98
  if overBudget > 0 {
99
99
  HStack(spacing: RunicSpacing.xxs) {
100
100
  Image(systemName: "exclamationmark.triangle.fill")
101
- .font(.caption2)
101
+ .font(RunicFont.caption2)
102
102
  .foregroundStyle(Color(red: 0.94, green: 0.36, blue: 0.36))
103
103
  Text("\(overBudget) project\(overBudget == 1 ? "" : "s") over budget")
104
- .font(.caption)
104
+ .font(RunicFont.caption)
105
105
  .foregroundStyle(.secondary)
106
106
  }
107
107
  }
108
108
  if nearLimit > 0 {
109
109
  HStack(spacing: RunicSpacing.xxs) {
110
110
  Image(systemName: "exclamationmark.circle.fill")
111
- .font(.caption2)
111
+ .font(RunicFont.caption2)
112
112
  .foregroundStyle(Color(red: 0.94, green: 0.74, blue: 0.26))
113
113
  Text("\(nearLimit) project\(nearLimit == 1 ? "" : "s") near limit")
114
- .font(.caption)
114
+ .font(RunicFont.caption)
115
115
  .foregroundStyle(.secondary)
116
116
  }
117
117
  }
@@ -167,34 +167,34 @@ private struct ProjectBudgetRow: View {
167
167
  var body: some View {
168
168
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
169
169
  HStack(alignment: .firstTextBaseline) {
170
- Text(project.projectName)
171
- .font(.body)
170
+ Text(self.project.projectName)
171
+ .font(RunicFont.body)
172
172
  .fontWeight(.medium)
173
173
  .lineLimit(1)
174
174
  Spacer()
175
175
  Text(self.budgetText)
176
- .font(.caption)
176
+ .font(RunicFont.caption)
177
177
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
178
178
  }
179
179
 
180
180
  UsageProgressBar(
181
- percent: project.percentUsed,
182
- tint: project.statusColor,
181
+ percent: self.project.percentUsed,
182
+ tint: self.project.statusColor,
183
183
  accessibilityLabel: "Budget usage")
184
184
 
185
185
  HStack(alignment: .firstTextBaseline) {
186
- Text(String(format: "%.0f%%", project.percentUsed))
187
- .font(.caption)
186
+ Text(String(format: "%.0f%%", self.project.percentUsed))
187
+ .font(RunicFont.caption)
188
188
  .foregroundStyle(self.statusTextColor)
189
189
  Spacer()
190
- if project.isOverBudget {
190
+ if self.project.isOverBudget {
191
191
  Text("Over budget")
192
- .font(.caption)
192
+ .font(RunicFont.caption)
193
193
  .foregroundStyle(Color(red: 0.94, green: 0.36, blue: 0.36))
194
194
  .fontWeight(.medium)
195
- } else if project.isNearLimit {
195
+ } else if self.project.isNearLimit {
196
196
  Text("Near limit")
197
- .font(.caption)
197
+ .font(RunicFont.caption)
198
198
  .foregroundStyle(Color(red: 0.94, green: 0.74, blue: 0.26))
199
199
  .fontWeight(.medium)
200
200
  }
@@ -208,13 +208,13 @@ private struct ProjectBudgetRow: View {
208
208
  }
209
209
 
210
210
  private var budgetText: String {
211
- "\(UsageFormatter.usdString(project.spent)) / \(UsageFormatter.usdString(project.budget.monthlyLimit))"
211
+ "\(UsageFormatter.usdString(self.project.spent)) / \(UsageFormatter.usdString(self.project.budget.monthlyLimit))"
212
212
  }
213
213
 
214
214
  private var statusTextColor: Color {
215
215
  if self.isHighlighted {
216
216
  return MenuHighlightStyle.selectionText
217
217
  }
218
- return project.statusColor
218
+ return self.project.statusColor
219
219
  }
220
220
  }
@@ -33,11 +33,11 @@ struct ProviderTabBarView: View {
33
33
  .aspectRatio(contentMode: .fit)
34
34
  .frame(width: 18, height: 18)
35
35
  } else if tab.provider == nil {
36
- Image(systemName: "square.grid.2x2")
36
+ Image(systemName: "square.grid.2x2")
37
37
  .font(.system(size: 14, weight: .medium))
38
38
  }
39
39
  Text(tab.label)
40
- .font(.system(.caption, design: .rounded))
40
+ .font(RunicFont.caption)
41
41
  .fontWeight(tab.isSelected ? .semibold : .regular)
42
42
  .lineLimit(1)
43
43
  }
@@ -47,16 +47,14 @@ struct ProviderTabBarView: View {
47
47
  Capsule(style: .continuous)
48
48
  .fill(tab.isSelected
49
49
  ? tab.brandColor.opacity(RunicColors.Opacity.medium)
50
- : Color(nsColor: .controlBackgroundColor).opacity(RunicColors.Opacity.subtle))
51
- )
50
+ : Color(nsColor: .controlBackgroundColor).opacity(RunicColors.Opacity.subtle)))
52
51
  .overlay(
53
52
  Capsule(style: .continuous)
54
53
  .stroke(
55
54
  tab.isSelected
56
55
  ? tab.brandColor.opacity(RunicColors.Opacity.strong)
57
56
  : Color(nsColor: .separatorColor).opacity(RunicColors.Opacity.light),
58
- lineWidth: 0.5)
59
- )
57
+ lineWidth: 0.5))
60
58
  .foregroundStyle(tab.isSelected ? tab.brandColor : .secondary)
61
59
  .shadow(
62
60
  color: tab.isSelected ? tab.brandColor.opacity(0.15) : .clear,