@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
@@ -7,45 +7,45 @@ import SwiftUI
7
7
  /// These values are carefully tuned for readability and visual hierarchy.
8
8
  enum MenuCardMetrics {
9
9
  /// Horizontal padding on left/right edges of card content
10
- static let horizontalPadding: CGFloat = RunicSpacing.sm // 12
10
+ static let horizontalPadding: CGFloat = RunicSpacing.sm // 12
11
11
 
12
12
  /// Top padding for the header section (provider name, email)
13
- static let headerTopPadding: CGFloat = RunicSpacing.xxs // 4
13
+ static let headerTopPadding: CGFloat = RunicSpacing.xxs // 4
14
14
 
15
15
  /// Bottom padding for the header section
16
- static let headerBottomPadding: CGFloat = RunicSpacing.xxs // 4
16
+ static let headerBottomPadding: CGFloat = RunicSpacing.xxs // 4
17
17
 
18
18
  /// Top padding for content sections (usage, credits, cost)
19
- static let sectionTopPadding: CGFloat = RunicSpacing.xs // 8
19
+ static let sectionTopPadding: CGFloat = RunicSpacing.xs // 8
20
20
 
21
21
  /// Bottom padding for content sections
22
- static let sectionBottomPadding: CGFloat = RunicSpacing.xxs // 4
22
+ static let sectionBottomPadding: CGFloat = RunicSpacing.xxs // 4
23
23
 
24
24
  /// Vertical spacing between major sections (e.g., Session vs Weekly)
25
- static let sectionSpacing: CGFloat = RunicSpacing.xs // 8
25
+ static let sectionSpacing: CGFloat = RunicSpacing.xs // 8
26
26
 
27
27
  /// Spacing between related blocks of content
28
- static let blockSpacing: CGFloat = RunicSpacing.xs // 8
28
+ static let blockSpacing: CGFloat = RunicSpacing.xs // 8
29
29
 
30
30
  /// Spacing between individual text lines within a section
31
- static let lineSpacing: CGFloat = RunicSpacing.xxs // 4
31
+ static let lineSpacing: CGFloat = RunicSpacing.xxs // 4
32
32
 
33
33
  /// Base padding for menu items
34
- static let menuItemBasePadding: CGFloat = RunicSpacing.xxs // 4
34
+ static let menuItemBasePadding: CGFloat = RunicSpacing.xxs // 4
35
35
 
36
36
  /// Additional padding for text with descenders (y, g, p, q, j)
37
- static let menuItemDescenderPadding: CGFloat = RunicSpacing.xxxs // 2
37
+ static let menuItemDescenderPadding: CGFloat = RunicSpacing.xxxs // 2
38
38
 
39
39
  /// Tail padding at the end of sections
40
- static let tailPadding: CGFloat = RunicSpacing.xxs // 4
41
- static let metricCardPadding: CGFloat = RunicSpacing.sm // 8
40
+ static let tailPadding: CGFloat = RunicSpacing.xxs // 4
41
+ static let metricCardPadding: CGFloat = RunicSpacing.sm // 8
42
42
  static let metricCardCornerRadius: CGFloat = RunicCornerRadius.sm
43
43
  }
44
44
 
45
45
  /// SwiftUI card used inside the NSMenu to mirror Apple's rich menu panels.
