@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
@@ -80,18 +80,18 @@ struct MediumWidgetView: View {
80
80
  HStack(spacing: 8) {
81
81
  // Status icon
82
82
  Image(systemName: entry.systemStatus.iconName)
83
- .font(.system(size: 16, weight: .semibold))
83
+ .font(RunicFont.system(size: 16, weight: .semibold))
84
84
  .foregroundColor(statusColor)
85
85
 
86
86
  Text("Runic")
87
- .font(.system(size: 16, weight: .bold))
87
+ .font(RunicFont.system(size: 16, weight: .bold))
88
88
  .foregroundColor(.white)
89
89
 
90
90
  Spacer()
91
91
 
92
92
  // Provider count
93
93
  Text("\(providers.count) active")
94
- .font(.system(size: 12, weight: .medium))
94
+ .font(RunicFont.system(size: 12, weight: .medium))
95
95
  .foregroundColor(.white.opacity(0.7))
96
96
  }
97
97
  }
@@ -110,18 +110,18 @@ struct MediumWidgetView: View {
110
110
  .frame(width: 10, height: 10)
111
111
 
112
112
  Text(provider.displayName)
113
- .font(.system(size: 13, weight: .semibold))
113
+ .font(RunicFont.system(size: 13, weight: .semibold))
114
114
  .foregroundColor(.white)
115
115
 
116
116
  Spacer()
117
117
 
118
118
  // Remaining percentage
119
119
  Text("\(Int(provider.remainingPercent))%")
120
- .font(.system(size: 15, weight: .bold, design: .rounded))
120
+ .font(RunicFont.system(size: 15, weight: .bold))
121
121
  .foregroundColor(.white)
122
122
 
123
123
  Text("left")
124
- .font(.system(size: 11, weight: .medium))
124
+ .font(RunicFont.system(size: 11, weight: .medium))
125
125
  .foregroundColor(.white.opacity(0.6))
126
126
  }
127
127
 
