@sriinnu/runic 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +1 -1
  3. package/README.md +47 -29
  4. package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
  5. package/RuniciOS/Views/ProviderDetailView.swift +9 -9
  6. package/RuniciOS/Views/ProviderListView.swift +10 -10
  7. package/RuniciOS/Widgets/LargeWidget.swift +14 -14
  8. package/RuniciOS/Widgets/LiveActivity.swift +19 -19
  9. package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
  10. package/RuniciOS/Widgets/MediumWidget.swift +10 -10
  11. package/RuniciOS/Widgets/RunicWidget.swift +5 -5
  12. package/RuniciOS/Widgets/SmallWidget.swift +9 -9
  13. package/Scripts/package_app.sh +2 -2
  14. package/Sources/Runic/About.swift +3 -3
  15. package/Sources/Runic/AppNotifications.swift +5 -3
  16. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +17 -4
  17. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
  18. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
  19. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
  20. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +47 -14
  21. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
  22. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
  23. package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
  24. package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
  25. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
  26. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
  27. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
  28. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +3 -6
  29. package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
  30. package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
  31. package/Sources/Runic/Core/RunicColors.swift +6 -6
  32. package/Sources/Runic/Core/RunicSpacing.swift +7 -7
  33. package/Sources/Runic/Core/RunicTypography.swift +206 -0
  34. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +10 -36
  35. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +10 -36
  36. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +10 -36
  37. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +10 -36
  38. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +10 -36
  39. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
  40. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +10 -36
  41. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +10 -36
  42. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +10 -36
  43. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +10 -36
  44. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +10 -36
  45. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +10 -36
  46. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +10 -36
  47. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +10 -36
  48. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +10 -36
  49. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +10 -36
  50. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
  51. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +10 -36
  52. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +10 -36
  53. package/Sources/Runic/Core/Stores/SettingsStore.swift +99 -52
  54. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +10 -36
  55. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
  56. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
  57. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
  58. package/Sources/Runic/Core/Stores/UsageStore.swift +234 -167
  59. package/Sources/Runic/Core/Stores/XAITokenStore.swift +10 -36
  60. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +10 -36
  61. package/Sources/Runic/Core/UsageExporter.swift +8 -4
  62. package/Sources/Runic/ProviderRegistry.swift +1 -1
  63. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
  64. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
  65. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
  66. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
  67. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
  68. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
  69. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
  70. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
  71. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
  72. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
  73. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
  74. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
  75. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
  76. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
  77. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
  78. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
  79. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
  80. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
  81. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
  82. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
  83. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
  84. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
  85. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
  86. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
  87. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
  88. package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
  89. package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
  90. package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
  91. package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
  92. package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
  93. package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
  94. package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
  95. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
  96. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
  97. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
  98. package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
  99. package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
  100. package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
  101. package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
  102. package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
  103. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
  104. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
  105. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
  106. package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
  107. package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
  108. package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
  109. package/Sources/Runic/Resources/provider-context-windows.json +20 -0
  110. package/Sources/Runic/RunicApp.swift +46 -36
  111. package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
  112. package/Sources/Runic/UsagePaceText.swift +1 -1
  113. package/Sources/Runic/UsageProgressBar.swift +13 -9
  114. package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
  115. package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
  116. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
  117. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
  118. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
  119. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
  120. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
  121. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
  122. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
  123. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +15 -20
  124. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
  125. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
  126. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
  127. package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
  128. package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
  129. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
  130. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
  131. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
  132. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +198 -58
  133. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
  134. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
  135. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
  136. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
  137. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
  138. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
  139. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +28 -76
  140. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
  141. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
  142. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
  143. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
  144. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
  145. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
  146. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
  147. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
  148. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
  149. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
  150. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
  151. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
  152. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
  153. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
  154. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +108 -33
  155. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
  156. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
  157. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
  158. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
  159. package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
  160. package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
  161. package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
  162. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
  163. package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
  164. package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
  165. package/Sources/RunicCLI/CLIEntry.swift +83 -42
  166. package/Sources/RunicCLI/CLIRenderer.swift +1 -1
  167. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
  168. package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
  169. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
  170. package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
  171. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
  172. package/Sources/RunicClaudeWebProbe/main.swift +1 -1
  173. package/Sources/RunicCore/AlertRuleStore.swift +20 -20
  174. package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
  175. package/Sources/RunicCore/CostUsageModels.swift +1 -1
  176. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
  177. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +80 -71
  178. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
  179. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
  180. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
  181. package/Sources/RunicCore/Logging/OSLogLogHandler.swift +6 -6
  182. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
  183. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
  184. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
  185. package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
  186. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
  187. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
  188. package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
  189. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
  190. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
  191. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
  192. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
  193. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
  194. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +5 -3
  195. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
  196. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
  197. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
  198. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
  199. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
  200. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
  201. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
  202. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
  203. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
  204. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +1 -0
  205. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +12 -1
  206. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
  207. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
  208. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
  209. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
  210. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
  211. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
  212. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
  213. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
  214. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
  215. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
  216. package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +1 -0
  217. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
  218. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
  219. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
  220. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
  221. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +3 -2
  222. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
  223. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
  224. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
  225. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +6 -2
  226. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
  227. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
  228. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
  229. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
  230. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
  231. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +8 -11
  232. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
  233. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
  234. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
  235. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
  236. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +25 -16
  237. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
  238. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
  239. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
  240. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
  241. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
  242. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +27 -16
  243. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
  244. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
  245. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
  246. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
  247. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
  248. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
  249. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
  250. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
  251. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
  252. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
  253. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +51 -30
  254. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
  255. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
  256. package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
  257. package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
  258. package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
  259. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
  260. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
  261. package/Sources/RunicCore/Team/TeamModels.swift +111 -110
  262. package/Sources/RunicCore/Team/TeamStore.swift +126 -109
  263. package/Sources/RunicCore/UsageFetcher.swift +2 -2
  264. package/Sources/RunicCore/UsageFormatter.swift +25 -25
  265. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +141 -29
  266. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +59 -5
  267. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +161 -0
  268. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
  269. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
  270. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
  271. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
  272. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
  273. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
  274. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
  275. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
  276. package/Tests/RunicTests/AppDelegateTests.swift +8 -3
  277. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
  278. package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
  279. package/Tests/RunicTests/CLICostTests.swift +6 -7
  280. package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
  281. package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
  282. package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
  283. package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
  284. package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
  285. package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
  286. package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
  287. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
  288. package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
  289. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
  290. package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
  291. package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
  292. package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
  293. package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
  294. package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
  295. package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
  296. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
  297. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
  298. package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
  299. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
  300. package/Tests/RunicTests/InstallOriginTests.swift +1 -2
  301. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
  302. package/Tests/RunicTests/LiveAccountTests.swift +3 -3
  303. package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
  304. package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
  305. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
  306. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
  307. package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
  308. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
  309. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
  310. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
  311. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
  312. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
  313. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
  314. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
  315. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
  316. package/Tests/RunicTests/PathBuilderTests.swift +14 -15
  317. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
  318. package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
  319. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
  320. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
  321. package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
  322. package/Tests/RunicTests/RunicTests.swift +6 -7
  323. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
  324. package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
  325. package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
  326. package/Tests/RunicTests/StatusMenuTests.swift +63 -22
  327. package/Tests/RunicTests/StatusProbeTests.swift +28 -29
  328. package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
  329. package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
  330. package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
  331. package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
  332. package/Tests/RunicTests/TextParsingTests.swift +1 -2
  333. package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
  334. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
  335. package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
  336. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
  337. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
  338. package/Tests/RunicTests/UsagePaceTests.swift +5 -6
  339. package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
  340. package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
  341. package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
  342. package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
  343. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
  344. package/appcast.xml +36 -37
  345. package/package.json +1 -1
  346. package/runic.png +0 -0
  347. package/runic.svg +15 -0
  348. package/version.env +2 -2
@@ -225,7 +225,9 @@ private enum ProviderInsightsComposer {
225
225
  help: self.budgetHelpText(topProjectSpendForecast)))
226
226
  }
227
227
 
