@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
@@ -1,5 +1,6 @@
1
1
  import AppKit
2
2
  import RunicCore
3
+ import Security
3
4
  import SwiftUI
4
5
 
5
6
  @MainActor
@@ -9,7 +10,7 @@ struct IntegrationsPane: View {
9
10
 
10
11
  @AppStorage("vaayuAPIServerEnabled") private var vaayuAPIServerEnabled = false
11
12
  @AppStorage("vaayuAPIServerPort") private var vaayuAPIServerPort = 3000
12
- @AppStorage("vaayuAPIKey") private var vaayuAPIKey = ""
13
+ @State private var vaayuAPIKey = ""
13
14
  @AppStorage("defaultWebhookURL") private var defaultWebhookURL = ""
14
15
  @AppStorage("webhookFormat") private var webhookFormat = "slack"
15
16
  @AppStorage("githubIntegrationEnabled") private var githubIntegrationEnabled = false
@@ -28,15 +29,20 @@ struct IntegrationsPane: View {
28
29
  var isRunning: Bool
29
30
  var port: Int
30
31
 
31
- var statusIndicator: String { "●" }
32
- var statusColor: Color { self.isRunning ? .green : .red }
32
+ var statusIndicator: String {
33
+ "●"
34
+ }
35
+
36
+ var statusColor: Color {
37
+ self.isRunning ? .green : .red
38
+ }
33
39
  }
34
40
 
35
41
  var body: some View {
36
42
  PreferencesPane {
37
43
  SettingsSection(contentSpacing: PreferencesLayoutMetrics.sectionSpacing) {
38
44
  Text("vaayu Integration")
39
- .font(.caption)
45
+ .font(RunicFont.caption)
40
46
  .foregroundStyle(.secondary)
41
47
  .textCase(.uppercase)
42
48
 
@@ -49,11 +55,11 @@ struct IntegrationsPane: View {
49
55
 
50
56
  if self.vaayuAPIServerEnabled {
51
57
  Text("Running on port \(self.vaayuAPIServerPort)")
52
- .font(.footnote)
58
+ .font(RunicFont.footnote)
53
59
  .foregroundStyle(.green)
54
60
  } else {
55
61
  Text("Stopped")
56
- .font(.footnote)
62
+ .font(RunicFont.footnote)
57
63
  .foregroundStyle(.secondary)
58
64
  }
59
65
  }
@@ -61,21 +67,21 @@ struct IntegrationsPane: View {
61
67
  if self.vaayuAPIServerEnabled {
62
68
  VStack(alignment: .leading, spacing: 4) {
63
69
  Text("Port number")
64
- .font(.body)
70
+ .font(RunicFont.body)
65
71
 
66
72
  TextField("Port", value: self.$vaayuAPIServerPort, format: .number)
67
73
  .textFieldStyle(.roundedBorder)
68
74
  .frame(maxWidth: 150)
69
75
 
70
76
  Text("Default: 3000. Restart required after changing port.")
71
- .font(.footnote)
77
+ .font(RunicFont.footnote)
72
78
  .foregroundStyle(.tertiary)
73
79
  }
74
80
 
75
81
  VStack(alignment: .leading, spacing: 4) {
76
82
  HStack {
77
83
  Text("API Key")
78
- .font(.body)
84
+ .font(RunicFont.body)
79
85
 
80
86
  Spacer()
81
87
 
@@ -98,7 +104,7 @@ struct IntegrationsPane: View {
98
104
 
99
105
  if !self.vaayuAPIKey.isEmpty {
100
106
  Text(self.maskedAPIKey)
101
- .font(.footnote.monospaced())
107
+ .font(RunicFont.footnote.monospaced())
102
108
  .foregroundStyle(.secondary)
103
109
  .padding(RunicSpacing.xs)
104
110
  .background(Color(nsColor: .controlBackgroundColor))
@@ -107,14 +113,14 @@ struct IntegrationsPane: View {
107
113
 
108
114
  if let generatedKey = self.generatedAPIKey {
109
115
  Text("New key generated: \(generatedKey)")
110
- .font(.footnote)
116
+ .font(RunicFont.footnote)
111
117
  .foregroundStyle(.green)
112
118
  }
113
119
  }
114
120
  }
115
121
 
116
122
  Text("Exposes Runic usage data via REST API for external integrations.")
117
- .font(.footnote)
123
+ .font(RunicFont.footnote)
118
124
  .foregroundStyle(.tertiary)
119
125
  }
120
126
  }
@@ -123,14 +129,14 @@ struct IntegrationsPane: View {
123
129
 
124
130
  SettingsSection(contentSpacing: PreferencesLayoutMetrics.sectionSpacing) {
125
131
  Text("MCP Servers")
126
- .font(.caption)
132
+ .font(RunicFont.caption)
127
133
  .foregroundStyle(.secondary)
128
134
  .textCase(.uppercase)
129
135
 
130
136
  VStack(spacing: RunicSpacing.xs) {
131
137
  HStack {
132
138
  Text("Configured servers")
133
- .font(.body)
139
+ .font(RunicFont.body)
134
140
  Spacer()
135
141
  Button {
136
142
  self.showingAddServerSheet = true
@@ -147,10 +153,10 @@ struct IntegrationsPane: View {
147
153
  .font(.system(size: 32))
148
154
  .foregroundStyle(.tertiary)
149
155
  Text("No MCP servers yet")
150
- .font(.body.weight(.semibold))
156
+ .font(RunicFont.body.weight(.semibold))
151
157
  .foregroundStyle(.secondary)
152
158
  Text("Add a server to manage connection details")
153
- .font(.footnote)
159
+ .font(RunicFont.footnote)
154
160
  .foregroundStyle(.tertiary)
155
161
  }
156
162
  .frame(maxWidth: .infinity)
@@ -160,13 +166,13 @@ struct IntegrationsPane: View {
160
166
  HStack(spacing: RunicSpacing.sm) {
161
167
  Text(server.statusIndicator)
162
168
  .foregroundStyle(server.statusColor)
163
- .font(.title3)
169
+ .font(RunicFont.title3)
164
170
 
165
171
  VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
166
172
  Text(server.name)
167
- .font(.body)
173
+ .font(RunicFont.body)
168
174
  Text("Port \(server.port)")
169
- .font(.footnote)
175
+ .font(RunicFont.footnote)
170
176
  .foregroundStyle(.secondary)
171
177
  }
172
178
 
@@ -205,7 +211,7 @@ struct IntegrationsPane: View {
205
211
  }
206
212
 
207
213
  Text("MCP servers enable Claude Desktop and other tools to access Runic data.")
208
- .font(.footnote)
214
+ .font(RunicFont.footnote)
209
215
  .foregroundStyle(.tertiary)
210
216
  }
211
217
 
@@ -213,26 +219,26 @@ struct IntegrationsPane: View {
213
219
 
214
220
  SettingsSection(contentSpacing: PreferencesLayoutMetrics.sectionSpacing) {
215
221
  Text("Webhooks")
216
- .font(.caption)
222
+ .font(RunicFont.caption)
217
223
  .foregroundStyle(.secondary)
218
224
  .textCase(.uppercase)
219
225
 
220
226
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
221
227
  VStack(alignment: .leading, spacing: 4) {
222
228
  Text("Default webhook URL")
223
- .font(.body)
229
+ .font(RunicFont.body)
224
230
 
225
231
  TextField("https://hooks.slack.com/services/...", text: self.$defaultWebhookURL)
226
232
  .textFieldStyle(.roundedBorder)
227
233
 
228
234
  Text("Used for all alerts unless overridden in alert rule settings.")
229
- .font(.footnote)
235
+ .font(RunicFont.footnote)
230
236
  .foregroundStyle(.tertiary)
231
237
  }
232
238
 
233
239
  VStack(alignment: .leading, spacing: 4) {
234
240
  Text("Webhook format")
235
- .font(.body)
241
+ .font(RunicFont.body)
236
242
 
237
243
  Picker("", selection: self.$webhookFormat) {
238
244
  Text("Slack").tag("slack")
@@ -243,7 +249,7 @@ struct IntegrationsPane: View {
243
249
  .frame(maxWidth: 300)
244
250
 
245
251
  Text("Formats webhook payloads for different platforms.")
246
- .font(.footnote)
252
+ .font(RunicFont.footnote)
247
253
  .foregroundStyle(.tertiary)
248
254
  }
249
255
 
@@ -256,7 +262,7 @@ struct IntegrationsPane: View {
256
262
 
257
263
  if let result = self.testWebhookResult {
258
264
  Text(result)
259
- .font(.footnote)
265
+ .font(RunicFont.footnote)
260
266
  .foregroundStyle(result.contains("Success") ? .green : .red)
261
267
  }
262
268
  }
@@ -267,7 +273,7 @@ struct IntegrationsPane: View {
267
273
 
268
274
  SettingsSection(contentSpacing: PreferencesLayoutMetrics.sectionSpacing) {
269
275
  Text("GitHub Integration")
270
- .font(.caption)
276
+ .font(RunicFont.caption)
271
277
  .foregroundStyle(.secondary)
272
278
  .textCase(.uppercase)
273
279
 
@@ -280,7 +286,7 @@ struct IntegrationsPane: View {
280
286
  if self.githubIntegrationEnabled {
281
287
  VStack(alignment: .leading, spacing: 4) {
282
288
  Text("Repository path")
283
- .font(.body)
289
+ .font(RunicFont.body)
284
290
 
285
291
  HStack {
286
292
  TextField("/path/to/repo", text: self.$githubRepositoryPath)
@@ -299,7 +305,7 @@ struct IntegrationsPane: View {
299
305
  Image(systemName: "checkmark.circle.fill")
300
306
  .foregroundStyle(.green)
301
307
  Text("Valid Git repository")
302
- .font(.footnote)
308
+ .font(RunicFont.footnote)
303
309
  .foregroundStyle(.green)
304
310
  }
305
311
  } else {
@@ -307,7 +313,7 @@ struct IntegrationsPane: View {
307
313
  Image(systemName: "xmark.circle.fill")
308
314
  .foregroundStyle(.red)
309
315
  Text("Not a valid Git repository")
310
- .font(.footnote)
316
+ .font(RunicFont.footnote)
311
317
  .foregroundStyle(.red)
312
318
  }
313
319
  }
@@ -319,6 +325,22 @@ struct IntegrationsPane: View {
319
325
  }
320
326
  .onAppear {
321
327
  self.loadMCPServers()
328
+ self.vaayuAPIKey = VaayuKeychainHelper.load() ?? ""
329
+ // Migrate any existing value out of UserDefaults
330
+ let defaults = UserDefaults.standard
331
+ if let legacyKey = defaults.string(forKey: "vaayuAPIKey"), !legacyKey.isEmpty {
332
+ if VaayuKeychainHelper.save(legacyKey) {
333
+ self.vaayuAPIKey = legacyKey
334
+ defaults.removeObject(forKey: "vaayuAPIKey")
335
+ }
336
+ }
337
+ }
338
+ .onChange(of: self.vaayuAPIKey) { _, newValue in
339
+ if newValue.isEmpty {
340
+ VaayuKeychainHelper.delete()
341
+ } else {
342
+ VaayuKeychainHelper.save(newValue)
343
+ }
322
344
  }
323
345
  .sheet(isPresented: self.$showingAddServerSheet) {
324
346
  AddMCPServerSheet(
@@ -462,11 +484,11 @@ private struct AddMCPServerSheet: View {
462
484
  var body: some View {
463
485
  VStack(alignment: .leading, spacing: RunicSpacing.lg) {
464
486
  Text("Add MCP Server")
465
- .font(.title2.weight(.semibold))
487
+ .font(RunicFont.title2.weight(.semibold))
466
488
 
467
489
  VStack(alignment: .leading, spacing: RunicSpacing.xs) {
468
490
  Text("Name")
469
- .font(.subheadline.weight(.medium))
491
+ .font(RunicFont.subheadline.weight(.medium))
470
492
  TextField("Local MCP Server", text: self.$name)
471
493
  .textFieldStyle(.roundedBorder)
472
494
  .focused(self.$isNameFocused)
@@ -474,7 +496,7 @@ private struct AddMCPServerSheet: View {
474
496
 
475
497
  VStack(alignment: .leading, spacing: RunicSpacing.xs) {
476
498
  Text("Port")
477
- .font(.subheadline.weight(.medium))
499
+ .font(RunicFont.subheadline.weight(.medium))
478
500
  TextField("", value: self.$port, format: .number)
479
501
  .textFieldStyle(.roundedBorder)
480
502
  .frame(width: 140)
@@ -495,3 +517,56 @@ private struct AddMCPServerSheet: View {
495
517
  .onAppear { self.isNameFocused = true }
496
518
  }
497
519
  }
520
+
521
+ // MARK: - Vaayu API Key Keychain Helper
522
+
523
+ private enum VaayuKeychainHelper {
524
+ private static let service = "com.sriinnu.athena.Runic"
525
+ private static let account = "vaayu-api-key"
526
+
527
+ static func load() -> String? {
528
+ var result: CFTypeRef?
529
+ let query: [String: Any] = [
530
+ kSecClass as String: kSecClassGenericPassword,
531
+ kSecAttrService as String: self.service,
532
+ kSecAttrAccount as String: self.account,
533
+ kSecMatchLimit as String: kSecMatchLimitOne,
534
+ kSecReturnData as String: true,
535
+ kSecUseAuthenticationUI as String: kSecUseAuthenticationUIFail,
536
+ ]
537
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
538
+ guard status == errSecSuccess,
539
+ let data = result as? Data,
540
+ let token = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines),
541
+ !token.isEmpty
542
+ else {
543
+ return nil
544
+ }
545
+ return token
546
+ }
547
+
548
+ @discardableResult
549
+ static func save(_ value: String) -> Bool {
550
+ self.delete()
551
+ let data = Data(value.utf8)
552
+ let query: [String: Any] = [
553
+ kSecClass as String: kSecClassGenericPassword,
554
+ kSecAttrService as String: self.service,
555
+ kSecAttrAccount as String: self.account,
556
+ kSecValueData as String: data,
557
+ kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock,
558
+ ]
559
+ return SecItemAdd(query as CFDictionary, nil) == errSecSuccess
560
+ }
561
+
562
+ @discardableResult
563
+ static func delete() -> Bool {
564
+ let query: [String: Any] = [
565
+ kSecClass as String: kSecClassGenericPassword,
566
+ kSecAttrService as String: self.service,
567
+ kSecAttrAccount as String: self.account,
568
+ ]
569
+ let status = SecItemDelete(query as CFDictionary)
570
+ return status == errSecSuccess || status == errSecItemNotFound
571
+ }
572
+ }
@@ -23,17 +23,19 @@ struct PerformancePane: View {
23
23
  @State private var appeared = false
24
24
 
25
25
  enum QualityRatingFrequency: String, CaseIterable, Identifiable {
26
- case every = "every"
26
+ case every
27
27
  case over1000 = "over_1000"
28
28
  case over5000 = "over_5000"
29
29
 
30
- var id: String { self.rawValue }
30
+ var id: String {
31
+ self.rawValue
32
+ }
31
33
 
32
34
  var label: String {
33
35
  switch self {
34
- case .every: return "After every response"
35
- case .over1000: return "Only >1000 tokens"
36
- case .over5000: return "Only >5000 tokens"
36
+ case .every: "After every response"
37
+ case .over1000: "Only >1000 tokens"
38
+ case .over5000: "Only >5000 tokens"
37
39
  }
38
40
  }
39
41
  }
@@ -47,8 +49,9 @@ struct PerformancePane: View {
47
49
  binding: self.$performanceTrackingEnabled)
48
50
 
49
51
  if !self.performanceTrackingEnabled {
50
- Text("Performance tracking is disabled. Historical data is preserved but new metrics won't be collected.")
51
- .font(.footnote)
52
+ Text(
53
+ "Performance tracking is disabled. Historical data is preserved but new metrics won't be collected.")
54
+ .font(RunicFont.footnote)
52
55
  .foregroundStyle(.orange)
53
56
  .padding(.vertical, RunicSpacing.xs)
54
57
  }
@@ -59,7 +62,7 @@ struct PerformancePane: View {
59
62
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
60
63
  VStack(alignment: .leading, spacing: 4) {
61
64
  Text("Raw metrics retention")
62
- .font(.body)
65
+ .font(RunicFont.body)
63
66
 
64
67
  Picker("", selection: self.$rawMetricsRetentionDays) {
65
68
  Text("30 days").tag(30)
@@ -70,13 +73,13 @@ struct PerformancePane: View {
70
73
  .frame(maxWidth: 300)
71
74
 
72
75
  Text("How long to keep detailed latency and error records.")
73
- .font(.footnote)
76
+ .font(RunicFont.footnote)
74
77
  .foregroundStyle(.tertiary)
75
78
  }
76
79
 
77
80
  VStack(alignment: .leading, spacing: 4) {
78
81
  Text("Aggregated stats retention")
79
- .font(.body)
82
+ .font(RunicFont.body)
80
83
 
81
84
  Picker("", selection: self.$aggregatedStatsRetentionYears) {
82
85
  Text("1 year").tag(1)
@@ -87,7 +90,7 @@ struct PerformancePane: View {
87
90
  .frame(maxWidth: 300)
88
91
 
89
92
  Text("How long to keep daily performance summaries.")
90
- .font(.footnote)
93
+ .font(RunicFont.footnote)
91
94
  .foregroundStyle(.tertiary)
92
95
  }
93
96
  }
@@ -104,7 +107,7 @@ struct PerformancePane: View {
104
107
  if self.qualityRatingPromptsEnabled {
105
108
  VStack(alignment: .leading, spacing: 4) {
106
109
  Text("Prompt frequency")
107
- .font(.body)
110
+ .font(RunicFont.body)
108
111
 
109
112
  Picker("", selection: self.$qualityRatingFrequency) {
110
113
  ForEach(QualityRatingFrequency.allCases) { freq in
@@ -115,7 +118,7 @@ struct PerformancePane: View {
115
118
  .frame(maxWidth: 400)
116
119
 
117
120
  Text("When to show rating prompts based on response size.")
118
- .font(.footnote)
121
+ .font(RunicFont.footnote)
119
122
  .foregroundStyle(.tertiary)
120
123
  }
121
124
 
@@ -135,9 +138,9 @@ struct PerformancePane: View {
135
138
  VStack(alignment: .leading, spacing: RunicSpacing.sm) {
136
139
  HStack {
137
140
  Text("Current database size:")
138
- .font(.body)
141
+ .font(RunicFont.body)
139
142
  Text(self.databaseSize)
140
- .font(.body.weight(.semibold))
143
+ .font(RunicFont.body.weight(.semibold))
141
144
  .foregroundStyle(.secondary)
142
145
  }
143
146
 
@@ -157,13 +160,13 @@ struct PerformancePane: View {
157
160
 
158
161
  if let status = self.vacuumStatus {
159
162
  Text(status)
160
- .font(.footnote)
163
+ .font(RunicFont.footnote)
161
164
  .foregroundStyle(status.contains("Success") ? .green : .red)
162
165
  }
163
166
  }
164
167
 
165
168
  Text("Optimizes database file by reclaiming unused space.")
166
- .font(.footnote)
169
+ .font(RunicFont.footnote)
167
170
  .foregroundStyle(.tertiary)
168
171
 
169
172
  Divider()
@@ -185,13 +188,13 @@ struct PerformancePane: View {
185
188
 
186
189
  if let status = self.clearDataStatus {
187
190
  Text(status)
188
- .font(.footnote)
191
+ .font(RunicFont.footnote)
189
192
  .foregroundStyle(status.contains("Success") ? .green : .red)
190
193
  }
191
194
  }
192
195
 
193
196
  Text("Removes metrics older than configured retention periods.")
194
- .font(.footnote)
197
+ .font(RunicFont.footnote)
195
198
  .foregroundStyle(.tertiary)
196
199
  }
197
200
  }
@@ -204,8 +207,9 @@ struct PerformancePane: View {
204
207
  binding: self.$anonymousUsageStatsEnabled)
205
208
 
206
209
  if self.anonymousUsageStatsEnabled {
207
- Text("Only aggregate statistics are shared. Request IDs, prompts, and responses are never included.")
208
- .font(.footnote)
210
+ Text(
211
+ "Only aggregate statistics are shared. Request IDs, prompts, and responses are never included.")
212
+ .font(RunicFont.footnote)
209
213
  .foregroundStyle(.tertiary)
210
214
  .padding(.vertical, RunicSpacing.xs)
211
215
  }
@@ -223,8 +227,7 @@ struct PerformancePane: View {
223
227
  Task {
224
228
  let appSupport = FileManager.default.urls(
225
229
  for: .applicationSupportDirectory,
226
- in: .userDomainMask
227
- ).first!
230
+ in: .userDomainMask).first!
228
231
  let dbPath = appSupport.appendingPathComponent("Runic/performance.db")
229
232
 
230
233
  if let attributes = try? FileManager.default.attributesOfItem(atPath: dbPath.path),