46
46
  struct UsageMenuCardView: View {
47
47
  struct Model {
48
- enum PercentStyle: String, Sendable {
48
+ enum PercentStyle: String {
49
49
  case left
50
50
  case used
51
51
 
@@ -83,7 +83,7 @@ struct UsageMenuCardView: View {
83
83
  case error
84
84
  }
85
85
 
86
- struct TokenUsageSection: Sendable {
86
+ struct TokenUsageSection {
87
87
  let sessionLine: String
88
88
  let sessionDetailLine: String?
89
89
  let monthLine: String
@@ -94,13 +94,13 @@ struct UsageMenuCardView: View {
94
94
  let errorCopyText: String?
95
95
  }
96
96
 
97
- struct ProviderCostSection: Sendable {
97
+ struct ProviderCostSection {
98
98
  let title: String
99
99
  let percentUsed: Double
100
100
  let spendLine: String
101
101
  }
102
102
 
103
- struct InsightsSection: Sendable {
103
+ struct InsightsSection {
104
104
  let title: String
105
105
  let todayLine: String?
106
106
  let todayDetail: String?
@@ -157,11 +157,6 @@ struct UsageMenuCardView: View {
157
157
  let width: CGFloat
158
158
  @Environment(\.menuItemHighlighted) private var isHighlighted
159
159
 
160
- init(model: Model, width: CGFloat) {
161
- self.model = model
162
- self.width = width
163
- }
164
-
165
160
  var body: some View {
166
161
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
167
162
  UsageMenuCardHeaderView(model: self.model)
@@ -181,7 +176,7 @@ struct UsageMenuCardView: View {
181
176
  } else {
182
177
  let hasUsage = !self.model.metrics.isEmpty
183
178
  let includeSummarySections = self.model.menuMode != .glance
184
- let includeInsightSections = self.model.menuMode == .`operator`
179
+ let includeInsightSections = self.model.menuMode == .operator
185
180
  let hasCredits = includeSummarySections && self.model.creditsText != nil
186
181
  let hasExtraUsage = includeSummarySections && self.model.providerCost != nil
187
182
  let hasTokenCost = includeSummarySections && self.model.tokenUsage != nil
@@ -229,28 +224,28 @@ struct UsageMenuCardView: View {
229
224
  if hasTokenCost, let tokenUsage = self.model.tokenUsage {
230
225
  VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
231
226
  Text("Cost")
232
- .font(.body)
227
+ .font(RunicFont.body)
233
228
  .fontWeight(.medium)
234
229
  Text(tokenUsage.sessionLine)
235
- .font(.footnote)
230
+ .font(RunicFont.footnote)
236
231
  if let sessionDetail = tokenUsage.sessionDetailLine {
237
232
  Text(sessionDetail)
238
- .font(.footnote)
233
+ .font(RunicFont.footnote)
239
234
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
240
235
  }
241
236
  Text(tokenUsage.monthLine)
242
- .font(.footnote)
237
+ .font(RunicFont.footnote)
243
238
  if let monthDetail = tokenUsage.monthDetailLine {
244
239
  Text(monthDetail)
245
- .font(.footnote)
240
+ .font(RunicFont.footnote)
246
241
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
247
242
  }
248
243
  Text(tokenUsage.updatedLine)
249
- .font(.caption)
244
+ .font(RunicFont.caption)
250
245
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
251
246
  if let hint = tokenUsage.hintLine, !hint.isEmpty {
252
247
  Text(hint)
253
- .font(.footnote)
248
+ .font(RunicFont.footnote)
254
249
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
255
250
  .lineLimit(4)
256
251
  .fixedSize(horizontal: false, vertical: true)
@@ -258,10 +253,10 @@ struct UsageMenuCardView: View {
258
253
  if let error = tokenUsage.errorLine, !error.isEmpty {
259
254
  HStack(alignment: .top, spacing: RunicSpacing.xs) {
260
255
  Image(systemName: "exclamationmark.triangle.fill")
261
- .font(.footnote)
256
+ .font(RunicFont.footnote)
262
257
  .foregroundStyle(RunicColors.error)
263
258
  Text(error)
264
- .font(.footnote)
259
+ .font(RunicFont.footnote)
265
260
  .foregroundStyle(MenuHighlightStyle.error(self.isHighlighted))
266
261
  .lineLimit(4)
267
262
  .fixedSize(horizontal: false, vertical: true)
@@ -295,7 +290,7 @@ struct UsageMenuCardView: View {
295
290
 
296
291
  private var hasDetails: Bool {
297
292
  let includeSummarySections = self.model.menuMode != .glance
298
- let includeInsightSections = self.model.menuMode == .`operator`
293
+ let includeInsightSections = self.model.menuMode == .operator
299
294
  return !self.model.metrics.isEmpty || self.model.placeholder != nil || (includeSummarySections && (
300
295
  self.model.tokenUsage != nil || self.model.providerCost != nil)) || (includeInsightSections &&
301
296
  self.model.insights != nil)
@@ -313,7 +308,7 @@ private struct UsageMenuCardHeaderView: View {
313
308
 
314
309
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
315
310
  Text(self.model.providerName)
316
- .font(.headline)
311
+ .font(RunicFont.headline)
317
312
  .fontWeight(.semibold)
318
313
  if !self.model.email.isEmpty {
319
314
  ProfilePill(
@@ -344,7 +339,7 @@ private struct UsageMenuCardHeaderView: View {
344
339
  let subtitleAlignment: VerticalAlignment = self.model.subtitleStyle == .error ? .top : .firstTextBaseline
345
340
  HStack(alignment: subtitleAlignment) {
346
341
  Text(self.model.subtitleText)
347
- .font(.footnote.weight(.medium))
342
+ .font(RunicFont.footnote.weight(.medium))
348
343
  .foregroundStyle(self.subtitleColor)
349
344
  .lineLimit(self.model.subtitleStyle == .error ? 4 : 1)
350
345
  .multilineTextAlignment(.leading)
@@ -359,7 +354,7 @@ private struct UsageMenuCardHeaderView: View {
359
354
 
360
355
  if let topModelLine = self.model.topModelLine {
361
356
  Text(topModelLine)
362
- .font(.footnote)
357
+ .font(RunicFont.footnote)
363
358
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
364
359
  .lineLimit(1)
365
360
  }
@@ -394,13 +389,17 @@ private struct UsageMenuCardHeaderView: View {
394
389
  }
395
390
 
396
391
  private var headerBorder: some View {
397
- return RoundedRectangle(cornerRadius: RunicCornerRadius.lg, style: .continuous)
392
+ RoundedRectangle(cornerRadius: RunicCornerRadius.lg, style: .continuous)
398
393
  .stroke(Color.white.opacity(RunicColors.Opacity.medium), lineWidth: 0.7)
399
394
  }
400
395
 
401
396
  private var brandNSColor: NSColor {
402
397
  let color = ProviderDescriptorRegistry.descriptor(for: self.model.provider).branding.color
403
- return NSColor(calibratedRed: CGFloat(color.red), green: CGFloat(color.green), blue: CGFloat(color.blue), alpha: 1)
398
+ return NSColor(
399
+ calibratedRed: CGFloat(color.red),
400
+ green: CGFloat(color.green),
401
+ blue: CGFloat(color.blue),
402
+ alpha: 1)
404
403
  }
405
404
  }
406
405
 
@@ -455,7 +454,7 @@ private struct ProviderAvatarView: View {
455
454
  .shadow(color: Color.black.opacity(0.18), radius: 1, x: 0, y: 1)
456
455
  } else {
457
456
  Text(self.fallbackInitials)
458
- .font(.caption.weight(.semibold))
457
+ .font(RunicFont.caption.weight(.semibold))
459
458
  .foregroundStyle(Color.white.opacity(0.95))
460
459
  }
461
460
  }
@@ -467,7 +466,11 @@ private struct ProviderAvatarView: View {
467
466
 
468
467
  private var brandNSColor: NSColor {
469
468
  let color = ProviderDescriptorRegistry.descriptor(for: self.provider).branding.color
470
- return NSColor(calibratedRed: CGFloat(color.red), green: CGFloat(color.green), blue: CGFloat(color.blue), alpha: 1)
469
+ return NSColor(
470
+ calibratedRed: CGFloat(color.red),
471
+ green: CGFloat(color.green),
472
+ blue: CGFloat(color.blue),
473
+ alpha: 1)
471
474
  }
472
475
 
473
476
  private var fallbackInitials: String {
@@ -491,9 +494,9 @@ private struct ProfilePill: View {
491
494
  var body: some View {
492
495
  HStack(spacing: RunicSpacing.xxxs) {
493
496
  Image(systemName: self.systemImage)
494
- .font(.caption2.weight(.semibold))
497
+ .font(RunicFont.caption2.weight(.semibold))
495
498
  Text(self.text)
496
- .font(.caption.weight(.medium))
499
+ .font(RunicFont.caption.weight(.medium))
497
500
  }
498
501
  .padding(.horizontal, RunicSpacing.compact)
499
502
  .padding(.vertical, RunicSpacing.xxxs)
@@ -509,32 +512,31 @@ private struct ProfilePill: View {
509
512
  private var foregroundColor: Color {
510
513
  switch self.style {
511
514
  case .email:
512
- return .secondary
515
+ .secondary
513
516
  case .plan:
514
- return self.tint.opacity(RunicColors.Opacity.vivid)
517
+ self.tint.opacity(RunicColors.Opacity.vivid)
515
518
  }
516
519
  }
517
520
 
518
521
  private var backgroundColor: Color {
519
522
  switch self.style {
520
523
  case .email:
521
- return Color(nsColor: .quaternaryLabelColor).opacity(RunicColors.Opacity.emphasis)
524
+ Color(nsColor: .quaternaryLabelColor).opacity(RunicColors.Opacity.emphasis)
522
525
  case .plan:
523
- return self.tint.opacity(RunicColors.Opacity.light)
526
+ self.tint.opacity(RunicColors.Opacity.light)
524
527
  }
525
528
  }
526
529
 
527
530
  private var borderColor: Color {
528
531
  switch self.style {
529
532
  case .email:
530
- return Color(nsColor: .separatorColor).opacity(RunicColors.Opacity.strong)
533
+ Color(nsColor: .separatorColor).opacity(RunicColors.Opacity.strong)
531
534
  case .plan:
532
- return self.tint.opacity(RunicColors.Opacity.medium)
535
+ self.tint.opacity(RunicColors.Opacity.medium)
533
536
  }
534
537
  }
535
538
  }
536
539
 
537
-
538
540
  private struct MenuHeaderBadgeView: View {
539
541
  let badge: UsageMenuCardView.Model.HeaderBadge
540
542
  let isHighlighted: Bool
@@ -548,10 +550,10 @@ private struct MenuHeaderBadgeView: View {
548
550
  .frame(width: 10, height: 10)
549
551
  } else {
550
552
  Image(systemName: self.badgeIcon)
551
- .font(.caption2.weight(.semibold))
553
+ .font(RunicFont.caption2.weight(.semibold))
552
554
  }
553
555
  Text(self.badge.text)
554
- .font(.caption2.weight(.semibold))
556
+ .font(RunicFont.caption2.weight(.semibold))
555
557
  }
556
558
  .padding(.horizontal, RunicSpacing.compact)
557
559
  .padding(.vertical, RunicSpacing.xxxs)
@@ -566,42 +568,42 @@ private struct MenuHeaderBadgeView: View {
566
568
 
567
569
  private var badgeIcon: String {
568
570
  switch self.badge.style {
569
- case .info: return "arrow.triangle.2.circlepath"
570
- case .warning: return "exclamationmark.triangle.fill"
571
- case .error: return "xmark.circle.fill"
571
+ case .info: "arrow.triangle.2.circlepath"
572
+ case .warning: "exclamationmark.triangle.fill"
573
+ case .error: "xmark.circle.fill"
572
574
  }
573
575
  }
574
576
 
575
577
  private var backgroundColor: Color {
576
578
  switch self.badge.style {
577
579
  case .info:
578
- return Color(nsColor: .systemBlue).opacity(self.isHighlighted ? 0.25 : 0.12)
580
+ Color(nsColor: .systemBlue).opacity(self.isHighlighted ? 0.25 : 0.12)
579
581
  case .warning:
580
- return Color(nsColor: .systemOrange).opacity(self.isHighlighted ? 0.35 : 0.15)
582
+ Color(nsColor: .systemOrange).opacity(self.isHighlighted ? 0.35 : 0.15)
581
583
  case .error:
582
- return Color(nsColor: .systemRed).opacity(self.isHighlighted ? 0.35 : 0.15)
584
+ Color(nsColor: .systemRed).opacity(self.isHighlighted ? 0.35 : 0.15)
583
585
  }
584
586
  }
585
587
 
586
588
  private var foregroundColor: Color {
587
589
  switch self.badge.style {
588
590
  case .info:
589
- return Color(nsColor: .systemBlue).opacity(RunicColors.Opacity.vivid)
591
+ Color(nsColor: .systemBlue).opacity(RunicColors.Opacity.vivid)
590
592
  case .warning:
591
- return Color(nsColor: .systemOrange)
593
+ Color(nsColor: .systemOrange)
592
594
  case .error:
593
- return Color(nsColor: .systemRed)
595
+ Color(nsColor: .systemRed)
594
596
  }
595
597
  }
596
598
 
597
599
  private var borderColor: Color {
598
600
  switch self.badge.style {
599
601
  case .info:
600
- return Color(nsColor: .systemBlue).opacity(RunicColors.Opacity.medium)
602
+ Color(nsColor: .systemBlue).opacity(RunicColors.Opacity.medium)
601
603
  case .warning:
602
- return Color(nsColor: .systemOrange).opacity(RunicColors.Opacity.strong)
604
+ Color(nsColor: .systemOrange).opacity(RunicColors.Opacity.strong)
603
605
  case .error:
604
- return Color(nsColor: .systemRed).opacity(RunicColors.Opacity.strong)
606
+ Color(nsColor: .systemRed).opacity(RunicColors.Opacity.strong)
605
607
  }
606
608
  }
607
609
  }
@@ -615,15 +617,15 @@ private struct MenuEmptyStateView: View {
615
617
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
616
618
  HStack(spacing: RunicSpacing.xxs) {
617
619
  Image(systemName: "sparkles")
618
- .font(.caption.weight(.semibold))
620
+ .font(RunicFont.caption.weight(.semibold))
619
621
  Text("Connect \(self.providerName)")
620
- .font(.subheadline.weight(.semibold))
622
+ .font(RunicFont.subheadline.weight(.semibold))
621
623
  }
622
624
  Text(self.placeholder)
623
- .font(.footnote)
625
+ .font(RunicFont.footnote)
624
626
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
625
627
  Text("Open Settings → Providers, add credentials, then refresh.")
626
- .font(.footnote)
628
+ .font(RunicFont.footnote)
627
629
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
628
630
  .fixedSize(horizontal: false, vertical: true)
629
631
  }
@@ -667,7 +669,7 @@ private struct CopyIconButton: View {
667
669
  }
668
670
  } label: {
669
671
  Image(systemName: self.didCopy ? "checkmark" : "doc.on.doc")
670
- .font(.caption2.weight(.semibold))
672
+ .font(RunicFont.caption2.weight(.semibold))
671
673
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
672
674
  .frame(width: 18, height: 18)
673
675
  }
@@ -690,7 +692,7 @@ private struct ProviderCostContent: View {
690
692
  var body: some View {
691
693
  VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
692
694
  Text(self.section.title)
693
- .font(.body)
695
+ .font(RunicFont.body)
694
696
  .fontWeight(.medium)
695
697
  UsageProgressBar(
696
698
  percent: self.section.percentUsed,
@@ -698,10 +700,10 @@ private struct ProviderCostContent: View {
698
700
  accessibilityLabel: "Extra usage spent")
699
701
  HStack(alignment: .firstTextBaseline) {
700
702
  Text(self.section.spendLine)
701
- .font(.footnote)
703
+ .font(RunicFont.footnote)
702
704
  Spacer()
703
705
  Text(String(format: "%.0f%% used", min(100, max(0, self.section.percentUsed))))
704
- .font(.system(.footnote, design: .rounded))
706
+ .font(RunicFont.footnote)
705
707
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
706
708
  }
707
709
  }
@@ -715,88 +717,88 @@ private struct InsightsContent: View {
715
717
  var body: some View {
716
718
  VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
717
719
  Text(self.section.title)
718
- .font(.body)
720
+ .font(RunicFont.body)
719
721
  .fontWeight(.medium)
720
722
  if let today = self.section.todayLine {
721
723
  Text(today)
722
- .font(.footnote)
724
+ .font(RunicFont.footnote)
723
725
  }
724
726
  if let detail = self.section.todayDetail {
725
727
  Text(detail)
726
- .font(.footnote)
728
+ .font(RunicFont.footnote)
727
729
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
728
730
  }
729
731
  if let forecastLine = self.section.forecastLine {
730
732
  Text(forecastLine)
731
- .font(.footnote)
733
+ .font(RunicFont.footnote)
732
734
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
733
735
  }
734
736
  if let block = self.section.blockLine {
735
737
  Text(block)
736
- .font(.footnote)
738
+ .font(RunicFont.footnote)
737
739
  }
738
740
  if let blockDetail = self.section.blockDetail {
739
741
  Text(blockDetail)
740
- .font(.footnote)
742
+ .font(RunicFont.footnote)
741
743
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
742
744
  }
743
745
  if let modelLine = self.section.modelLine {
744
746
  Text(modelLine)
745
- .font(.footnote)
747
+ .font(RunicFont.footnote)
746
748
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
747
749
  }
748
750
  if let projectLine = self.section.projectLine {
749
751
  Text(projectLine)
750
- .font(.footnote)
752
+ .font(RunicFont.footnote)
751
753
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
752
754
  }
753
755
  if let projectDetail = self.section.projectDetail {
754
756
  Text(projectDetail)
755
- .font(.footnote)
757
+ .font(RunicFont.footnote)
756
758
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
757
759
  }
758
760
  if let anomalyLine = self.section.anomalyLine {
759
761
  Text(anomalyLine)
760
- .font(.footnote)
762
+ .font(RunicFont.footnote)
761
763
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
762
764
  }
763
765
  if let anomalyDetail = self.section.anomalyDetail {
764
766
  Text(anomalyDetail)
765
- .font(.footnote)
767
+ .font(RunicFont.footnote)
766
768
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
767
769
  }
768
770
  if let reliabilityLine = self.section.reliabilityLine {
769
771
  Text(reliabilityLine)
770
- .font(.footnote)
772
+ .font(RunicFont.footnote)
771
773
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
772
774
  }
773
775
  if let reliabilityDetail = self.section.reliabilityDetail {
774
776
  Text(reliabilityDetail)
775
- .font(.footnote)
777
+ .font(RunicFont.footnote)
776
778
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
777
779
  }
778
780
  if let routingLine = self.section.routingLine {
779
781
  Text(routingLine)
780
- .font(.footnote)
782
+ .font(RunicFont.footnote)
781
783
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
782
784
  }
783
785
  if let routingDetail = self.section.routingDetail {
784
786
  Text(routingDetail)
785
- .font(.footnote)
787
+ .font(RunicFont.footnote)
786
788
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
787
789
  }
788
790
  if let updated = self.section.updatedLine {
789
791
  Text(updated)
790
- .font(.caption)
792
+ .font(RunicFont.caption)
791
793
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
792
794
  }
793
795
  if let error = self.section.errorLine {
794
796
  HStack(alignment: .top, spacing: RunicSpacing.xs) {
795
797
  Image(systemName: "exclamationmark.triangle.fill")
796
- .font(.footnote)
798
+ .font(RunicFont.footnote)
797
799
  .foregroundStyle(RunicColors.error)
798
800
  Text(error)
799
- .font(.footnote)
801
+ .font(RunicFont.footnote)
800
802
  .foregroundStyle(MenuHighlightStyle.error(self.isHighlighted))
801
803
  .lineLimit(4)
802
804
  .fixedSize(horizontal: false, vertical: true)
@@ -841,12 +843,12 @@ private struct UsageMenuMetricCard: View {
841
843
  VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
842
844
  HStack(alignment: .firstTextBaseline, spacing: RunicSpacing.xs) {
843
845
  Text(self.metric.title)
844
- .font(.caption.weight(.semibold))
846
+ .font(RunicFont.caption.weight(.semibold))
845
847
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
846
848
  Spacer(minLength: RunicSpacing.xs)
847
849
  if let reset = self.metric.resetText {
848
850
  Text(reset)
849
- .font(.caption2)
851
+ .font(RunicFont.caption2)
850
852
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
851
853
  }
852
854
  }
@@ -861,7 +863,7 @@ private struct UsageMenuMetricCard: View {
861
863
  if self.displayMode.showsPercent {
862
864
  HStack(alignment: .firstTextBaseline, spacing: RunicSpacing.xs) {
863
865
  Text(self.metric.percentLabel)
864
- .font(.system(.caption, design: .rounded))
866
+ .font(RunicFont.caption)
865
867
  .fontWeight(.medium)
866
868
  Spacer()
867
869
  }
@@ -869,7 +871,7 @@ private struct UsageMenuMetricCard: View {
869
871
 
870
872
  if let detail = self.metric.detailText {
871
873
  Text(detail)
872
- .font(.caption)
874
+ .font(RunicFont.caption)
873
875
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
874
876
  .lineLimit(1)
875
877
  .textCase(.none)
@@ -880,14 +882,12 @@ private struct UsageMenuMetricCard: View {
880
882
  RoundedRectangle(
881
883
  cornerRadius: MenuCardMetrics.metricCardCornerRadius,
882
884
  style: .continuous)
883
- .fill(Color(nsColor: .controlBackgroundColor).opacity(self.isHighlighted ? 0.2 : 0.14))
884
- )
885
+ .fill(Color(nsColor: .controlBackgroundColor).opacity(self.isHighlighted ? 0.2 : 0.14)))
885
886
  .overlay(
886
887
  RoundedRectangle(
887
888
  cornerRadius: MenuCardMetrics.metricCardCornerRadius,
888
889
  style: .continuous)
889
- .strokeBorder(Color(nsColor: .separatorColor).opacity(0.25), lineWidth: 1)
890
- )
890
+ .strokeBorder(Color(nsColor: .separatorColor).opacity(0.25), lineWidth: 1))
891
891
  }
892
892
  }
893
893
 
@@ -936,20 +936,6 @@ struct UsageMenuCardCreditsSectionView: View {
936
936
  let bottomPadding: CGFloat
937
937
  let width: CGFloat
938
938
 
939
- init(
940
- model: UsageMenuCardView.Model,
941
- showBottomDivider: Bool,
942
- topPadding: CGFloat,
943
- bottomPadding: CGFloat,
944
- width: CGFloat)
945
- {
946
- self.model = model
947
- self.showBottomDivider = showBottomDivider
948
- self.topPadding = topPadding
949
- self.bottomPadding = bottomPadding
950
- self.width = width
951
- }
952
-
953
939
  var body: some View {
954
940
  if let credits = self.model.creditsText {
955
941
  VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
@@ -996,7 +982,7 @@ private struct CreditsBarContent: View {
996
982
  var body: some View {
997
983
  VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
998
984
  Text("Credits")
999
- .font(.body)
985
+ .font(RunicFont.body)
1000
986
  .fontWeight(.medium)
1001
987
  if let percentLeft {
1002
988
  UsageProgressBar(
@@ -1005,19 +991,19 @@ private struct CreditsBarContent: View {
1005
991
  accessibilityLabel: "Credits remaining")
1006
992
  HStack(alignment: .firstTextBaseline) {
1007
993
  Text(self.creditsText)
1008
- .font(.system(.caption, design: .rounded))
994
+ .font(RunicFont.caption)
1009
995
  Spacer()
1010
996
  Text(self.scaleText)
1011
- .font(.system(.caption, design: .rounded))
997
+ .font(RunicFont.caption)
1012
998
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
1013
999
  }
1014
1000
  } else {
1015
1001
  Text(self.creditsText)
1016
- .font(.system(.caption, design: .rounded))
1002
+ .font(RunicFont.caption)
1017
1003
  }
1018
1004
  if let hintText, !hintText.isEmpty {
1019
1005
  Text(hintText)
1020
- .font(.footnote)
1006
+ .font(RunicFont.footnote)
1021
1007
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
1022
1008
  .lineLimit(4)
1023
1009
  .fixedSize(horizontal: false, vertical: true)
@@ -1061,28 +1047,28 @@ struct UsageMenuCardCostSectionView: View {
1061
1047
  if let tokenUsage = self.model.tokenUsage {
1062
1048
  VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
1063
1049
  Text("Cost")
1064
- .font(.body)
1050
+ .font(RunicFont.body)
1065
1051
  .fontWeight(.medium)
1066
1052
  Text(tokenUsage.sessionLine)
1067
- .font(.system(.caption, design: .rounded))
1053
+ .font(RunicFont.caption)
1068
1054
  if let sessionDetail = tokenUsage.sessionDetailLine {
1069
1055
  Text(sessionDetail)
1070
- .font(.footnote)
1056
+ .font(RunicFont.footnote)
1071
1057
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
1072
1058
  }
1073
1059
  Text(tokenUsage.monthLine)
1074
- .font(.system(.caption, design: .rounded))
1060
+ .font(RunicFont.caption)
1075
1061
  if let monthDetail = tokenUsage.monthDetailLine {
1076
1062
  Text(monthDetail)
1077
- .font(.footnote)
1063
+ .font(RunicFont.footnote)
1078
1064
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
1079
1065
  }
1080
1066
  Text(tokenUsage.updatedLine)
1081
- .font(.caption)
1067
+ .font(RunicFont.caption)
1082
1068
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
1083
1069
  if let hint = tokenUsage.hintLine, !hint.isEmpty {
1084
1070
  Text(hint)
1085
- .font(.footnote)
1071
+ .font(RunicFont.footnote)
1086
1072
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
1087
1073
  .lineLimit(4)
1088
1074
  .fixedSize(horizontal: false, vertical: true)
@@ -1090,10 +1076,10 @@ struct UsageMenuCardCostSectionView: View {
1090
1076
  if let error = tokenUsage.errorLine, !error.isEmpty {
1091
1077
  HStack(alignment: .top, spacing: RunicSpacing.xs) {
1092
1078
  Image(systemName: "exclamationmark.triangle.fill")
1093
- .font(.footnote)
1079
+ .font(RunicFont.footnote)
1094
1080
  .foregroundStyle(RunicColors.error)
1095
1081
  Text(error)
1096
- .font(.footnote)
1082
+ .font(RunicFont.footnote)
1097
1083
  .foregroundStyle(MenuHighlightStyle.error(self.isHighlighted))
1098
1084
  .lineLimit(4)
1099
1085
  .fixedSize(horizontal: false, vertical: true)
@@ -1496,9 +1482,9 @@ extension UsageMenuCardView.Model {
1496
1482
  }
1497
1483
 
1498
1484
  private static func observedUsageDays(_ snapshot: CostUsageTokenSnapshot) -> Int {
1499
- let nonEmptyDays = snapshot.daily.filter { entry in
1485
+ let nonEmptyDays = snapshot.daily.count(where: { entry in
1500
1486
  (entry.totalTokens ?? 0) > 0 || (entry.costUSD ?? 0) > 0
1501
- }.count
1487
+ })
1502
1488
  if nonEmptyDays > 0 {
1503
1489
  return min(30, nonEmptyDays)
1504
1490
  }
@@ -1537,8 +1523,9 @@ extension UsageMenuCardView.Model {
1537
1523
  ? input.ledgerTopProjectSpendForecast
1538
1524
  : nil
1539
1525
  let anomaly = input.ledgerAnomaly?.provider == input.provider ? input.ledgerAnomaly : nil
1540
- let hasData = daily != nil || block != nil || topModel != nil || topProject != nil || spendForecast != nil || anomaly != nil
1541
- if !hasData && (error?.isEmpty ?? true) {
1526
+ let hasData = daily != nil || block != nil || topModel != nil || topProject != nil || spendForecast != nil ||
1527
+ anomaly != nil
1528
+ if !hasData, error?.isEmpty ?? true {
1542
1529
  return nil
1543
1530
  }
1544
1531
 
@@ -1820,10 +1807,10 @@ extension UsageMenuCardView.Model {
1820
1807
  return nil
1821
1808
  }
1822
1809
 
1823
- var hash: UInt64 = 0xcbf29ce484222325
1810
+ var hash: UInt64 = 0xCBF2_9CE4_8422_2325
1824
1811
  for byte in projectID.lowercased().utf8 {
1825
1812
  hash ^= UInt64(byte)
1826
- hash = hash &* 0x100000001b3
1813
+ hash = hash &* 0x100_0000_01B3
1827
1814
  }
1828
1815
  return String(format: "%08llx", hash)
1829
1816
  }
@@ -1889,7 +1876,9 @@ private final class ClickToCopyView: NSView {
1889
1876
  fatalError("init(coder:) has not been implemented")
1890
1877
  }
1891
1878
 
1892
- override func acceptsFirstMouse(for event: NSEvent?) -> Bool { true }
1879
+ override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
1880
+ true
1881
+ }
1893
1882
 
1894
1883
  override func mouseDown(with event: NSEvent) {
1895
1884
  _ = event