228
- if let today = self.tokenWindowValue(tokens: tokenSnapshot?.sessionTokens, cost: tokenSnapshot?.sessionCostUSD) {
228
+ if let today = self
229
+ .tokenWindowValue(tokens: tokenSnapshot?.sessionTokens, cost: tokenSnapshot?.sessionCostUSD)
230
+ {
229
231
  rows.append(ProviderInsightLine(id: "today", label: "Today", value: today))
230
232
  }
231
233
 
@@ -247,14 +249,14 @@ private enum ProviderInsightsComposer {
247
249
  }
248
250
 
249
251
  static func coverageSummaryLabel(for provider: UsageProvider, store: UsageStore) -> String? {
250
- Self.effectiveCoverage(for: provider, store: store).summaryLabel
252
+ self.effectiveCoverage(for: provider, store: store).summaryLabel
251
253
  }
252
254
 
253
255
  static func effectiveCoverage(
254
256
  for provider: UsageProvider,
255
257
  store: UsageStore) -> ProviderUsageCoverage
256
258
  {
257
- Self.effectiveCoverage(
259
+ self.effectiveCoverage(
258
260
  provider: provider,
259
261
  metadataCoverage: store.metadata(for: provider).usageCoverage,
260
262
  topModel: store.ledgerTopModel(for: provider),
@@ -277,13 +279,13 @@ private enum ProviderInsightsComposer {
277
279
  {
278
280
  let hasModelBreakdown =
279
281
  metadataCoverage.supportsModelBreakdown
280
- || (topModel?.provider == provider)
281
- || !modelBreakdown.isEmpty
282
+ || (topModel?.provider == provider)
283
+ || !modelBreakdown.isEmpty
282
284
 
283
285
  let hasProjectAttribution =
284
286
  metadataCoverage.supportsProjectAttribution
285
- || (topProject?.provider == provider)
286
- || !projectBreakdown.isEmpty
287
+ || (topProject?.provider == provider)
288
+ || !projectBreakdown.isEmpty
287
289
 
288
290
  let hasTokenMetrics = metadataCoverage.supportsTokenMetrics
289
291
  || snapshot?.providerCost != nil
@@ -457,21 +459,21 @@ private enum ProviderInsightsComposer {
457
459
 
458
460
  private static func projectSourceLabel(_ source: UsageLedgerProjectNameSource) -> String {
459
461
  switch source {
460
- case .projectName: return "project name"
461
- case .projectID: return "project id"
462
- case .inferredFromPath: return "path-derived"
463
- case .inferredFromName: return "name-derived"
464
- case .budgetOverride: return "budget override"
465
- case .unknown: return "unknown"
462
+ case .projectName: "project name"
463
+ case .projectID: "project id"
464
+ case .inferredFromPath: "path-derived"
465
+ case .inferredFromName: "name-derived"
466
+ case .budgetOverride: "budget override"
467
+ case .unknown: "unknown"
466
468
  }
467
469
  }
468
470
 
469
471
  private static func projectConfidenceLabel(_ confidence: UsageLedgerProjectNameConfidence) -> String {
470
472
  switch confidence {
471
- case .high: return "high"
472
- case .medium: return "medium"
473
- case .low: return "low"
474
- case .none: return "none"
473
+ case .high: "high"
474
+ case .medium: "medium"
475
+ case .low: "low"
476
+ case .none: "none"
475
477
  }
476
478
  }
477
479
 
@@ -480,10 +482,10 @@ private enum ProviderInsightsComposer {
480
482
  return nil
481
483
  }
482
484
 
483
- var hash: UInt64 = 0xcbf29ce484222325
485
+ var hash: UInt64 = 0xCBF2_9CE4_8422_2325
484
486
  for byte in projectID.lowercased().utf8 {
485
487
  hash ^= UInt64(byte)
486
- hash = hash &* 0x100000001b3
488
+ hash = hash &* 0x100_0000_01B3
487
489
  }
488
490
  return String(format: "%08llx", hash)
489
491
  }
@@ -542,7 +544,8 @@ private enum ProviderInsightsComposer {
542
544
 
543
545
  private static func projectBudgetValue(_ forecast: UsageLedgerSpendForecast?) -> String? {
544
546
  guard let forecast, let limit = forecast.budgetLimitUSD, limit > 0 else { return nil }
545
- let name = self.shortProjectName(forecast.projectName?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "Top project")
547
+ let name = self
548
+ .shortProjectName(forecast.projectName?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "Top project")
546
549
  if let eta = forecast.budgetETAInDays {
547
550
  return "\(name) · \(self.budgetBreachETAText(days: eta))"
548
551
  }
@@ -574,7 +577,7 @@ private enum ProviderInsightsComposer {
574
577
  guard let forecast, let limit = forecast.budgetLimitUSD, limit > 0 else { return nil }
575
578
  var lines = [
576
579
  "Budget limit: \(UsageFormatter.usdString(limit))",
577
- "Projected 30d: \(UsageFormatter.usdString(forecast.projected30DayCostUSD))"
580
+ "Projected 30d: \(UsageFormatter.usdString(forecast.projected30DayCostUSD))",
578
581
  ]
579
582
  if let eta = forecast.budgetETAInDays {
580
583
  lines.append(self.budgetBreachETAText(days: eta))
@@ -649,7 +652,7 @@ private enum ProviderInsightsComposer {
649
652
  "Spend today: \(UsageFormatter.usdString(spend.todayValue))",
650
653
  "Baseline (\(anomaly.baselineDays)d avg): \(UsageFormatter.usdString(spend.baselineAverage))",
651
654
  "Increase: +\(percent)%",
652
- "Severity: \(spend.severity.label)"
655
+ "Severity: \(spend.severity.label)",
653
656
  ]
654
657
  if let explanation = anomaly.explanation {
655
658
  lines.append(contentsOf: explanation.details.prefix(2))
@@ -660,13 +663,12 @@ private enum ProviderInsightsComposer {
660
663
  private static func fetchHealthValue(_ attempts: [ProviderFetchAttempt]) -> String? {
661
664
  guard !attempts.isEmpty else { return nil }
662
665
  let rendered = attempts.prefix(3).map { attempt in
663
- let status: String
664
- if !attempt.wasAvailable {
665
- status = "unavailable"
666
+ let status = if !attempt.wasAvailable {
667
+ "unavailable"
666
668
  } else if self.trimmed(attempt.errorDescription) != nil {
667
- status = "failed"
669
+ "failed"
668
670
  } else {
669
- status = "ok"
671
+ "ok"
670
672
  }
671
673
  return "\(self.fetchStrategyLabel(attempt)) \(status)"
672
674
  }
@@ -712,13 +714,13 @@ private enum ProviderInsightsComposer {
712
714
 
713
715
  private static func fetchKindLabel(_ kind: ProviderFetchKind) -> String {
714
716
  switch kind {
715
- case .cli: return "cli"
716
- case .web: return "web"
717
- case .oauth: return "oauth"
718
- case .apiToken: return "api"
719
- case .api: return "api"
720
- case .localProbe: return "local"
721
- case .webDashboard: return "web"
717
+ case .cli: "cli"
718
+ case .web: "web"
719
+ case .oauth: "oauth"
720
+ case .apiToken: "api"
721
+ case .api: "api"
722
+ case .localProbe: "local"
723
+ case .webDashboard: "web"
722
724
  }
723
725
  }
724
726
 
@@ -730,7 +732,7 @@ private enum ProviderInsightsComposer {
730
732
 
731
733
  private static func windowModelsValue(_ snapshot: UsageSnapshot?) -> String? {
732
734
  guard let snapshot else { return nil }
733
- let windows = [snapshot.primary, snapshot.secondary, snapshot.tertiary].compactMap { $0 }
735
+ let windows = [snapshot.primary, snapshot.secondary, snapshot.tertiary].compactMap(\.self)
734
736
  guard !windows.isEmpty else { return nil }
735
737
 
736
738
  var seen: Set<String> = []
@@ -764,7 +766,9 @@ struct ProvidersPane: View {
764
766
  @State private var activeConfirmation: ProviderSettingsConfirmationState?
765
767
  @State private var sidebarSelection: UsageProvider?
766
768
 
767
- private var providers: [UsageProvider] { self.settings.orderedProviders() }
769
+ private var providers: [UsageProvider] {
770
+ self.settings.orderedProviders()
771
+ }
768
772
 
769
773
  var body: some View {
770
774
  Group {
@@ -835,10 +839,10 @@ struct ProvidersPane: View {
835
839
 
836
840
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
837
841
  Text("Built-in providers")
838
- .font(.subheadline.weight(.semibold))
842
+ .font(RunicFont.subheadline.weight(.semibold))
839
843
  .foregroundStyle(.primary)
840
844
  Text("Switch to sidebar layout for per-day history cards and model or project drill-down details.")
841
- .font(.caption)
845
+ .font(RunicFont.caption)
842
846
  .foregroundStyle(.secondary)
843
847
  .fixedSize(horizontal: false, vertical: true)
844
848
  }
@@ -848,14 +852,12 @@ struct ProvidersPane: View {
848
852
  .padding(ProviderListMetrics.listHeaderPadding)
849
853
  .background(
850
854
  RoundedRectangle(cornerRadius: ProviderListMetrics.listHeaderCornerRadius, style: .continuous)
851
- .fill(Color(nsColor: .controlBackgroundColor).opacity(ProviderListMetrics.listHeaderBackgroundOpacity))
852
- )
855
+ .fill(Color(nsColor: .controlBackgroundColor).opacity(ProviderListMetrics.listHeaderBackgroundOpacity)))
853
856
  .overlay(
854
857
  RoundedRectangle(cornerRadius: ProviderListMetrics.listHeaderCornerRadius, style: .continuous)
855
858
  .strokeBorder(
856
859
  Color(nsColor: .separatorColor).opacity(ProviderListMetrics.listHeaderBorderOpacity),
857
- lineWidth: 1)
858
- )
860
+ lineWidth: 1))
859
861
  .padding(.horizontal, ProviderListMetrics.contentInset)
860
862
  }
861
863
 
@@ -903,7 +905,7 @@ struct ProvidersPane: View {
903
905
  onCopyError: { text in self.copyToPasteboard(text) })
904
906
  } else {
905
907
  Text("Select a provider")
906
- .font(.title3)
908
+ .font(RunicFont.title3)
907
909
  .foregroundStyle(.secondary)
908
910
  .frame(maxWidth: .infinity, maxHeight: .infinity)
909
911
  }
@@ -1237,7 +1239,7 @@ private struct ProviderListBrandIcon: View {
1237
1239
  RoundedRectangle(cornerRadius: RunicCornerRadius.sm, style: .continuous)
1238
1240
  .fill(brandColor.opacity(0.18))
1239
1241
  Text(initial)
1240
- .font(.system(size: ProviderListMetrics.iconSize * 0.5, weight: .bold, design: .rounded))
1242
+ .font(RunicFont.system(size: ProviderListMetrics.iconSize * 0.5, weight: .bold))
1241
1243
  .foregroundStyle(brandColor)
1242
1244
  }
1243
1245
  .frame(width: ProviderListMetrics.iconSize, height: ProviderListMetrics.iconSize)
@@ -1257,7 +1259,7 @@ private struct ProviderInsightsView: View {
1257
1259
  columns: [
1258
1260
  GridItem(
1259
1261
  .adaptive(minimum: ProviderListMetrics.providerInsightsGridItemMinWidth),
1260
- spacing: ProviderListMetrics.providerInsightsChipSpacing)
1262
+ spacing: ProviderListMetrics.providerInsightsChipSpacing),
1261
1263
  ],
1262
1264
  alignment: .leading,
1263
1265
  spacing: ProviderListMetrics.providerInsightsChipSpacing)
@@ -1272,8 +1274,7 @@ private struct ProviderInsightsView: View {
1272
1274
  RoundedRectangle(
1273
1275
  cornerRadius: ProviderListMetrics.providerInsightsCardCornerRadius,
1274
1276
  style: .continuous)
1275
- .fill(Color(nsColor: .controlBackgroundColor).opacity(0.42))
1276
- )
1277
+ .fill(Color(nsColor: .controlBackgroundColor).opacity(0.42)))
1277
1278
  .overlay {
1278
1279
  RoundedRectangle(
1279
1280
  cornerRadius: ProviderListMetrics.providerInsightsCardCornerRadius,
@@ -1288,31 +1289,29 @@ private struct ProviderInsightChip: View {
1288
1289
 
1289
1290
  var body: some View {
1290
1291
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
1291
- Text(line.label.uppercased())
1292
- .font(.caption2.weight(.semibold))
1292
+ Text(self.line.label.uppercased())
1293
+ .font(RunicFont.caption2.weight(.semibold))
1293
1294
  .tracking(0.2)
1294
1295
  .foregroundStyle(.tertiary)
1295
- Text(line.value)
1296
- .font(.caption)
1296
+ Text(self.line.value)
1297
+ .font(RunicFont.caption)
1297
1298
  .foregroundStyle(.secondary)
1298
1299
  .lineLimit(2)
1299
1300
  .fixedSize(horizontal: false, vertical: true)
1300
1301
  .textSelection(.enabled)
1301
- .help(line.help ?? "")
1302
+ .help(self.line.help ?? "")
1302
1303
  }
1303
1304
  .padding(ProviderListMetrics.providerInsightsChipPadding)
1304
1305
  .frame(maxWidth: .infinity, alignment: .leading)
1305
1306
  .background(
1306
1307
  RoundedRectangle(cornerRadius: ProviderListMetrics.providerInsightsChipCornerRadius, style: .continuous)
1307
- .fill(Color(nsColor: .textBackgroundColor).opacity(0.45))
1308
- )
1308
+ .fill(Color(nsColor: .textBackgroundColor).opacity(0.45)))
1309
1309
  .overlay(
1310
1310
  RoundedRectangle(cornerRadius: ProviderListMetrics.providerInsightsChipCornerRadius, style: .continuous)
1311
- .strokeBorder(Color(nsColor: .separatorColor).opacity(0.2), lineWidth: 1)
1312
- )
1313
- .help(line.help ?? "")
1314
- .accessibilityLabel("\(line.label): \(line.value)")
1315
- .accessibilityHint(line.help ?? "")
1311
+ .strokeBorder(Color(nsColor: .separatorColor).opacity(0.2), lineWidth: 1))
1312
+ .help(self.line.help ?? "")
1313
+ .accessibilityLabel("\(self.line.label): \(self.line.value)")
1314
+ .accessibilityHint(self.line.help ?? "")
1316
1315
  }
1317
1316
  }
1318
1317
 
@@ -1350,10 +1349,10 @@ private struct ProviderListProviderRowView: View {
1350
1349
  ProviderListBrandIcon(provider: self.provider)
1351
1350
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
1352
1351
  Text(metadata.displayName)
1353
- .font(.headline.weight(.semibold))
1352
+ .font(RunicFont.headline.weight(.semibold))
1354
1353
  .foregroundStyle(.primary)
1355
1354
  Text(self.subtitle)
1356
- .font(.footnote)
1355
+ .font(RunicFont.footnote)
1357
1356
  .foregroundStyle(.secondary)
1358
1357
  .fixedSize(horizontal: false, vertical: true)
1359
1358
  }
@@ -1363,7 +1362,7 @@ private struct ProviderListProviderRowView: View {
1363
1362
  HStack(alignment: .center, spacing: RunicSpacing.xs) {
1364
1363
  self.sourceBadge
1365
1364
  Text(self.statusLabel)
1366
- .font(.caption2)
1365
+ .font(RunicFont.caption2)
1367
1366
  .foregroundStyle(.tertiary)
1368
1367
  .lineLimit(1)
1369
1368
 
@@ -1372,7 +1371,7 @@ private struct ProviderListProviderRowView: View {
1372
1371
  .controlSize(.small)
1373
1372
  .alignmentGuide(.firstTextBaseline) { d in d[VerticalAlignment.center] }
1374
1373
  Text("Refreshing…")
1375
- .font(.caption2)
1374
+ .font(RunicFont.caption2)
1376
1375
  .foregroundStyle(.secondary)
1377
1376
  } else {
1378
1377
  self.usageStatusBadge
@@ -1420,7 +1419,7 @@ private struct ProviderListProviderRowView: View {
1420
1419
  let (color, backgroundColor) = self.usageStatusColors
1421
1420
 
1422
1421
  return Text(self.usageStatus.text)
1423
- .font(.caption2.weight(.medium))
1422
+ .font(RunicFont.caption2.weight(.medium))
1424
1423
  .padding(.horizontal, ProviderListMetrics.statusBadgePaddingH)
1425
1424
  .padding(.vertical, ProviderListMetrics.statusBadgePaddingV)
1426
1425
  .background(Capsule(style: .continuous).fill(backgroundColor))
@@ -1429,24 +1428,23 @@ private struct ProviderListProviderRowView: View {
1429
1428
 
1430
1429
  private var sourceBadge: some View {
1431
1430
  Text(self.sourceLabel)
1432
- .font(.caption2.weight(.medium))
1431
+ .font(RunicFont.caption2.weight(.medium))
1433
1432
  .foregroundStyle(.secondary)
1434
1433
  .padding(.horizontal, RunicSpacing.xs)
1435
1434
  .padding(.vertical, RunicSpacing.xxxs)
1436
1435
  .background(
1437
1436
  Capsule(style: .continuous)
1438
- .fill(Color(nsColor: .controlBackgroundColor).opacity(0.7))
1439
- )
1437
+ .fill(Color(nsColor: .controlBackgroundColor).opacity(0.7)))
1440
1438
  }
1441
1439
 
1442
1440
  private var usageStatusColors: (Color, Color) {
1443
1441
  switch self.usageStatus.style {
1444
1442
  case .success:
1445
- return (.green, Color.green.opacity(0.15))
1443
+ (.green, Color.green.opacity(0.15))
1446
1444
  case .error:
1447
- return (.red, Color.red.opacity(0.15))
1445
+ (.red, Color.red.opacity(0.15))
1448
1446
  case .neutral:
1449
- return (.secondary, Color(nsColor: .controlBackgroundColor))
1447
+ (.secondary, Color(nsColor: .controlBackgroundColor))
1450
1448
  }
1451
1449
  }
1452
1450
 
@@ -1516,7 +1514,6 @@ private struct ProviderListSectionDividerView: View {
1516
1514
  }
1517
1515
 
1518
1516
  extension View {
1519
- @ViewBuilder
1520
1517
  fileprivate func providerSectionDivider(isVisible: Bool) -> some View {
1521
1518
  overlay(alignment: .bottom) {
1522
1519
  if isVisible {
@@ -1541,9 +1538,9 @@ private struct ProviderListToggleRowView: View {
1541
1538
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
1542
1539
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
1543
1540
  Text(self.toggle.title)
1544
- .font(.callout.weight(.semibold))
1541
+ .font(RunicFont.callout.weight(.semibold))
1545
1542
  Text(self.toggle.subtitle)
1546
- .font(.footnote)
1543
+ .font(RunicFont.footnote)
1547
1544
  .foregroundStyle(.secondary)
1548
1545
  .fixedSize(horizontal: false, vertical: true)
1549
1546
  }
@@ -1551,7 +1548,7 @@ private struct ProviderListToggleRowView: View {
1551
1548
  if self.toggle.binding.wrappedValue {
1552
1549
  if let status = self.toggle.statusText?(), !status.isEmpty {
1553
1550
  Text(status)
1554
- .font(.caption)
1551
+ .font(RunicFont.caption)
1555
1552
  .foregroundStyle(.secondary)
1556
1553
  .lineLimit(4)
1557
1554
  .fixedSize(horizontal: false, vertical: true)
@@ -1559,8 +1556,7 @@ private struct ProviderListToggleRowView: View {
1559
1556
  .frame(maxWidth: .infinity, alignment: .leading)
1560
1557
  .background(
1561
1558
  RoundedRectangle(cornerRadius: RunicCornerRadius.sm, style: .continuous)
1562
- .fill(Color(nsColor: .controlBackgroundColor).opacity(0.55))
1563
- )
1559
+ .fill(Color(nsColor: .controlBackgroundColor).opacity(0.55)))
1564
1560
  .overlay {
1565
1561
  RoundedRectangle(cornerRadius: RunicCornerRadius.sm, style: .continuous)
1566
1562
  .strokeBorder(Color(nsColor: .separatorColor).opacity(0.2), lineWidth: 1)
@@ -1607,7 +1603,8 @@ private struct ProviderListToggleRowView: View {
1607
1603
 
1608
1604
  private var supplementalCardBackground: some View {
1609
1605
  RoundedRectangle(cornerRadius: ProviderListMetrics.providerCardCornerRadius, style: .continuous)
1610
- .fill(Color(nsColor: .controlBackgroundColor).opacity(ProviderListMetrics.supplementalCardBackgroundOpacity))
1606
+ .fill(Color(nsColor: .controlBackgroundColor)
1607
+ .opacity(ProviderListMetrics.supplementalCardBackgroundOpacity))
1611
1608
  }
1612
1609
 
1613
1610
  private var supplementalCardBorderColor: Color {
@@ -1629,9 +1626,9 @@ private struct ProviderListFieldRowView: View {
1629
1626
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
1630
1627
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
1631
1628
  Text(self.field.title)
1632
- .font(.callout.weight(.semibold))
1629
+ .font(RunicFont.callout.weight(.semibold))
1633
1630
  Text(self.field.subtitle)
1634
- .font(.footnote)
1631
+ .font(RunicFont.footnote)
1635
1632
  .foregroundStyle(.secondary)
1636
1633
  .fixedSize(horizontal: false, vertical: true)
1637
1634
  }
@@ -1640,12 +1637,12 @@ private struct ProviderListFieldRowView: View {
1640
1637
  case .plain:
1641
1638
  TextField(self.field.placeholder ?? "", text: self.field.binding)
1642
1639
  .textFieldStyle(.roundedBorder)
1643
- .font(.callout)
1640
+ .font(RunicFont.callout)
1644
1641
  .frame(maxWidth: ProviderListMetrics.fieldMaxWidth, alignment: .leading)
1645
1642
  case .secure:
1646
1643
  SecureField(self.field.placeholder ?? "", text: self.field.binding)
1647
1644
  .textFieldStyle(.roundedBorder)
1648
- .font(.callout)
1645
+ .font(RunicFont.callout)
1649
1646
  .frame(maxWidth: ProviderListMetrics.fieldMaxWidth, alignment: .leading)
1650
1647
  }
1651
1648
 
@@ -1677,7 +1674,8 @@ private struct ProviderListFieldRowView: View {
1677
1674
 
1678
1675
  private var supplementalCardBackground: some View {
1679
1676
  RoundedRectangle(cornerRadius: ProviderListMetrics.providerCardCornerRadius, style: .continuous)
1680
- .fill(Color(nsColor: .controlBackgroundColor).opacity(ProviderListMetrics.supplementalCardBackgroundOpacity))
1677
+ .fill(Color(nsColor: .controlBackgroundColor)
1678
+ .opacity(ProviderListMetrics.supplementalCardBackgroundOpacity))
1681
1679
  }
1682
1680
 
1683
1681
  private var supplementalCardBorderColor: Color {
@@ -1697,7 +1695,7 @@ extension View {
1697
1695
  }
1698
1696
  }
1699
1697
 
1700
- private struct ProviderErrorDisplay: Sendable {
1698
+ private struct ProviderErrorDisplay {
1701
1699
  let preview: String
1702
1700
  let full: String
1703
1701
  }
@@ -1705,7 +1703,9 @@ private struct ProviderErrorDisplay: Sendable {
1705
1703
  @MainActor
1706
1704
  private struct ProviderListScrollInsetFixer: NSViewRepresentable {
1707
1705
  private final class HitTestIgnoringView: NSView {
1708
- override func hitTest(_ point: NSPoint) -> NSView? { nil }
1706
+ override func hitTest(_ point: NSPoint) -> NSView? {
1707
+ nil
1708
+ }
1709
1709
  }
1710
1710
 
1711
1711
  func makeNSView(context: Context) -> NSView {
@@ -1721,12 +1721,14 @@ private struct ProviderListScrollInsetFixer: NSViewRepresentable {
1721
1721
  let zeroInsets = NSEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
1722
1722
  let currentContentInsets = scrollView.contentInsets
1723
1723
  if currentContentInsets.top != 0 || currentContentInsets.left != 0 ||
1724
- currentContentInsets.bottom != 0 || currentContentInsets.right != 0 {
1724
+ currentContentInsets.bottom != 0 || currentContentInsets.right != 0
1725
+ {
1725
1726
  scrollView.contentInsets = zeroInsets
1726
1727
  }
1727
1728
  let currentScrollerInsets = scrollView.scrollerInsets
1728
1729
  if currentScrollerInsets.top != 0 || currentScrollerInsets.left != 0 ||
1729
- currentScrollerInsets.bottom != 0 || currentScrollerInsets.right != 0 {
1730
+ currentScrollerInsets.bottom != 0 || currentScrollerInsets.right != 0
1731
+ {
1730
1732
  scrollView.scrollerInsets = zeroInsets
1731
1733
  }
1732
1734
  }
@@ -1756,11 +1758,11 @@ private struct ProviderErrorView: View {
1756
1758
  VStack(alignment: .leading, spacing: RunicSpacing.xs) {
1757
1759
  HStack(alignment: .center, spacing: RunicSpacing.xs) {
1758
1760
  Image(systemName: "exclamationmark.triangle.fill")
1759
- .font(.caption)
1761
+ .font(RunicFont.caption)
1760
1762
  .foregroundStyle(.orange)
1761
1763
  .alignmentGuide(.firstTextBaseline) { d in d[VerticalAlignment.center] }
1762
1764
  Text(self.title)
1763
- .font(.callout.weight(.semibold))
1765
+ .font(RunicFont.callout.weight(.semibold))
1764
1766
  .foregroundStyle(.orange)
1765
1767
  Spacer()
1766
1768
  Button {
@@ -1768,9 +1770,9 @@ private struct ProviderErrorView: View {
1768
1770
  } label: {
1769
1771
  HStack(alignment: .center, spacing: RunicSpacing.xxs) {
1770
1772
  Image(systemName: "doc.on.doc")
1771
- .font(.caption)
1773
+ .font(RunicFont.caption)
1772
1774
  Text("Copy")
1773
- .font(.caption)
1775
+ .font(RunicFont.caption)
1774
1776
  }
1775
1777
  }
1776
1778
  .buttonStyle(.bordered)
@@ -1780,7 +1782,7 @@ private struct ProviderErrorView: View {
1780
1782
  }
1781
1783
 
1782
1784
  Text(self.display.preview)
1783
- .font(.callout)
1785
+ .font(RunicFont.callout)
1784
1786
  .foregroundStyle(.secondary)
1785
1787
  .lineLimit(3)
1786
1788
  .fixedSize(horizontal: false, vertical: true)
@@ -1788,19 +1790,18 @@ private struct ProviderErrorView: View {
1788
1790
  .frame(maxWidth: .infinity, alignment: .leading)
1789
1791
  .background(
1790
1792
  RoundedRectangle(cornerRadius: ProviderListMetrics.errorCardCornerRadius, style: .continuous)
1791
- .fill(Color.orange.opacity(0.08))
1792
- )
1793
+ .fill(Color.orange.opacity(0.08)))
1793
1794
 
1794
1795
  if self.display.preview != self.display.full {
1795
1796
  Button(self.isExpanded ? "Hide details" : "Show details") { self.isExpanded.toggle() }
1796
1797
  .buttonStyle(.link)
1797
- .font(.callout)
1798
+ .font(RunicFont.callout)
1798
1799
  }
1799
1800
 
1800
1801
  if self.isExpanded {
1801
1802
  ScrollView(.horizontal, showsIndicators: true) {
1802
1803
  Text(self.display.full)
1803
- .font(.system(.caption, design: .monospaced))
1804
+ .font(RunicFont.caption)
1804
1805
  .foregroundStyle(.primary)
1805
1806
  .textSelection(.enabled)
1806
1807
  .fixedSize(horizontal: false, vertical: true)
@@ -1809,19 +1810,16 @@ private struct ProviderErrorView: View {
1809
1810
  .frame(maxHeight: 200)
1810
1811
  .background(
1811
1812
  RoundedRectangle(cornerRadius: ProviderListMetrics.errorCardCornerRadius, style: .continuous)
1812
- .fill(Color(nsColor: .textBackgroundColor))
1813
- )
1813
+ .fill(Color(nsColor: .textBackgroundColor)))
1814
1814
  .overlay(
1815
1815
  RoundedRectangle(cornerRadius: ProviderListMetrics.errorCardCornerRadius, style: .continuous)
1816
- .stroke(Color.orange.opacity(0.3), lineWidth: 1)
1817
- )
1816
+ .stroke(Color.orange.opacity(0.3), lineWidth: 1))
1818
1817
  }
1819
1818
  }
1820
1819
  .padding(ProviderListMetrics.errorCardPadding)
1821
1820
  .background(
1822
1821
  RoundedRectangle(cornerRadius: ProviderListMetrics.errorCardCornerRadius, style: .continuous)
1823
- .fill(Color.orange.opacity(0.03))
1824
- )
1822
+ .fill(Color.orange.opacity(0.03)))
1825
1823
  }
1826
1824
  }
1827
1825
 
@@ -1865,7 +1863,7 @@ private struct ProviderSidebarRow: View {
1865
1863
  .frame(width: 20, height: 20)
1866
1864
  }
1867
1865
  Text(self.store.metadata(for: self.provider).displayName)
1868
- .font(.body)
1866
+ .font(RunicFont.body)
1869
1867
  .foregroundStyle(self.isEnabled ? .primary : .secondary)
1870
1868
  .lineLimit(1)
1871
1869
  .layoutPriority(1)
@@ -1901,14 +1899,15 @@ private struct ProviderSidebarSectionCard<Content: View>: View {
1901
1899
  RoundedRectangle(
1902
1900
  cornerRadius: ProviderListMetrics.sidebarCardCornerRadius,
1903
1901
  style: .continuous)
1904
- .fill(Color(nsColor: .controlBackgroundColor).opacity(ProviderListMetrics.sidebarCardBackgroundOpacity))
1905
- )
1902
+ .fill(Color(nsColor: .controlBackgroundColor)
1903
+ .opacity(ProviderListMetrics.sidebarCardBackgroundOpacity)))
1906
1904
  .overlay(
1907
1905
  RoundedRectangle(
1908
1906
  cornerRadius: ProviderListMetrics.sidebarCardCornerRadius,
1909
1907
  style: .continuous)
1910
- .strokeBorder(Color(nsColor: .separatorColor).opacity(ProviderListMetrics.sidebarCardBorderOpacity), lineWidth: 1)
1911
- )
1908
+ .strokeBorder(
1909
+ Color(nsColor: .separatorColor).opacity(ProviderListMetrics.sidebarCardBorderOpacity),
1910
+ lineWidth: 1))
1912
1911
  }
1913
1912
  }
1914
1913
 
@@ -1921,7 +1920,7 @@ private struct ProviderHistoryNavigationButton: View {
1921
1920
  var body: some View {
1922
1921
  Button(action: self.action) {
1923
1922
  Image(systemName: self.systemName)
1924
- .font(.caption.weight(.semibold))
1923
+ .font(RunicFont.caption.weight(.semibold))
1925
1924
  .foregroundStyle(self.enabled ? .secondary : .tertiary)
1926
1925
  .frame(width: 26, height: 26)
1927
1926
  }
@@ -1935,10 +1934,9 @@ private struct ProviderHistoryNavigationButton: View {
1935
1934
  .strokeBorder(
1936
1935
  Color(
1937
1936
  nsColor: .separatorColor).opacity(self.enabled ? 0.28 : 0.1),
1938
- lineWidth: 1))
1939
- )
1937
+ lineWidth: 1)))
1940
1938
  .help(self.help)
1941
- .accessibilityLabel(help)
1939
+ .accessibilityLabel(self.help)
1942
1940
  }
1943
1941
  }
1944
1942
 
@@ -1947,7 +1945,7 @@ private struct ProviderSidebarSectionHeader: View {
1947
1945
 
1948
1946
  var body: some View {
1949
1947
  Text(self.title)
1950
- .font(.subheadline.weight(.semibold))
1948
+ .font(RunicFont.subheadline.weight(.semibold))
1951
1949
  .foregroundStyle(.secondary)
1952
1950
  }
1953
1951
  }
@@ -1961,12 +1959,12 @@ private struct ProviderSidebarKeyValueRow: View {
1961
1959
  var body: some View {
1962
1960
  HStack(alignment: .firstTextBaseline, spacing: RunicSpacing.xs) {
1963
1961
  Text("\(self.label):")
1964
- .font(.caption)
1962
+ .font(RunicFont.caption)
1965
1963
  .foregroundStyle(.tertiary)
1966
1964
  .frame(width: ProviderListMetrics.sidebarStatusLabelWidth, alignment: .leading)
1967
1965
  if let helpText = self.helpText {
1968
1966
  Text(self.value)
1969
- .font(.caption)
1967
+ .font(RunicFont.caption)
1970
1968
  .foregroundStyle(.secondary)
1971
1969
  .lineLimit(2)
1972
1970
  .fixedSize(horizontal: false, vertical: true)
@@ -1974,7 +1972,7 @@ private struct ProviderSidebarKeyValueRow: View {
1974
1972
  .help(helpText)
1975
1973
  } else {
1976
1974
  Text(self.value)
1977
- .font(.caption)
1975
+ .font(RunicFont.caption)
1978
1976
  .foregroundStyle(.secondary)
1979
1977
  .lineLimit(2)
1980
1978
  .fixedSize(horizontal: false, vertical: true)
@@ -1993,11 +1991,11 @@ private struct ProviderSidebarMetricChip: View {
1993
1991
  var body: some View {
1994
1992
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
1995
1993
  Text(self.title)
1996
- .font(.caption2.weight(.semibold))
1994
+ .font(RunicFont.caption2.weight(.semibold))
1997
1995
  .foregroundStyle(.tertiary)
1998
1996
  .textCase(.uppercase)
1999
1997
  Text(self.value)
2000
- .font(.caption.weight(.medium))
1998
+ .font(RunicFont.caption.weight(.medium))
2001
1999
  .foregroundStyle(.secondary)
2002
2000
  .lineLimit(2)
2003
2001
  .fixedSize(horizontal: false, vertical: true)
@@ -2010,15 +2008,14 @@ private struct ProviderSidebarMetricChip: View {
2010
2008
  RoundedRectangle(
2011
2009
  cornerRadius: ProviderListMetrics.sidebarMicroCardCornerRadius,
2012
2010
  style: .continuous)
2013
- .fill(Color(nsColor: .textBackgroundColor).opacity(ProviderListMetrics.sidebarMicroCardBackgroundOpacity))
2014
- )
2011
+ .fill(Color(nsColor: .textBackgroundColor)
2012
+ .opacity(ProviderListMetrics.sidebarMicroCardBackgroundOpacity)))
2015
2013
  .overlay(
2016
2014
  RoundedRectangle(
2017
2015
  cornerRadius: ProviderListMetrics.sidebarMicroCardCornerRadius,
2018
2016
  style: .continuous)
2019
2017
  .strokeBorder(Color(
2020
- nsColor: .separatorColor).opacity(ProviderListMetrics.sidebarMicroCardBorderOpacity), lineWidth: 1)
2021
- )
2018
+ nsColor: .separatorColor).opacity(ProviderListMetrics.sidebarMicroCardBorderOpacity), lineWidth: 1))
2022
2019
  .help(self.helpText ?? "")
2023
2020
  }
2024
2021
  }
@@ -2027,7 +2024,9 @@ private enum ProviderDetailSubview: String, CaseIterable, Identifiable {
2027
2024
  case overview = "Overview"
2028
2025
  case history = "History"
2029
2026
 
2030
- var id: String { self.rawValue }
2027
+ var id: String {
2028
+ self.rawValue
2029
+ }
2031
2030
  }
2032
2031
 
2033
2032
  private enum ProviderHistoryMetricMode: String, CaseIterable, Identifiable {
@@ -2035,7 +2034,9 @@ private enum ProviderHistoryMetricMode: String, CaseIterable, Identifiable {
2035
2034
  case cost = "Cost"
2036
2035
  case requests = "Requests"
2037
2036
 
2038
- var id: String { self.rawValue }
2037
+ var id: String {
2038
+ self.rawValue
2039
+ }
2039
2040
  }
2040
2041
 
2041
2042
  private enum ProviderHistoryDayDetailMode: String, CaseIterable, Identifiable {
@@ -2043,7 +2044,9 @@ private enum ProviderHistoryDayDetailMode: String, CaseIterable, Identifiable {
2043
2044
  case models = "Models"
2044
2045
  case projects = "Projects"
2045
2046
 
2046
- var id: String { self.rawValue }
2047
+ var id: String {
2048
+ self.rawValue
2049
+ }
2047
2050
  }
2048
2051
 
2049
2052
  @MainActor
@@ -2059,7 +2062,7 @@ private struct ProviderHistoryCalendarDayCell: View {
2059
2062
  Button(action: self.action) {
2060
2063
  VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
2061
2064
  Text("\(self.dayNumber)")
2062
- .font(.caption.weight(self.isSelected ? .semibold : .regular))
2065
+ .font(RunicFont.caption.weight(self.isSelected ? .semibold : .regular))
2063
2066
  .foregroundStyle(self.isInMonth ? .primary : .tertiary)
2064
2067
  Spacer(minLength: 0)
2065
2068
  if self.hasActivity {
@@ -2076,12 +2079,10 @@ private struct ProviderHistoryCalendarDayCell: View {
2076
2079
  .frame(maxWidth: .infinity, minHeight: 46, alignment: .topLeading)
2077
2080
  .background(
2078
2081
  RoundedRectangle(cornerRadius: RunicCornerRadius.sm, style: .continuous)
2079
- .fill(self.backgroundColor)
2080
- )
2082
+ .fill(self.backgroundColor))
2081
2083
  .overlay(
2082
2084
  RoundedRectangle(cornerRadius: RunicCornerRadius.sm, style: .continuous)
2083
- .strokeBorder(self.borderColor, lineWidth: self.isSelected ? 1.5 : 1)
2084
- )
2085
+ .strokeBorder(self.borderColor, lineWidth: self.isSelected ? 1.5 : 1))
2085
2086
  .opacity(self.isInMonth ? 1 : 0.52)
2086
2087
  }
2087
2088
  .buttonStyle(.plain)
@@ -2142,9 +2143,9 @@ private struct ProviderSidebarDetailView: View {
2142
2143
  ProviderListBrandIcon(provider: self.provider)
2143
2144
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
2144
2145
  Text(self.store.metadata(for: self.provider).displayName)
2145
- .font(.title2.weight(.semibold))
2146
+ .font(RunicFont.title2.weight(.semibold))
2146
2147
  Text(self.subtitle)
2147
- .font(.caption)
2148
+ .font(RunicFont.caption)
2148
2149
  .foregroundStyle(.secondary)
2149
2150
  }
2150
2151
  Spacer()
@@ -2207,7 +2208,7 @@ private struct ProviderSidebarDetailView: View {
2207
2208
 
2208
2209
  if let diagnosticsCopyStatus {
2209
2210
  Text(diagnosticsCopyStatus)
2210
- .font(.caption2)
2211
+ .font(RunicFont.caption2)
2211
2212
  .foregroundStyle(.tertiary)
2212
2213
  }
2213
2214
  }
@@ -2231,11 +2232,11 @@ private struct ProviderSidebarDetailView: View {
2231
2232
  if !topModelLines.isEmpty {
2232
2233
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
2233
2234
  Text(self.modelSectionTitle)
2234
- .font(.caption2.weight(.semibold))
2235
+ .font(RunicFont.caption2.weight(.semibold))
2235
2236
  .foregroundStyle(.secondary)
2236
2237
  ForEach(Array(topModelLines.enumerated()), id: \.offset) { index, line in
2237
2238
  Text("\(index + 1). \(line)")
2238
- .font(.caption)
2239
+ .font(RunicFont.caption)
2239
2240
  .foregroundStyle(.secondary)
2240
2241
  .textSelection(.enabled)
2241
2242
  }
@@ -2246,11 +2247,11 @@ private struct ProviderSidebarDetailView: View {
2246
2247
  if !topProjectLines.isEmpty {
2247
2248
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
2248
2249
  Text("Projects")
2249
- .font(.caption2.weight(.semibold))
2250
+ .font(RunicFont.caption2.weight(.semibold))
2250
2251
  .foregroundStyle(.secondary)
2251
2252
  ForEach(Array(topProjectLines.enumerated()), id: \.offset) { index, line in
2252
2253
  Text("\(index + 1). \(line)")
2253
- .font(.caption)
2254
+ .font(RunicFont.caption)
2254
2255
  .foregroundStyle(.secondary)
2255
2256
  .textSelection(.enabled)
2256
2257
  }
@@ -2280,19 +2281,19 @@ private struct ProviderSidebarDetailView: View {
2280
2281
  ForEach(self.settingsFields) { field in
2281
2282
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
2282
2283
  Text(field.title)
2283
- .font(.body.weight(.medium))
2284
+ .font(RunicFont.body.weight(.medium))
2284
2285
  Text(field.subtitle)
2285
- .font(.caption)
2286
+ .font(RunicFont.caption)
2286
2287
  .foregroundStyle(.secondary)
2287
2288
  switch field.kind {
2288
2289
  case .plain:
2289
2290
  TextField(field.placeholder ?? "", text: field.binding)
2290
2291
  .textFieldStyle(.roundedBorder)
2291
- .font(.callout)
2292
+ .font(RunicFont.callout)
2292
2293
  case .secure:
2293
2294
  SecureField(field.placeholder ?? "", text: field.binding)
2294
2295
  .textFieldStyle(.roundedBorder)
2295
- .font(.callout)
2296
+ .font(RunicFont.callout)
2296
2297
  }
2297
2298
  self.fieldActions(field.actions)
2298
2299
  }
@@ -2310,9 +2311,9 @@ private struct ProviderSidebarDetailView: View {
2310
2311
  Toggle(isOn: toggle.binding) {
2311
2312
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
2312
2313
  Text(toggle.title)
2313
- .font(.body.weight(.medium))
2314
+ .font(RunicFont.body.weight(.medium))
2314
2315
  Text(toggle.subtitle)
2315
- .font(.caption)
2316
+ .font(RunicFont.caption)
2316
2317
  .foregroundStyle(.secondary)
2317
2318
  }
2318
2319
  }
@@ -2321,7 +2322,7 @@ private struct ProviderSidebarDetailView: View {
2321
2322
  if toggle.binding.wrappedValue {
2322
2323
  if let status = toggle.statusText?(), !status.isEmpty {
2323
2324
  Text(status)
2324
- .font(.caption)
2325
+ .font(RunicFont.caption)
2325
2326
  .foregroundStyle(.secondary)
2326
2327
  .padding(RunicSpacing.xs)
2327
2328
  .frame(maxWidth: .infinity, alignment: .leading)
@@ -2331,7 +2332,7 @@ private struct ProviderSidebarDetailView: View {
2331
2332
  style: .continuous)
2332
2333
  .fill(Color(
2333
2334
  nsColor: .controlBackgroundColor).opacity(
2334
- ProviderListMetrics.sidebarMicroCardBackgroundOpacity)))
2335
+ ProviderListMetrics.sidebarMicroCardBackgroundOpacity)))
2335
2336
  }
2336
2337
  self.toggleActions(toggle.actions)
2337
2338
  }
@@ -2364,7 +2365,7 @@ private struct ProviderSidebarDetailView: View {
2364
2365
  private var statusBadge: some View {
2365
2366
  let (color, bg) = self.statusColors
2366
2367
  return Text(self.usageStatus.text)
2367
- .font(.caption2.weight(.medium))
2368
+ .font(RunicFont.caption2.weight(.medium))
2368
2369
  .padding(.horizontal, RunicSpacing.xs)
2369
2370
  .padding(.vertical, RunicSpacing.xxs)
2370
2371
  .background(bg)
@@ -2374,9 +2375,9 @@ private struct ProviderSidebarDetailView: View {
2374
2375
 
2375
2376
  private var statusColors: (Color, Color) {
2376
2377
  switch self.usageStatus.style {
2377
- case .success: return (.green, Color.green.opacity(0.15))
2378
- case .error: return (.red, Color.red.opacity(0.15))
2379
- case .neutral: return (.secondary, Color(nsColor: .controlBackgroundColor))
2378
+ case .success: (.green, Color.green.opacity(0.15))
2379
+ case .error: (.red, Color.red.opacity(0.15))
2380
+ case .neutral: (.secondary, Color(nsColor: .controlBackgroundColor))
2380
2381
  }
2381
2382
  }
2382
2383
 
@@ -2392,19 +2393,21 @@ private struct ProviderSidebarDetailView: View {
2392
2393
  ProviderHistoryNavigationButton(
2393
2394
  systemName: "chevron.left",
2394
2395
  enabled: true,
2395
- help: "Previous month") {
2396
+ help: "Previous month")
2397
+ {
2396
2398
  self.shiftHistoryMonth(by: -1)
2397
2399
  }
2398
2400
 
2399
2401
  Text(self.historyMonthTitle)
2400
- .font(.caption.weight(.semibold))
2402
+ .font(RunicFont.caption.weight(.semibold))
2401
2403
  .foregroundStyle(.secondary)
2402
2404
  .frame(minWidth: 130, alignment: .leading)
2403
2405
 
2404
2406
  ProviderHistoryNavigationButton(
2405
2407
  systemName: "chevron.right",
2406
2408
  enabled: self.canShiftHistoryForward,
2407
- help: "Next month") {
2409
+ help: "Next month")
2410
+ {
2408
2411
  self.shiftHistoryMonth(by: 1)
2409
2412
  }
2410
2413
 
@@ -2424,14 +2427,14 @@ private struct ProviderSidebarDetailView: View {
2424
2427
  ProgressView()
2425
2428
  .controlSize(.small)
2426
2429
  Text("Loading history…")
2427
- .font(.caption)
2430
+ .font(RunicFont.caption)
2428
2431
  .foregroundStyle(.secondary)
2429
2432
  }
2430
2433
  .padding(.vertical, RunicSpacing.xs)
2431
2434
  } else if let snapshot = self.historySnapshot {
2432
2435
  if !snapshot.isSupported {
2433
2436
  Text(snapshot.note ?? "History is not available for this provider yet.")
2434
- .font(.caption)
2437
+ .font(RunicFont.caption)
2435
2438
  .foregroundStyle(.secondary)
2436
2439
  .padding(.vertical, RunicSpacing.xs)
2437
2440
  .frame(maxWidth: .infinity, alignment: .leading)
@@ -2439,16 +2442,18 @@ private struct ProviderSidebarDetailView: View {
2439
2442
  RoundedRectangle(
2440
2443
  cornerRadius: ProviderListMetrics.sidebarMicroCardCornerRadius,
2441
2444
  style: .continuous)
2442
- .fill(Color(nsColor: .controlBackgroundColor).opacity(ProviderListMetrics.sidebarCardBackgroundOpacity))
2443
- )
2445
+ .fill(Color(nsColor: .controlBackgroundColor)
2446
+ .opacity(ProviderListMetrics.sidebarCardBackgroundOpacity)))
2444
2447
  } else {
2445
2448
  if let note = snapshot.note, !note.isEmpty {
2446
2449
  Text(note)
2447
- .font(.caption2)
2450
+ .font(RunicFont.caption2)
2448
2451
  .foregroundStyle(.tertiary)
2449
2452
  }
2450
- Text(snapshot.days.count == 1 ? "1 active day in \(self.historyMonthTitle)." : "\(snapshot.days.count) active days in \(self.historyMonthTitle).")
2451
- .font(.caption2)
2453
+ Text(snapshot.days
2454
+ .count == 1 ? "1 active day in \(self.historyMonthTitle)." :
2455
+ "\(snapshot.days.count) active days in \(self.historyMonthTitle).")
2456
+ .font(RunicFont.caption2)
2452
2457
  .foregroundStyle(.secondary)
2453
2458
  .padding(.bottom, RunicSpacing.xxs)
2454
2459
 
@@ -2457,7 +2462,7 @@ private struct ProviderSidebarDetailView: View {
2457
2462
  }
2458
2463
  } else {
2459
2464
  Text("History is empty for this period.")
2460
- .font(.caption)
2465
+ .font(RunicFont.caption)
2461
2466
  .foregroundStyle(.secondary)
2462
2467
  .padding(.vertical, RunicSpacing.xs)
2463
2468
  }
@@ -2465,10 +2470,10 @@ private struct ProviderSidebarDetailView: View {
2465
2470
  if let historyError = self.historyError {
2466
2471
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
2467
2472
  Text("History load failed")
2468
- .font(.caption.weight(.semibold))
2473
+ .font(RunicFont.caption.weight(.semibold))
2469
2474
  .foregroundStyle(.red)
2470
2475
  Text(historyError)
2471
- .font(.caption)
2476
+ .font(RunicFont.caption)
2472
2477
  .foregroundStyle(.secondary)
2473
2478
  .textSelection(.enabled)
2474
2479
  Button("Retry") {
@@ -2483,18 +2488,17 @@ private struct ProviderSidebarDetailView: View {
2483
2488
  RoundedRectangle(
2484
2489
  cornerRadius: ProviderListMetrics.sidebarMicroCardCornerRadius,
2485
2490
  style: .continuous)
2486
- .fill(Color.red.opacity(0.06))
2487
- )
2491
+ .fill(Color.red.opacity(0.06)))
2488
2492
  .overlay(
2489
2493
  RoundedRectangle(
2490
2494
  cornerRadius: ProviderListMetrics.sidebarMicroCardCornerRadius,
2491
2495
  style: .continuous)
2492
- .strokeBorder(Color.red.opacity(0.28), lineWidth: 1)
2493
- )
2496
+ .strokeBorder(Color.red.opacity(0.28), lineWidth: 1))
2494
2497
  }
2495
2498
 
2496
- Text("Local aggregated history only. Prompts, cookies, API keys, and raw payloads are never shown here.")
2497
- .font(.caption2)
2499
+ Text(
2500
+ "Local aggregated history only. Prompts, cookies, API keys, and raw payloads are never shown here.")
2501
+ .font(RunicFont.caption2)
2498
2502
  .foregroundStyle(.tertiary)
2499
2503
  }
2500
2504
  }
@@ -2503,22 +2507,32 @@ private struct ProviderSidebarDetailView: View {
2503
2507
  private var historyCalendarGrid: some View {
2504
2508
  VStack(alignment: .leading, spacing: RunicSpacing.xs) {
2505
2509
  let weekdays = self.weekdaySymbols
2506
- LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: RunicSpacing.xxs), count: 7), spacing: RunicSpacing.xxs) {
2510
+ LazyVGrid(
2511
+ columns: Array(repeating: GridItem(.flexible(), spacing: RunicSpacing.xxs), count: 7),
2512
+ spacing: RunicSpacing.xxs)
2513
+ {
2507
2514
  ForEach(weekdays, id: \.self) { symbol in
2508
2515
  Text(symbol)
2509
- .font(.caption2.weight(.semibold))
2516
+ .font(RunicFont.caption2.weight(.semibold))
2510
2517
  .foregroundStyle(.tertiary)
2511
2518
  .frame(maxWidth: .infinity)
2512
2519
  }
2513
2520
  }
2514
2521
 
2515
- LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: RunicSpacing.xxs), count: 7), spacing: RunicSpacing.xxs) {
2522
+ LazyVGrid(
2523
+ columns: Array(repeating: GridItem(.flexible(), spacing: RunicSpacing.xxs), count: 7),
2524
+ spacing: RunicSpacing.xxs)
2525
+ {
2516
2526
  ForEach(self.calendarDaysForMonth, id: \.self) { day in
2517
- let inMonth = self.historyCalendar.isDate(day, equalTo: self.historyMonthStart, toGranularity: .month)
2527
+ let inMonth = self.historyCalendar.isDate(
2528
+ day,
2529
+ equalTo: self.historyMonthStart,
2530
+ toGranularity: .month)
2518
2531
  let normalizedDay = self.historyCalendar.startOfDay(for: day)
2519
2532
  let summary = self.historySummaryByDay[normalizedDay]
2520
2533
  let dayNumber = self.historyCalendar.component(.day, from: day)
2521
- let isSelected = self.historySelectedDay.map { self.historyCalendar.isDate($0, inSameDayAs: day) } ?? false
2534
+ let isSelected = self.historySelectedDay
2535
+ .map { self.historyCalendar.isDate($0, inSameDayAs: day) } ?? false
2522
2536
  ProviderHistoryCalendarDayCell(
2523
2537
  dayNumber: dayNumber,
2524
2538
  isInMonth: inMonth,
@@ -2526,7 +2540,7 @@ private struct ProviderSidebarDetailView: View {
2526
2540
  hasActivity: summary != nil,
2527
2541
  intensity: self.historyIntensity(for: summary),
2528
2542
  action: { self.historySelectedDay = normalizedDay })
2529
- .help(self.historyDayHelp(for: day, summary: summary))
2543
+ .help(self.historyDayHelp(for: day, summary: summary))
2530
2544
  }
2531
2545
  }
2532
2546
  }
@@ -2537,7 +2551,7 @@ private struct ProviderSidebarDetailView: View {
2537
2551
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
2538
2552
  HStack(alignment: .firstTextBaseline, spacing: RunicSpacing.xs) {
2539
2553
  Text("Day details")
2540
- .font(.caption.weight(.semibold))
2554
+ .font(RunicFont.caption.weight(.semibold))
2541
2555
  .foregroundStyle(.tertiary)
2542
2556
  .textCase(.uppercase)
2543
2557
  .tracking(0.25)
@@ -2556,7 +2570,7 @@ private struct ProviderSidebarDetailView: View {
2556
2570
 
2557
2571
  if let selected = self.selectedHistoryDaySummary {
2558
2572
  Text(selected.dayStart.formatted(.dateTime.weekday(.wide).month(.abbreviated).day().year()))
2559
- .font(.caption.weight(.semibold))
2573
+ .font(RunicFont.caption.weight(.semibold))
2560
2574
  .foregroundStyle(.secondary)
2561
2575
 
2562
2576
  if self.historyDayDetailMode == .summary {
@@ -2586,8 +2600,9 @@ private struct ProviderSidebarDetailView: View {
2586
2600
  }
2587
2601
 
2588
2602
  if self.hasModelBreakdown, let topModel = selected.topModel {
2589
- Text("Top model: \(self.usageLine(title: UsageFormatter.modelDisplayName(topModel.model), totals: topModel.totals, requests: topModel.entryCount, model: topModel.model))")
2590
- .font(.caption)
2603
+ Text(
2604
+ "Top model: \(self.usageLine(title: UsageFormatter.modelDisplayName(topModel.model), totals: topModel.totals, requests: topModel.entryCount, model: topModel.model))")
2605
+ .font(RunicFont.caption)
2591
2606
  .foregroundStyle(.secondary)
2592
2607
  .textSelection(.enabled)
2593
2608
  }
@@ -2595,7 +2610,7 @@ private struct ProviderSidebarDetailView: View {
2595
2610
  if self.hasProjectAttribution, let topProject = selected.topProject {
2596
2611
  let project = self.projectDisplay(topProject)
2597
2612
  Text("Top project: \(project.title)")
2598
- .font(.caption)
2613
+ .font(RunicFont.caption)
2599
2614
  .foregroundStyle(.secondary)
2600
2615
  .textSelection(.enabled)
2601
2616
  .help(project.helpText ?? "")
@@ -2605,23 +2620,28 @@ private struct ProviderSidebarDetailView: View {
2605
2620
  if self.historyDayDetailMode == .models {
2606
2621
  if self.hasModelBreakdown, !selected.modelSummaries.isEmpty {
2607
2622
  Text("Models used")
2608
- .font(.caption2.weight(.medium))
2623
+ .font(RunicFont.caption2.weight(.medium))
2609
2624
  .foregroundStyle(.secondary)
2610
- ForEach(Array(selected.modelSummaries.prefix(12).enumerated()), id: \.offset) { _, summary in
2625
+ ForEach(
2626
+ Array(selected.modelSummaries.prefix(12).enumerated()),
2627
+ id: \.offset)
2628
+ { _, summary in
2611
2629
  Text("• \(self.historyModelLine(summary))")
2612
- .font(.caption2)
2630
+ .font(RunicFont.caption2)
2613
2631
  .foregroundStyle(.tertiary)
2614
2632
  .textSelection(.enabled)
2615
2633
  .help(self.historyModelLine(summary))
2616
2634
  }
2617
2635
  } else if self.hasModelBreakdown, !selected.modelsUsed.isEmpty {
2618
2636
  Text("Models used: \(self.renderedModelsList(selected.modelsUsed).joined(separator: ", "))")
2619
- .font(.caption2)
2637
+ .font(RunicFont.caption2)
2620
2638
  .foregroundStyle(.tertiary)
2621
2639
  .textSelection(.enabled)
2622
2640
  } else {
2623
- Text(self.hasModelBreakdown ? "No models recorded for this day." : "Model attribution is not available for this provider.")
2624
- .font(.caption)
2641
+ Text(self
2642
+ .hasModelBreakdown ? "No models recorded for this day." :
2643
+ "Model attribution is not available for this provider.")
2644
+ .font(RunicFont.caption)
2625
2645
  .foregroundStyle(.tertiary)
2626
2646
  }
2627
2647
  }
@@ -2629,28 +2649,34 @@ private struct ProviderSidebarDetailView: View {
2629
2649
  if self.historyDayDetailMode == .projects {
2630
2650
  if self.hasProjectAttribution, !selected.projectSummaries.isEmpty {
2631
2651
  Text("Top projects")
2632
- .font(.caption2.weight(.medium))
2652
+ .font(RunicFont.caption2.weight(.medium))
2633
2653
  .foregroundStyle(.secondary)
2634
- ForEach(Array(selected.projectSummaries.prefix(12).enumerated()), id: \.offset) { _, summary in
2654
+ ForEach(
2655
+ Array(selected.projectSummaries.prefix(12).enumerated()),
2656
+ id: \.offset)
2657
+ { _, summary in
2635
2658
  let project = self.projectDisplay(summary)
2636
2659
  Text("• \(self.historyProjectLine(summary))")
2637
- .font(.caption2)
2660
+ .font(RunicFont.caption2)
2638
2661
  .foregroundStyle(.tertiary)
2639
2662
  .textSelection(.enabled)
2640
2663
  .help(project.helpText ?? "")
2641
2664
  }
2642
2665
  } else {
2643
- Text(self.hasProjectAttribution ? "No projects recorded for this day." : "Project attribution is not available for this provider.")
2644
- .font(.caption)
2666
+ Text(self
2667
+ .hasProjectAttribution ? "No projects recorded for this day." :
2668
+ "Project attribution is not available for this provider.")
2669
+ .font(RunicFont.caption)
2645
2670
  .foregroundStyle(.tertiary)
2646
2671
  }
2647
2672
  }
2648
2673
  } else {
2649
- Text(self.historySelectedDay?.formatted(.dateTime.weekday(.wide).month(.abbreviated).day().year()) ?? "No day selected")
2650
- .font(.caption.weight(.semibold))
2674
+ Text(self.historySelectedDay?
2675
+ .formatted(.dateTime.weekday(.wide).month(.abbreviated).day().year()) ?? "No day selected")
2676
+ .font(RunicFont.caption.weight(.semibold))
2651
2677
  .foregroundStyle(.secondary)
2652
2678
  Text("No recorded activity for this day.")
2653
- .font(.caption)
2679
+ .font(RunicFont.caption)
2654
2680
  .foregroundStyle(.tertiary)
2655
2681
  }
2656
2682
  }
@@ -2720,11 +2746,11 @@ private struct ProviderSidebarDetailView: View {
2720
2746
  private func historyMetricValue(for day: ProviderHistoryDaySnapshot) -> Double {
2721
2747
  switch self.historyMetricMode {
2722
2748
  case .tokens:
2723
- return Double(day.totals.totalTokens)
2749
+ Double(day.totals.totalTokens)
2724
2750
  case .cost:
2725
- return max(0, day.totals.costUSD ?? 0)
2751
+ max(0, day.totals.costUSD ?? 0)
2726
2752
  case .requests:
2727
- return Double(day.requestCount)
2753
+ Double(day.requestCount)
2728
2754
  }
2729
2755
  }
2730
2756
 
@@ -2887,21 +2913,21 @@ private struct ProviderSidebarDetailView: View {
2887
2913
 
2888
2914
  private func projectSourceLabel(_ source: UsageLedgerProjectNameSource) -> String {
2889
2915
  switch source {
2890
- case .projectName: return "project name"
2891
- case .projectID: return "project id"
2892
- case .inferredFromPath: return "path-derived"
2893
- case .inferredFromName: return "name-derived"
2894
- case .budgetOverride: return "budget override"
2895
- case .unknown: return "unknown"
2916
+ case .projectName: "project name"
2917
+ case .projectID: "project id"
2918
+ case .inferredFromPath: "path-derived"
2919
+ case .inferredFromName: "name-derived"
2920
+ case .budgetOverride: "budget override"
2921
+ case .unknown: "unknown"
2896
2922
  }
2897
2923
  }
2898
2924
 
2899
2925
  private func projectConfidenceLabel(_ confidence: UsageLedgerProjectNameConfidence) -> String {
2900
2926
  switch confidence {
2901
- case .high: return "high"
2902
- case .medium: return "medium"
2903
- case .low: return "low"
2904
- case .none: return "none"
2927
+ case .high: "high"
2928
+ case .medium: "medium"
2929
+ case .low: "low"
2930
+ case .none: "none"
2905
2931
  }
2906
2932
  }
2907
2933
 
@@ -2909,10 +2935,10 @@ private struct ProviderSidebarDetailView: View {
2909
2935
  guard let projectID = projectID?.trimmingCharacters(in: .whitespacesAndNewlines), !projectID.isEmpty else {
2910
2936
  return nil
2911
2937
  }
2912
- var hash: UInt64 = 0xcbf29ce484222325
2938
+ var hash: UInt64 = 0xCBF2_9CE4_8422_2325
2913
2939
  for byte in projectID.lowercased().utf8 {
2914
2940
  hash ^= UInt64(byte)
2915
- hash = hash &* 0x100000001b3
2941
+ hash = hash &* 0x100_0000_01B3
2916
2942
  }
2917
2943
  return String(format: "%08llx", hash)
2918
2944
  }
@@ -2963,17 +2989,31 @@ private struct ProviderSidebarDetailView: View {
2963
2989
  let hasModelBreakdown = self.hasModelBreakdown
2964
2990
  let hasProjectAttribution = self.hasProjectAttribution
2965
2991
 
2966
- if let today = self.tokenWindowValue(tokens: tokenSnapshot?.sessionTokens, cost: tokenSnapshot?.sessionCostUSD) {
2967
- items.append(QuickMetricItem(id: "today", title: "Today", value: today, helpText: "Session cost and tokens."))
2992
+ if let today = self
2993
+ .tokenWindowValue(tokens: tokenSnapshot?.sessionTokens, cost: tokenSnapshot?.sessionCostUSD)
2994
+ {
2995
+ items.append(QuickMetricItem(
2996
+ id: "today",
2997
+ title: "Today",
2998
+ value: today,
2999
+ helpText: "Session cost and tokens."))
2968
3000
  }
2969
3001
  if let last30 = self.tokenWindowValue(
2970
3002
  tokens: tokenSnapshot?.last30DaysTokens,
2971
3003
  cost: tokenSnapshot?.last30DaysCostUSD)
2972
3004
  {
2973
- items.append(QuickMetricItem(id: "30d", title: "30d", value: last30, helpText: "Last 30 days cost and tokens."))
3005
+ items.append(QuickMetricItem(
3006
+ id: "30d",
3007
+ title: "30d",
3008
+ value: last30,
3009
+ helpText: "Last 30 days cost and tokens."))
2974
3010
  }
2975
3011
  if let spend = self.providerSpendValue(snapshot?.providerCost) {
2976
- items.append(QuickMetricItem(id: "spend", title: "Spend", value: spend, helpText: "Provider billing usage."))
3012
+ items.append(QuickMetricItem(
3013
+ id: "spend",
3014
+ title: "Spend",
3015
+ value: spend,
3016
+ helpText: "Provider billing usage."))
2977
3017
  }
2978
3018
  if hasModelBreakdown, let topModel = self.store.ledgerTopModel(for: self.provider) {
2979
3019
  let modelName = UsageFormatter.modelDisplayName(topModel.model)
@@ -3176,7 +3216,7 @@ private struct ProviderSidebarDetailView: View {
3176
3216
 
3177
3217
  private func labeledQuotaWindows(from snapshot: UsageSnapshot?) -> [(label: String, window: RateWindow)] {
3178
3218
  guard let snapshot else { return [] }
3179
- let windows = [snapshot.primary, snapshot.secondary, snapshot.tertiary].compactMap { $0 }
3219
+ let windows = [snapshot.primary, snapshot.secondary, snapshot.tertiary].compactMap(\.self)
3180
3220
  var seen: Set<String> = []
3181
3221
  var labeled: [(label: String, window: RateWindow)] = []
3182
3222
 
@@ -3246,7 +3286,9 @@ private struct ProviderSidebarDetailView: View {
3246
3286
 
3247
3287
  private func retryCount(from attempts: [ProviderFetchAttempt]) -> Int {
3248
3288
  guard !attempts.isEmpty else { return 0 }
3249
- if let successIndex = attempts.firstIndex(where: { $0.wasAvailable && self.trimmed($0.errorDescription) == nil }) {
3289
+ if let successIndex = attempts
3290
+ .firstIndex(where: { $0.wasAvailable && self.trimmed($0.errorDescription) == nil })
3291
+ {
3250
3292
  return max(0, successIndex)
3251
3293
  }
3252
3294
  return max(0, attempts.count - 1)
@@ -3267,13 +3309,13 @@ private struct ProviderSidebarDetailView: View {
3267
3309
 
3268
3310
  private static func fetchKindLabel(_ kind: ProviderFetchKind) -> String {
3269
3311
  switch kind {
3270
- case .cli: return "cli"
3271
- case .web: return "web"
3272
- case .oauth: return "oauth"
3273
- case .apiToken: return "api"
3274
- case .api: return "api"
3275
- case .localProbe: return "local"
3276
- case .webDashboard: return "web"
3312
+ case .cli: "cli"
3313
+ case .web: "web"
3314
+ case .oauth: "oauth"
3315
+ case .apiToken: "api"
3316
+ case .api: "api"
3317
+ case .localProbe: "local"
3318
+ case .webDashboard: "web"
3277
3319
  }
3278
3320
  }
3279
3321
 
@@ -3321,9 +3363,13 @@ private struct ProviderSidebarDetailView: View {
3321
3363
  lines.append("- primary_resets_at: \(iso.string(from: reset))")
3322
3364
  }
3323
3365
  if let cost = snapshot.providerCost {
3324
- lines.append("- provider_spend_used: \(UsageFormatter.currencyString(cost.used, currencyCode: cost.currencyCode))")
3366
+ lines
3367
+ .append(
3368
+ "- provider_spend_used: \(UsageFormatter.currencyString(cost.used, currencyCode: cost.currencyCode))")
3325
3369
  if cost.limit > 0 {
3326
- lines.append("- provider_spend_limit: \(UsageFormatter.currencyString(cost.limit, currencyCode: cost.currencyCode))")
3370
+ lines
3371
+ .append(
3372
+ "- provider_spend_limit: \(UsageFormatter.currencyString(cost.limit, currencyCode: cost.currencyCode))")
3327
3373
  }
3328
3374
  if let period = self.trimmed(cost.period) {
3329
3375
  lines.append("- provider_spend_period: \(period)")
@@ -3337,13 +3383,12 @@ private struct ProviderSidebarDetailView: View {
3337
3383
  lines.append("- none")
3338
3384
  } else {
3339
3385
  for attempt in attempts {
3340
- let state: String
3341
- if !attempt.wasAvailable {
3342
- state = "unavailable"
3386
+ let state = if !attempt.wasAvailable {
3387
+ "unavailable"
3343
3388
  } else if let error = self.trimmed(attempt.errorDescription) {
3344
- state = "failed: \(error)"
3389
+ "failed: \(error)"
3345
3390
  } else {
3346
- state = "ok"
3391
+ "ok"
3347
3392
  }
3348
3393
  lines.append("- \(attempt.strategyID) [\(Self.fetchKindLabel(attempt.kind))] \(state)")
3349
3394
  }