@@ -144,7 +144,7 @@ struct MediumWidgetView: View {
144
144
  if let countdown = provider.resetCountdown {
145
145
  HStack {
146
146
  Text("Resets \(countdown)")
147
- .font(.system(size: 10, weight: .medium))
147
+ .font(RunicFont.system(size: 10, weight: .medium))
148
148
  .foregroundColor(.white.opacity(0.5))
149
149
 
150
150
  Spacer()
@@ -228,16 +228,16 @@ struct MediumWidgetView: View {
228
228
  private var emptyStateView: some View {
229
229
  HStack(spacing: 12) {
230
230
  Image(systemName: "chart.bar.xaxis")
231
- .font(.system(size: 28))
231
+ .font(RunicFont.system(size: 28))
232
232
  .foregroundColor(.white.opacity(0.4))
233
233
 
234
234
  VStack(alignment: .leading, spacing: 4) {
235
235
  Text("No Providers Active")
236
- .font(.system(size: 14, weight: .semibold))
236
+ .font(RunicFont.system(size: 14, weight: .semibold))
237
237
  .foregroundColor(.white.opacity(0.7))
238
238
 
239
239
  Text("Open Runic to enable providers")
240
- .font(.system(size: 11))
240
+ .font(RunicFont.system(size: 11))
241
241
  .foregroundColor(.white.opacity(0.5))
242
242
  }
243
243
 
@@ -142,11 +142,11 @@ struct UsageProgressRing: View {
142
142
  // Center content
143
143
  VStack(spacing: size * 0.05) {
144
144
  Text("\(Int(provider.remainingPercent))%")
145
- .font(.system(size: size * 0.25, weight: .bold, design: .rounded))
145
+ .font(RunicFont.system(size: size * 0.25, weight: .bold))
146
146
  .foregroundColor(.white)
147
147
 
148
148
  Text("left")
149
- .font(.system(size: size * 0.12, weight: .medium))
149
+ .font(RunicFont.system(size: size * 0.12, weight: .medium))
150
150
  .foregroundColor(.white.opacity(0.7))
151
151
  }
152
152
  }
@@ -184,7 +184,7 @@ struct ProviderHeader: View {
184
184
  .frame(width: iconSize, height: iconSize)
185
185
 
186
186
  Text(provider.displayName)
187
- .font(.system(size: iconSize * 0.8, weight: .semibold))
187
+ .font(RunicFont.system(size: iconSize * 0.8, weight: .semibold))
188
188
  .foregroundColor(.white)
189
189
 
190
190
  Spacer()
@@ -201,13 +201,13 @@ struct CompactUsageBar: View {
201
201
  VStack(alignment: .leading, spacing: 4) {
202
202
  HStack {
203
203
  Text(provider.displayName)
204
- .font(.system(size: 12, weight: .medium))
204
+ .font(RunicFont.system(size: 12, weight: .medium))
205
205
  .foregroundColor(.white)
206
206
 
207
207
  Spacer()
208
208
 
209
209
  Text("\(Int(provider.remainingPercent))%")
210
- .font(.system(size: 12, weight: .bold, design: .rounded))
210
+ .font(RunicFont.system(size: 12, weight: .bold))
211
211
  .foregroundColor(.white)
212
212
  }
213
213
 
@@ -61,14 +61,14 @@ struct SmallWidgetView: View {
61
61
  .frame(width: 8, height: 8)
62
62
 
63
63
  Text(provider.displayName)
64
- .font(.system(size: 13, weight: .semibold))
64
+ .font(RunicFont.system(size: 13, weight: .semibold))
65
65
  .foregroundColor(.white)
66
66
 
67
67
  Spacer()
68
68
 
69
69
  // Status indicator
70
70
  Image(systemName: statusIcon(for: provider))
71
- .font(.system(size: 12))
71
+ .font(RunicFont.system(size: 12))
72
72
  .foregroundColor(statusColor(for: provider))
73
73
  }
74
74
  .padding(.horizontal, 16)
@@ -85,19 +85,19 @@ struct SmallWidgetView: View {
85
85
  VStack(spacing: 2) {
86
86
  if let countdown = provider.resetCountdown {
87
87
  Text("Resets")
88
- .font(.system(size: 10, weight: .medium))
88
+ .font(RunicFont.system(size: 10, weight: .medium))
89
89
  .foregroundColor(.white.opacity(0.6))
90
90
 
91
91
  Text(countdown)
92
- .font(.system(size: 12, weight: .semibold, design: .rounded))
92
+ .font(RunicFont.system(size: 12, weight: .semibold))
93
93
  .foregroundColor(.white.opacity(0.9))
94
94
  } else {
95
95
  Text("Updated")
96
- .font(.system(size: 10, weight: .medium))
96
+ .font(RunicFont.system(size: 10, weight: .medium))
97
97
  .foregroundColor(.white.opacity(0.6))
98
98
 
99
99
  Text(relativeTime(from: provider.updatedAt))
100
- .font(.system(size: 11, weight: .medium))
100
+ .font(RunicFont.system(size: 11, weight: .medium))
101
101
  .foregroundColor(.white.opacity(0.8))
102
102
  }
103
103
  }
@@ -160,15 +160,15 @@ struct SmallWidgetView: View {
160
160
  private var emptyStateView: some View {
161
161
  VStack(spacing: 8) {
162
162
  Image(systemName: "exclamationmark.triangle")
163
- .font(.system(size: 24))
163
+ .font(RunicFont.system(size: 24))
164
164
  .foregroundColor(.white.opacity(0.5))
165
165
 
166
166
  Text("No Data")
167
- .font(.system(size: 14, weight: .semibold))
167
+ .font(RunicFont.system(size: 14, weight: .semibold))
168
168
  .foregroundColor(.white.opacity(0.7))
169
169
 
170
170
  Text("Open app to refresh")
171
- .font(.system(size: 11))
171
+ .font(RunicFont.system(size: 11))
172
172
  .foregroundColor(.white.opacity(0.5))
173
173
  .multilineTextAlignment(.center)
174
174
  }
@@ -218,9 +218,9 @@ cat > "$APP/Contents/Info.plist" <<PLIST
218
218
  <key>LSMinimumSystemVersion</key><string>14.0</string>
219
219
  <key>LSUIElement</key><true/>
220
220
  <key>CFBundleIconFile</key><string>Icon</string>
221
- <key>NSHumanReadableCopyright</key><string>© 2026 Srinivas Pendela (hello@srinivas.dev). MIT License.</string>
221
+ <key>NSHumanReadableCopyright</key><string>© 2025-2026 Srinivas Pendela. MIT License.</string>
222
222
  <key>SUFeedURL</key><string>${FEED_URL}</string>
223
- <key>SUPublicEDKey</key><string>AGCY8w5vHirVfGGDGc8Szc5iuOqupZSh9pMj/Qs67XI=</string>
223
+ <key>SUPublicEDKey</key><string>1Ay3r9lgXQtyOjzVSCjlT5dvQuRlJghNKa8Goqvhfz4=</string>
224
224
  <key>SUEnableAutomaticChecks</key><${AUTO_CHECKS}/>
225
225
  <key>CodexBuildTimestamp</key><string>${BUILD_TIMESTAMP}</string>
226
226
  <key>CodexGitCommit</key><string>${GIT_COMMIT}</string>
@@ -24,9 +24,9 @@ func showAbout() {
24
24
  let description = NSAttributedString(
25
25
  string: "Runic tracks AI usage, tokens, and spend across providers from your menu bar.\n\n",
26
26
  attributes: [
27
- .font: NSFont.systemFont(ofSize: NSFont.smallSystemFontSize),
28
- .foregroundColor: NSColor.secondaryLabelColor,
29
- ])
27
+ .font: NSFont.systemFont(ofSize: NSFont.smallSystemFontSize),
28
+ .foregroundColor: NSColor.secondaryLabelColor,
29
+ ])
30
30
  let credits = NSMutableAttributedString(attributedString: description)
31
31
  credits.append(NSAttributedString(string: "MIT License\n", attributes: [
32
32
  .font: NSFont.systemFont(ofSize: NSFont.smallSystemFontSize),
@@ -1,5 +1,5 @@
1
- import RunicCore
2
1
  import Foundation
2
+ import RunicCore
3
3
  @preconcurrency import UserNotifications
4
4
 
5
5
  @MainActor
@@ -10,7 +10,9 @@ final class AppNotifications {
10
10
  private let logger = RunicLog.logger("notifications")
11
11
  private var authorizationTask: Task<Bool, Never>?
12
12
 
13
- init(centerProvider: @escaping @Sendable () -> UNUserNotificationCenter? = { AppNotifications.safeCurrentCenter() }) {
13
+ init(centerProvider: @escaping @Sendable ()
14
+ -> UNUserNotificationCenter? = { AppNotifications.safeCurrentCenter() })
15
+ {
14
16
  self.centerProvider = centerProvider
15
17
  }
16
18
 
@@ -60,7 +62,7 @@ final class AppNotifications {
60
62
  /// Safely obtain the current UNUserNotificationCenter, returning nil when the
61
63
  /// bundle proxy is not available (e.g. when running a bare executable outside
62
64
  /// a proper .app bundle).
63
- nonisolated private static func safeCurrentCenter() -> UNUserNotificationCenter? {
65
+ private nonisolated static func safeCurrentCenter() -> UNUserNotificationCenter? {
64
66
  guard Bundle.main.bundleIdentifier != nil else { return nil }
65
67
  return UNUserNotificationCenter.current()
66
68
  }
@@ -6,7 +6,16 @@ extension StatusItemController {
6
6
  // MARK: - Actions reachable from menus
7
7
 
8
8
  @objc func refreshNow() {
9
- Task { await self.store.refresh(trigger: .manual, forceTokenUsage: true) }
9
+ let provider = self.selectedMenuProvider
10
+ if let provider {
11
+ // Single provider selected — only refresh that one.
12
+ Task {
13
+ await self.store.refreshSingleProvider(provider)
14
+ }
15
+ } else {
16
+ // Overview — refresh all.
17
+ Task { await self.store.refresh(trigger: .manual, forceTokenUsage: true) }
18
+ }
10
19
  }
11
20
 
12
21
  @objc func installUpdate() {
@@ -149,9 +158,13 @@ extension StatusItemController {
149
158
  }
150
159
  }
151
160
 
152
- @objc func showSettingsGeneral() { self.openSettings(tab: .general) }
161
+ @objc func showSettingsGeneral() {
162
+ self.openSettings(tab: .general)
163
+ }
153
164
 
154
- @objc func showSettingsAbout() { self.openSettings(tab: .about) }
165
+ @objc func showSettingsAbout() {
166
+ self.openSettings(tab: .about)
167
+ }
155
168
 
156
169
  func openMenuFromShortcut() {
157
170
  if self.shouldMergeIcons {
@@ -593,7 +606,7 @@ extension StatusItemController {
593
606
  self.presentLoginAlert(title: info.title, message: info.message)
594
607
  }
595
608
 
596
- struct LoginAlertInfo: Equatable, Sendable {
609
+ struct LoginAlertInfo: Equatable {
597
610
  let title: String
598
611
  let message: String
599
612
  }
@@ -1,8 +1,9 @@
1
1
  import AppKit
2
- import RunicCore
3
2
  import QuartzCore
3
+ import RunicCore
4
4
 
5
5
  // MARK: - Animation & Performance Optimization
6
+
6
7
  //
7
8
  // **Zero Token Leakage Policy**:
8
9
  // - Use cookies/local storage from providers FIRST
@@ -433,14 +434,14 @@ extension StatusItemController {
433
434
  let isMerged = mergeIcons ?? self.shouldMergeIcons
434
435
  let isVisible = isMerged ? self.isEnabled(provider) : self.isVisible(provider)
435
436
  guard isVisible else { return false }
436
-
437
+
437
438
  // Critical: Only animate while actively fetching
438
439
  guard self.store.refreshingProviders.contains(provider) else { return false }
439
-
440
+
440
441
  // Stop animation if we have data OR reached error state (stale = error/cached)
441
442
  let isStale = self.store.isStale(provider: provider)
442
443
  let hasData = self.store.snapshot(for: provider) != nil
443
-
444
+
444
445
  // Animation only runs when: no data yet AND not in error/stale state
445
446
  return !hasData && !isStale
446
447
  }
@@ -59,7 +59,9 @@ extension StatusItemController {
59
59
  let reliability = self.store.ledgerReliabilityScore(for: provider)
60
60
  let routing = self.store.ledgerRoutingRecommendation(for: provider)
61
61
  let hasActiveBlock = activeBlock?.isActive == true
62
- guard daily != nil || hasActiveBlock || !modelBreakdown.isEmpty || !projectBreakdown.isEmpty || reliability != nil || routing != nil else {
62
+ guard daily != nil || hasActiveBlock || !modelBreakdown.isEmpty || !projectBreakdown
63
+ .isEmpty || reliability != nil || routing != nil
64
+ else {
63
65
  return nil
64
66
  }
65
67
 
@@ -230,22 +232,21 @@ extension StatusItemController {
230
232
  includeAttribution: Bool = false) -> String
231
233
  {
232
234
  let trimmedProjectName = projectName?.trimmingCharacters(in: .whitespacesAndNewlines)
233
- let displayName: String
234
- if let trimmedProjectName, !trimmedProjectName.isEmpty {
235
- displayName = trimmedProjectName
235
+ let displayName: String = if let trimmedProjectName, !trimmedProjectName.isEmpty {
236
+ trimmedProjectName
236
237
  } else if let projectID, !projectID.isEmpty {
237
238
  if let budgetName = ProjectBudgetStore.getBudget(projectID: projectID)?.projectName?
238
239
  .trimmingCharacters(in: .whitespacesAndNewlines),
239
240
  !budgetName.isEmpty
240
241
  {
241
- displayName = budgetName
242
+ budgetName
242
243
  } else if let fallback = UsageLedgerProjectIdentityResolver.fallbackDisplayName(projectID: projectID) {
243
- displayName = fallback
244
+ fallback
244
245
  } else {
245
- displayName = "Unknown project"
246
+ "Unknown project"
246
247
  }
247
248
  } else {
248
- displayName = "Unknown project"
249
+ "Unknown project"
249
250
  }
250
251
  guard includeAttribution else { return displayName }
251
252
  guard let annotation = self.projectNameAnnotation(
@@ -318,10 +319,10 @@ extension StatusItemController {
318
319
  return nil
319
320
  }
320
321
 
321
- var hash: UInt64 = 0xcbf29ce484222325
322
+ var hash: UInt64 = 0xCBF2_9CE4_8422_2325
322
323
  for byte in projectID.lowercased().utf8 {
323
324
  hash ^= UInt64(byte)
324
- hash = hash &* 0x100000001b3
325
+ hash = hash &* 0x100_0000_01B3
325
326
  }
326
327
  return String(format: "%08llx", hash)
327
328
  }
@@ -336,6 +337,7 @@ extension StatusItemController {
336
337
  let submenu = NSMenu()
337
338
  submenu.delegate = self
338
339
  let chartView = UsageBreakdownChartMenuView(breakdown: breakdown, width: width)
340
+ .runicTypography()
339
341
  let hosting = MenuHostingView(rootView: chartView)
340
342
  let controller = NSHostingController(rootView: chartView)
341
343
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -357,6 +359,7 @@ extension StatusItemController {
357
359
  let submenu = NSMenu()
358
360
  submenu.delegate = self
359
361
  let chartView = CreditsHistoryChartMenuView(breakdown: breakdown, width: width)
362
+ .runicTypography()
360
363
  let hosting = MenuHostingView(rootView: chartView)
361
364
  let controller = NSHostingController(rootView: chartView)
362
365
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -383,6 +386,7 @@ extension StatusItemController {
383
386
  daily: tokenSnapshot.daily,
384
387
  totalCostUSD: tokenSnapshot.last30DaysCostUSD,
385
388
  width: width)
389
+ .runicTypography()
386
390
  let hosting = MenuHostingView(rootView: chartView)
387
391
  let controller = NSHostingController(rootView: chartView)
388
392
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -411,6 +415,7 @@ extension StatusItemController {
411
415
  dailySummaries: dailySummaries,
412
416
  hourlySummaries: hourlySummaries,
413
417
  width: width)
418
+ .runicTypography()
414
419
  let hosting = MenuHostingView(rootView: chartView)
415
420
  let controller = NSHostingController(rootView: chartView)
416
421
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -438,6 +443,7 @@ extension StatusItemController {
438
443
  let submenu = NSMenu()
439
444
  submenu.delegate = self
440
445
  let chartView = HourlyActivityChartMenuView(hourlySummaries: hourlySummaries, width: width)
446
+ .runicTypography()
441
447
  let hosting = MenuHostingView(rootView: chartView)
442
448
  let controller = NSHostingController(rootView: chartView)
443
449
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -465,6 +471,7 @@ extension StatusItemController {
465
471
  let submenu = NSMenu()
466
472
  submenu.delegate = self
467
473
  let chartView = WeeklyActivityChartMenuView(dailySummaries: dailySummaries, width: width)
474
+ .runicTypography()
468
475
  let hosting = MenuHostingView(rootView: chartView)
469
476
  let controller = NSHostingController(rootView: chartView)
470
477
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -502,6 +509,7 @@ extension StatusItemController {
502
509
  currentUsedPercent: currentUsedPercent,
503
510
  todayTokens: todayTokens,
504
511
  width: width)
512
+ .runicTypography()
505
513
  let hosting = MenuHostingView(rootView: chartView)
506
514
  let controller = NSHostingController(rootView: chartView)
507
515
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -544,6 +552,7 @@ extension StatusItemController {
544
552
  primaryPercent: primaryPercent,
545
553
  secondaryPercent: secondaryPercent,
546
554
  width: width)
555
+ .runicTypography()
547
556
  let hosting = MenuHostingView(rootView: chartView)
548
557
  let controller = NSHostingController(rootView: chartView)
549
558
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -572,6 +581,7 @@ extension StatusItemController {
572
581
  let submenu = NSMenu()
573
582
  submenu.delegate = self
574
583
  let chartView = ProjectBreakdownMenuView(breakdown: breakdown, width: width)
584
+ .runicTypography()
575
585
  let hosting = MenuHostingView(rootView: chartView)
576
586
  let controller = NSHostingController(rootView: chartView)
577
587
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -598,6 +608,7 @@ extension StatusItemController {
598
608
  if !breakdown.isEmpty {
599
609
  let width = Self.menuCardBaseWidth
600
610
  let chartView = ModelBreakdownMenuView(breakdown: breakdown, width: width)
611
+ .runicTypography()
601
612
  let hosting = MenuHostingView(rootView: chartView)
602
613
  let controller = NSHostingController(rootView: chartView)
603
614
  let size = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
@@ -663,7 +674,7 @@ extension StatusItemController {
663
674
 
664
675
  private func modelQuotaWindows(for provider: UsageProvider) -> [RateWindow] {
665
676
  guard let snapshot = self.store.snapshot(for: provider) else { return [] }
666
- let windows = [snapshot.primary, snapshot.secondary, snapshot.tertiary].compactMap { $0 }
677
+ let windows = [snapshot.primary, snapshot.secondary, snapshot.tertiary].compactMap(\.self)
667
678
  var seen: Set<String> = []
668
679
  var result: [RateWindow] = []
669
680
 
@@ -691,8 +702,9 @@ extension StatusItemController {
691
702
  submenu.delegate = self
692
703
 
693
704
  // MARK: Model usage (24h) — tokens, prompts, estimated cost per model
705
+
694
706
  if let modelUsage = zai.modelUsage, !modelUsage.entries.isEmpty {
695
- let headerFont = NSFont.systemFont(ofSize: NSFont.systemFontSize, weight: .semibold)
707
+ let headerFont = RunicFont.nsFont(size: NSFont.systemFontSize, weight: .semibold)
696
708
  let header = NSMenuItem(title: "Models (24h)", action: nil, keyEquivalent: "")
697
709
  header.attributedTitle = NSAttributedString(string: "Models (24h)", attributes: [.font: headerFont])
698
710
  header.isEnabled = false
@@ -706,7 +718,7 @@ extension StatusItemController {
706
718
  title: "\(totalTokensStr) tokens · \(totalPromptsStr)\(totalCostStr)",
707
719
  action: nil, keyEquivalent: "")
708
720
  summaryItem.isEnabled = false
709
- let summaryFont = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize)
721
+ let summaryFont = RunicFont.nsFont(size: NSFont.smallSystemFontSize)
710
722
  summaryItem.attributedTitle = NSAttributedString(
711
723
  string: summaryItem.title,
712
724
  attributes: [.font: summaryFont, .foregroundColor: NSColor.secondaryLabelColor])
@@ -732,8 +744,9 @@ extension StatusItemController {
732
744
  }
733
745
 
734
746
  // MARK: Tool usage (24h) — MCP tool call counts
747
+
735
748
  if let toolUsage = zai.toolUsage, !toolUsage.entries.isEmpty {
736
- let headerFont = NSFont.systemFont(ofSize: NSFont.systemFontSize, weight: .semibold)
749
+ let headerFont = RunicFont.nsFont(size: NSFont.systemFontSize, weight: .semibold)
737
750
  let header = NSMenuItem(title: "MCP Tools (24h)", action: nil, keyEquivalent: "")
738
751
  header.attributedTitle = NSAttributedString(string: "MCP Tools (24h)", attributes: [.font: headerFont])
739
752
  header.isEnabled = false
@@ -743,7 +756,7 @@ extension StatusItemController {
743
756
  title: "\(toolUsage.totalCalls) total calls",
744
757
  action: nil, keyEquivalent: "")
745
758
  totalItem.isEnabled = false
746
- let totalFont = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize)
759
+ let totalFont = RunicFont.nsFont(size: NSFont.smallSystemFontSize)
747
760
  totalItem.attributedTitle = NSAttributedString(
748
761
  string: totalItem.title,
749
762
  attributes: [.font: totalFont, .foregroundColor: NSColor.secondaryLabelColor])
@@ -764,8 +777,9 @@ extension StatusItemController {
764
777
  }
765
778
 
766
779
  // MARK: Quota MCP details (existing — from timeLimit.usageDetails)
780
+
767
781
  if let timeLimit = zai.timeLimit, !timeLimit.usageDetails.isEmpty {
768
- let headerFont = NSFont.systemFont(ofSize: NSFont.systemFontSize, weight: .semibold)
782
+ let headerFont = RunicFont.nsFont(size: NSFont.systemFontSize, weight: .semibold)
769
783
  let header = NSMenuItem(title: "Quota Window", action: nil, keyEquivalent: "")
770
784
  header.attributedTitle = NSAttributedString(string: "Quota Window", attributes: [.font: headerFont])
771
785
  header.isEnabled = false
@@ -13,7 +13,7 @@ extension StatusItemController {
13
13
 
14
14
  let headerItem = NSMenuItem(title: "Custom Providers", action: nil, keyEquivalent: "")
15
15
  headerItem.isEnabled = false
16
- let font = NSFont.systemFont(ofSize: NSFont.systemFontSize, weight: .semibold)
16
+ let font = RunicFont.nsFont(size: NSFont.systemFontSize, weight: .semibold)
17
17
  headerItem.attributedTitle = NSAttributedString(string: "Custom Providers", attributes: [.font: font])
18
18
  menu.addItem(headerItem)
19
19
 
@@ -28,7 +28,10 @@ extension StatusItemController {
28
28
  }
29
29
  }
30
30
 
31
- private func makeCustomProviderMenuItem(provider: CustomProviderConfig, snapshot: CustomProviderSnapshot) -> NSMenuItem {
31
+ private func makeCustomProviderMenuItem(
32
+ provider: CustomProviderConfig,
33
+ snapshot: CustomProviderSnapshot) -> NSMenuItem
34
+ {
32
35
  let title: String
33
36
  if let quota = snapshot.usageData.quota, let used = snapshot.usageData.used, quota > 0 {
34
37
  let percent = Int((used / quota) * 100)
@@ -61,7 +64,7 @@ extension StatusItemController {
61
64
  item.image = image
62
65
  }
63
66
 
64
- let font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize)
67
+ let font = RunicFont.nsFont(size: NSFont.smallSystemFontSize)
65
68
  let subtitle = NSAttributedString(
66
69
  string: error,
67
70
  attributes: [.font: font, .foregroundColor: NSColor.systemRed])