@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
@@ -6,62 +6,62 @@ import Foundation
6
6
  /// The core philosophy: **Zero Token Leakage, Maximum Efficiency**
7
7
  enum PerformanceConstants {
8
8
  // MARK: - Animation Frame Rates
9
-
9
+
10
10
  /// Menubar icon animation (smooth on modern Macs)
11
11
  static let menubarFPS: Double = 60
12
-
12
+
13
13
  /// Popover menu animations (full detail when user is actively viewing)
14
14
  static let popoverFPS: Double = 60
15
-
15
+
16
16
  /// Status pulse effect (minimal overhead for subtle feedback)
17
17
  static let statusPulseFPS: Double = 15
18
-
18
+
19
19
  // MARK: - Data Fetching Policy
20
-
20
+
21
21
  /// **Zero Token Leakage**: Maximum pings per session
22
22
  /// - Single ping on menu open
23
23
  /// - Additional pings only if truly stale (>5min old)
24
24
  /// - Use cookies/cached data first
25
25
  static let maxPingsPerSession = 1
26
-
26
+
27
27
  /// Time before data considered stale (use cookies/cache until then)
28
28
  static let staleDuration: TimeInterval = 300 // 5 minutes
29
-
29
+
30
30
  /// Delay before first ping after menu opens
31
31
  static let menuOpenPingDelay: Duration = .seconds(1.2)
32
-
32
+
33
33
  // MARK: - Caching
34
-
34
+
35
35
  /// Icon render cache size (prevent redundant GPU work)
36
36
  static let iconCacheSize = 64
37
-
37
+
38
38
  /// Morph animation cache size
39
39
  static let morphCacheSize = 512
40
-
40
+
41
41
  // MARK: - Resource Limits
42
-
42
+
43
43
  /// Maximum concurrent provider fetches (prevent API spam)
44
44
  static let maxConcurrentFetches = 2
45
-
45
+
46
46
  /// Network timeout for single ping
47
47
  static let networkTimeout: TimeInterval = 10
48
-
48
+
49
49
  // MARK: - Animation Lifecycle
50
-
51
- /// Animation stops when:
52
- /// - Data successfully loaded
53
- /// - Error state reached (stale flag set)
54
- /// - User manually cancels
55
- /// - App enters background
56
-
50
+
51
+ // Animation stops when:
52
+ // - Data successfully loaded
53
+ // - Error state reached (stale flag set)
54
+ // - User manually cancels
55
+ // - App enters background
56
+
57
57
  /// Blink animation duration (slower "breath" to avoid flicker)
58
58
  static let blinkDuration: TimeInterval = 0.8
59
-
59
+
60
60
  /// Chance of double-blink effect
61
61
  static let doubleBlinkChance: Double = 0.05
62
-
62
+
63
63
  // MARK: - Memory Management
64
-
64
+
65
65
  /// Auto-cleanup idle resources after this duration
66
66
  static let idleCleanupDelay: TimeInterval = 60
67
67
  }
@@ -73,13 +73,13 @@ enum PerformanceConstants {
73
73
  @propertyWrapper
74
74
  struct PerformanceCritical<T> {
75
75
  private var value: T
76
-
76
+
77
77
  init(wrappedValue: T) {
78
78
  self.value = wrappedValue
79
79
  }
80
-
80
+
81
81
  var wrappedValue: T {
82
- get { value }
83
- set { value = newValue }
82
+ get { self.value }
83
+ set { self.value = newValue }
84
84
  }
85
85
  }
@@ -5,7 +5,6 @@ import SwiftUI
5
5
  /// Menu view displaying active alerts with severity indicators and acknowledgement
6
6
  @MainActor
7
7
  struct AlertsMenuView: View {
8
-
9
8
  // MARK: - Types
10
9
 
11
10
  typealias AlertEntry = AlertRuleStore.AlertHistoryEntry
@@ -49,7 +48,7 @@ struct AlertsMenuView: View {
49
48
  private var headerSection: some View {
50
49
  HStack(alignment: .firstTextBaseline) {
51
50
  Text("Alerts")
52
- .font(.headline)
51
+ .font(RunicFont.headline)
53
52
  .fontWeight(.semibold)
54
53
 
55
54
  if self.unacknowledgedCount > 0 {
@@ -67,7 +66,7 @@ struct AlertsMenuView: View {
67
66
  Task { await self.loadAlerts() }
68
67
  } label: {
69
68
  Image(systemName: "arrow.clockwise")
70
- .font(.caption)
69
+ .font(RunicFont.caption)
71
70
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
72
71
  }
73
72
  .buttonStyle(.plain)
@@ -78,17 +77,18 @@ struct AlertsMenuView: View {
78
77
 
79
78
  private var unacknowledgedBadge: some View {
80
79
  Text("\(self.unacknowledgedCount)")
81
- .font(.caption2)
80
+ .font(RunicFont.caption2)
82
81
  .fontWeight(.semibold)
83
82
  .foregroundStyle(.white)
84
83
  .padding(.horizontal, RunicSpacing.compact)
85
84
  .padding(.vertical, RunicSpacing.xxxs)
86
85
  .background(Capsule().fill(Color.red))
87
- .accessibilityLabel("\(self.unacknowledgedCount) unacknowledged alert\(self.unacknowledgedCount == 1 ? "" : "s")")
86
+ .accessibilityLabel(
87
+ "\(self.unacknowledgedCount) unacknowledged alert\(self.unacknowledgedCount == 1 ? "" : "s")")
88
88
  }
89
89
 
90
90
  private var unacknowledgedCount: Int {
91
- self.alerts.filter { !$0.acknowledged }.count
91
+ self.alerts.count(where: { !$0.acknowledged })
92
92
  }
93
93
 
94
94
  // MARK: - Alerts List
@@ -108,11 +108,11 @@ struct AlertsMenuView: View {
108
108
  private var emptyStateView: some View {
109
109
  VStack(spacing: RunicSpacing.xs) {
110
110
  Image(systemName: "checkmark.shield")
111
- .font(.title2)
111
+ .font(RunicFont.title2)
112
112
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
113
113
 
114
114
  Text("No active alerts")
115
- .font(.subheadline)
115
+ .font(RunicFont.subheadline)
116
116
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
117
117
  }
118
118
  .frame(maxWidth: .infinity)
@@ -158,13 +158,13 @@ private struct AlertRow: View {
158
158
 
159
159
  VStack(alignment: .leading, spacing: 2) {
160
160
  Text(self.alert.message)
161
- .font(.footnote)
161
+ .font(RunicFont.footnote)
162
162
  .foregroundStyle(MenuHighlightStyle.primary(self.isHighlighted))
163
163
  .lineLimit(3)
164
164
  .fixedSize(horizontal: false, vertical: true)
165
165
 
166
166
  Text(self.timeAgo(from: self.alert.triggeredAt))
167
- .font(.caption2)
167
+ .font(RunicFont.caption2)
168
168
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
169
169
  }
170
170
 
@@ -184,33 +184,31 @@ private struct AlertRow: View {
184
184
  let color = self.severityColor
185
185
 
186
186
  return Image(systemName: icon)
187
- .font(.caption)
187
+ .font(RunicFont.caption)
188
188
  .foregroundStyle(color)
189
189
  .frame(width: 16, alignment: .center)
190
190
  }
191
191
 
192
192
  private var severityIconName: String {
193
193
  switch self.alert.severity {
194
- case .info: return "info.circle.fill"
195
- case .warning: return "exclamationmark.triangle.fill"
196
- case .critical: return "exclamationmark.octagon.fill"
194
+ case .info: "info.circle.fill"
195
+ case .warning: "exclamationmark.triangle.fill"
196
+ case .critical: "exclamationmark.octagon.fill"
197
197
  }
198
198
  }
199
199
 
200
200
  private var severityColor: Color {
201
201
  switch self.alert.severity {
202
- case .info: return .green
203
- case .warning: return .yellow
204
- case .critical: return .red
202
+ case .info: .green
203
+ case .warning: .yellow
204
+ case .critical: .red
205
205
  }
206
206
  }
207
207
 
208
208
  private var backgroundStyle: some View {
209
- let baseColor = self.alert.acknowledged
209
+ self.alert.acknowledged
210
210
  ? Color(nsColor: .controlBackgroundColor).opacity(0.5)
211
211
  : Color(nsColor: .controlBackgroundColor)
212
-
213
- return baseColor
214
212
  }
215
213
 
216
214
  private var acknowledgeButton: some View {
@@ -227,7 +225,7 @@ private struct AlertRow: View {
227
225
  .frame(width: 12, height: 12)
228
226
  } else {
229
227
  Image(systemName: "checkmark.circle")
230
- .font(.caption)
228
+ .font(RunicFont.caption)
231
229
  .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
232
230
  }
233
231
  }
@@ -36,7 +36,7 @@ struct CostComparativeChartMenuView: View {
36
36
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
37
37
  if model.isEmpty {
38
38
  Text("No cost comparison data.")
39
- .font(.footnote)
39
+ .font(RunicFont.footnote)
40
40
  .foregroundStyle(.secondary)
41
41
  } else {
42
42
  Chart {
@@ -55,7 +55,7 @@ struct CostComparativeChartMenuView: View {
55
55
  AxisValueLabel {
56
56
  if let cost = value.as(Double.self) {
57
57
  Text(UsageFormatter.usdString(cost))
58
- .font(.caption2)
58
+ .font(RunicFont.caption2)
59
59
  .foregroundStyle(Color(nsColor: .tertiaryLabelColor))
60
60
  }
61
61
  }
@@ -66,7 +66,7 @@ struct CostComparativeChartMenuView: View {
66
66
  AxisGridLine().foregroundStyle(Color.clear)
67
67
  AxisTick().foregroundStyle(Color.clear)
68
68
  AxisValueLabel()
69
- .font(.caption2)
69
+ .font(RunicFont.caption2)
70
70
  .foregroundStyle(Color(nsColor: .labelColor))
71
71
  }
72
72
  }
@@ -94,14 +94,14 @@ struct CostComparativeChartMenuView: View {
94
94
  let detail = self.detailLines(model: model)
95
95
  VStack(alignment: .leading, spacing: 0) {
96
96
  Text(detail.primary)
97
- .font(.caption)
97
+ .font(RunicFont.caption)
98
98
  .foregroundStyle(.secondary)
99
99
  .lineLimit(2)
100
100
  .truncationMode(.tail)
101
101
  .frame(height: 32, alignment: .leading)
102
102
  if let secondary = detail.secondary {
103
103
  Text(secondary)
104
- .font(.caption)
104
+ .font(RunicFont.caption)
105
105
  .foregroundStyle(.secondary)
106
106
  .lineLimit(1)
107
107
  .truncationMode(.tail)
@@ -116,7 +116,7 @@ struct CostComparativeChartMenuView: View {
116
116
  .fill(Self.cheapColor)
117
117
  .frame(width: 8, height: 8)
118
118
  Text("Low cost")
119
- .font(.caption2)
119
+ .font(RunicFont.caption2)
120
120
  .foregroundStyle(.secondary)
121
121
  }
122
122
  HStack(spacing: RunicSpacing.xxs) {
@@ -124,7 +124,7 @@ struct CostComparativeChartMenuView: View {
124
124
  .fill(Self.mediumColor)
125
125
  .frame(width: 8, height: 8)
126
126
  Text("Medium cost")
127
- .font(.caption2)
127
+ .font(RunicFont.caption2)
128
128
  .foregroundStyle(.secondary)
129
129
  }
130
130
  HStack(spacing: RunicSpacing.xxs) {
@@ -132,7 +132,7 @@ struct CostComparativeChartMenuView: View {
132
132
  .fill(Self.expensiveColor)
133
133
  .frame(width: 8, height: 8)
134
134
  Text("High cost")
135
- .font(.caption2)
135
+ .font(RunicFont.caption2)
136
136
  .foregroundStyle(.secondary)
137
137
  }
138
138
  }
@@ -151,12 +151,13 @@ struct CostComparativeChartMenuView: View {
151
151
  private static func makeModel(from summaries: [UsageLedgerModelSummary]) -> [ModelCostData] {
152
152
  let withCost = summaries.compactMap { summary -> ModelCostData? in
153
153
  guard let cost = summary.totals.costUSD, cost > 0,
154
- summary.totals.totalTokens > 0 else {
154
+ summary.totals.totalTokens > 0
155
+ else {
155
156
  return nil
156
157
  }
157
158
  // Calculate average cost per token across all models
158
- let totalCost = summaries.compactMap { $0.totals.costUSD }.reduce(0, +)
159
- let totalTokens = summaries.map { $0.totals.totalTokens }.reduce(0, +)
159
+ let totalCost = summaries.compactMap(\.totals.costUSD).reduce(0, +)
160
+ let totalTokens = summaries.map(\.totals.totalTokens).reduce(0, +)
160
161
  let averageCostPerToken = totalTokens > 0 ? totalCost / Double(totalTokens) : 0
161
162
 
162
163
  return ModelCostData(
@@ -171,11 +172,11 @@ struct CostComparativeChartMenuView: View {
171
172
 
172
173
  private static func colorForExpensiveness(_ relative: Double) -> Color {
173
174
  if relative < 0.7 {
174
- return cheapColor
175
+ self.cheapColor
175
176
  } else if relative < 1.5 {
176
- return mediumColor
177
+ self.mediumColor
177
178
  } else {
178
- return expensiveColor
179
+ self.expensiveColor
179
180
  }
180
181
  }
181
182
 
@@ -255,13 +256,12 @@ struct CostComparativeChartMenuView: View {
255
256
  let costPer1M = data.costPerToken * 1_000_000
256
257
  let primary = "\(displayName): \(UsageFormatter.usdString(costPer1M)) per 1M tokens"
257
258
 
258
- let relativeText: String
259
- if data.relativeExpensiveness > 1.1 {
260
- relativeText = String(format: "%.1fx more expensive than average", data.relativeExpensiveness)
259
+ let relativeText = if data.relativeExpensiveness > 1.1 {
260
+ String(format: "%.1fx more expensive than average", data.relativeExpensiveness)
261
261
  } else if data.relativeExpensiveness < 0.9 {
262
- relativeText = String(format: "%.1fx cheaper than average", 1.0 / data.relativeExpensiveness)
262
+ String(format: "%.1fx cheaper than average", 1.0 / data.relativeExpensiveness)
263
263
  } else {
264
- relativeText = "Close to average cost"
264
+ "Close to average cost"
265
265
  }
266
266
 
267
267
  let tokens = UsageFormatter.tokenCountString(data.totalTokens)
@@ -39,7 +39,7 @@ struct CostHistoryChartMenuView: View {
39
39
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
40
40
  if model.points.isEmpty {
41
41
  Text("No cost history data.")
42
- .font(.footnote)
42
+ .font(RunicFont.footnote)
43
43
  .foregroundStyle(.secondary)
44
44
  } else {
45
45
  Chart {
@@ -64,7 +64,7 @@ struct CostHistoryChartMenuView: View {
64
64
  AxisGridLine().foregroundStyle(Color.clear)
65
65
  AxisTick().foregroundStyle(Color.clear)
66
66
  AxisValueLabel(format: .dateTime.month(.abbreviated).day())
67
- .font(.caption2)
67
+ .font(RunicFont.caption2)
68
68
  .foregroundStyle(Color(nsColor: .tertiaryLabelColor))
69
69
  }
70
70
  }
@@ -94,9 +94,9 @@ struct CostHistoryChartMenuView: View {
94
94
  if self.showHoverHint {
95
95
  HStack(spacing: RunicSpacing.xxs) {
96
96
  Image(systemName: "cursorarrow.click.2")
97
- .font(.caption2)
97
+ .font(RunicFont.caption2)
98
98
  Text("Hover to explore")
99
- .font(.caption2)
99
+ .font(RunicFont.caption2)
100
100
  }
101
101
  .foregroundStyle(.tertiary)
102
102
  .transition(.opacity)
@@ -105,13 +105,13 @@ struct CostHistoryChartMenuView: View {
105
105
  let detail = self.detailLines(model: model)
106
106
  VStack(alignment: .leading, spacing: 0) {
107
107
  Text(detail.primary)
108
- .font(.system(.caption, design: .rounded))
108
+ .font(RunicFont.caption)
109
109
  .foregroundStyle(.secondary)
110
110
  .lineLimit(1)
111
111
  .truncationMode(.tail)
112
112
  .frame(height: 16, alignment: .leading)
113
113
  Text(detail.secondary ?? " ")
114
- .font(.system(.caption, design: .rounded))
114
+ .font(RunicFont.caption)
115
115
  .foregroundStyle(.secondary)
116
116
  .lineLimit(1)
117
117
  .truncationMode(.tail)
@@ -122,7 +122,7 @@ struct CostHistoryChartMenuView: View {
122
122
 
123
123
  if let total = self.totalCostUSD {
124
124
  Text("Total (30d): \(UsageFormatter.usdString(total))")
125
- .font(.system(.caption, design: .rounded))
125
+ .font(RunicFont.caption)
126
126
  .foregroundStyle(.secondary)
127
127
  }
128
128
  }
@@ -30,7 +30,7 @@ struct CreditsHistoryChartMenuView: View {
30
30
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
31
31
  if model.points.isEmpty {
32
32
  Text("No credits history data.")
33
- .font(.footnote)
33
+ .font(RunicFont.footnote)
34
34
  .foregroundStyle(.secondary)
35
35
  } else {
36
36
  Chart {
@@ -55,7 +55,7 @@ struct CreditsHistoryChartMenuView: View {
55
55
  AxisGridLine().foregroundStyle(Color.clear)
56
56
  AxisTick().foregroundStyle(Color.clear)
57
57
  AxisValueLabel(format: .dateTime.month(.abbreviated).day())
58
- .font(.caption2)
58
+ .font(RunicFont.caption2)
59
59
  .foregroundStyle(Color(nsColor: .tertiaryLabelColor))
60
60
  }
61
61
  }
@@ -83,13 +83,13 @@ struct CreditsHistoryChartMenuView: View {
83
83
  let detail = self.detailLines(model: model)
84
84
  VStack(alignment: .leading, spacing: 0) {
85
85
  Text(detail.primary)
86
- .font(.system(.caption, design: .rounded))
86
+ .font(RunicFont.caption)
87
87
  .foregroundStyle(.secondary)
88
88
  .lineLimit(1)
89
89
  .truncationMode(.tail)
90
90
  .frame(height: 16, alignment: .leading)
91
91
  Text(detail.secondary ?? " ")
92
- .font(.system(.caption, design: .rounded))
92
+ .font(RunicFont.caption)
93
93
  .foregroundStyle(.secondary)
94
94
  .lineLimit(1)
95
95
  .truncationMode(.tail)
@@ -99,7 +99,7 @@ struct CreditsHistoryChartMenuView: View {
99
99
 
100
100
  if let total = model.totalCreditsUsed {
101
101
  Text("Total (30d): \(total.formatted(.number.precision(.fractionLength(0...2)))) credits")
102
- .font(.system(.caption, design: .rounded))
102
+ .font(RunicFont.caption)
103
103
  .foregroundStyle(.secondary)
104
104
  }
105
105
  }
@@ -11,10 +11,10 @@ struct EfficiencyMetricsMenuView: View {
11
11
 
12
12
  var label: String {
13
13
  switch self {
14
- case .model: return "Model"
15
- case .tokensPerRequest: return "Tokens/Req"
16
- case .costPerRequest: return "Cost/Req"
17
- case .cacheHitRate: return "Cache Hit %"
14
+ case .model: "Model"
15
+ case .tokensPerRequest: "Tokens/Req"
16
+ case .costPerRequest: "Cost/Req"
17
+ case .cacheHitRate: "Cache Hit %"
18
18
  }
19
19
  }
20
20
  }
@@ -65,12 +65,12 @@ struct EfficiencyMetricsMenuView: View {
65
65
 
66
66
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
67
67
  Text("Efficiency Metrics")
68
- .font(.headline)
68
+ .font(RunicFont.headline)
69
69
  .fontWeight(.semibold)
70
70
 
71
71
  if model.isEmpty {
72
72
  Text("No efficiency metrics available.")
73
- .font(.footnote)
73
+ .font(RunicFont.footnote)
74
74
  .foregroundStyle(.secondary)
75
75
  } else {
76
76
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
@@ -134,27 +134,27 @@ struct EfficiencyMetricsMenuView: View {
134
134
  HStack(spacing: RunicSpacing.lg) {
135
135
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
136
136
  Text("Total Requests")
137
- .font(.caption2)
137
+ .font(RunicFont.caption2)
138
138
  .foregroundStyle(.secondary)
139
139
  Text("\(Self.totalRequests(model))")
140
- .font(.caption)
140
+ .font(RunicFont.caption)
141
141
  .fontWeight(.medium)
142
142
  }
143
143
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
144
144
  Text("Avg Tokens/Req")
145
- .font(.caption2)
145
+ .font(RunicFont.caption2)
146
146
  .foregroundStyle(.secondary)
147
147
  Text(UsageFormatter.tokenCountString(Self.averageTokensPerRequest(model)))
148
- .font(.caption)
148
+ .font(RunicFont.caption)
149
149
  .fontWeight(.medium)
150
150
  }
151
151
  if Self.hasAnyCost(model) {
152
152
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
153
153
  Text("Avg Cost/Req")
154
- .font(.caption2)
154
+ .font(RunicFont.caption2)
155
155
  .foregroundStyle(.secondary)
156
156
  Text(UsageFormatter.usdString(Self.averageCostPerRequest(model)))
157
- .font(.caption)
157
+ .font(RunicFont.caption)
158
158
  .fontWeight(.medium)
159
159
  }
160
160
  }
@@ -181,7 +181,7 @@ struct EfficiencyMetricsMenuView: View {
181
181
  ascending: Bool) -> [ModelMetrics]
182
182
  {
183
183
  let metrics = summaries.map { ModelMetrics(summary: $0) }
184
- let sorted = metrics.sorted { lhs, rhs in
184
+ return metrics.sorted { lhs, rhs in
185
185
  let result: Bool
186
186
  switch sortBy {
187
187
  case .model:
@@ -197,7 +197,6 @@ struct EfficiencyMetricsMenuView: View {
197
197
  }
198
198
  return ascending ? result : !result
199
199
  }
200
- return sorted
201
200
  }
202
201
 
203
202
  private static func totalRequests(_ metrics: [ModelMetrics]) -> Int {
@@ -237,12 +236,12 @@ private struct SortableColumnHeader: View {
237
236
  } label: {
238
237
  HStack(spacing: RunicSpacing.xxs) {
239
238
  Text(self.title)
240
- .font(.caption)
239
+ .font(RunicFont.caption)
241
240
  .foregroundStyle(.secondary)
242
241
  .fontWeight(.medium)
243
242
  if self.currentSort == self.column {
244
243
  Image(systemName: self.ascending ? "chevron.up" : "chevron.down")
245
- .font(.caption2)
244
+ .font(RunicFont.caption2)
246
245
  .foregroundStyle(.secondary)
247
246
  }
248
247
  }
@@ -259,34 +258,34 @@ private struct MetricsRow: View {
259
258
  var body: some View {
260
259
  HStack(spacing: 0) {
261
260
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
262
- Text(UsageFormatter.modelDisplayName(metrics.modelName))
263
- .font(.caption)
261
+ Text(UsageFormatter.modelDisplayName(self.metrics.modelName))
262
+ .font(RunicFont.caption)
264
263
  .lineLimit(1)
265
264
  .truncationMode(.tail)
266
265
  if let context = UsageFormatter.modelContextLabel(for: metrics.modelName) {
267
266
  Text(context)
268
- .font(.caption2)
267
+ .font(RunicFont.caption2)
269
268
  .foregroundStyle(.secondary)
270
269
  .lineLimit(1)
271
270
  .truncationMode(.tail)
272
271
  }
273
272
  }
274
273
  .frame(width: 140, alignment: .leading)
275
- Text(UsageFormatter.tokenCountString(Int(metrics.tokensPerRequest)))
276
- .font(.caption)
274
+ Text(UsageFormatter.tokenCountString(Int(self.metrics.tokensPerRequest)))
275
+ .font(RunicFont.caption)
277
276
  .frame(width: 90, alignment: .leading)
278
277
  if let cost = metrics.costPerRequest {
279
278
  Text(UsageFormatter.usdString(cost))
280
- .font(.caption)
279
+ .font(RunicFont.caption)
281
280
  .frame(width: 80, alignment: .leading)
282
281
  } else {
283
282
  Text("—")
284
- .font(.caption)
283
+ .font(RunicFont.caption)
285
284
  .foregroundStyle(.secondary)
286
285
  .frame(width: 80, alignment: .leading)
287
286
  }
288
- Text(String(format: "%.1f%%", metrics.cacheHitRate * 100))
289
- .font(.caption)
287
+ Text(String(format: "%.1f%%", self.metrics.cacheHitRate * 100))
288
+ .font(RunicFont.caption)
290
289
  .foregroundStyle(self.cacheHitColor)
291
290
  .frame(width: 80, alignment: .leading)
292
291
  }
@@ -301,12 +300,12 @@ private struct MetricsRow: View {
301
300
  }
302
301
 
303
302
  private var cacheHitColor: Color {
304
- if metrics.cacheHitRate > 0.5 {
305
- return Color(red: 0.46, green: 0.75, blue: 0.36)
306
- } else if metrics.cacheHitRate > 0.2 {
307
- return Color(red: 0.94, green: 0.74, blue: 0.26)
303
+ if self.metrics.cacheHitRate > 0.5 {
304
+ Color(red: 0.46, green: 0.75, blue: 0.36)
305
+ } else if self.metrics.cacheHitRate > 0.2 {
306
+ Color(red: 0.94, green: 0.74, blue: 0.26)
308
307
  } else {
309
- return .secondary
308
+ .secondary
310
309
  }
311
310
  }
312
311
  }