@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,12 +1,12 @@
1
1
  import AppKit
2
- import RunicCore
3
2
  import Observation
3
+ import RunicCore
4
4
  import SwiftUI
5
5
 
6
6
  // MARK: - NSMenu construction
7
7
 
8
8
  extension StatusItemController {
9
- static let menuCardBaseWidth: CGFloat = 310
9
+ static let menuCardBaseWidth: CGFloat = 340
10
10
  private static let menuOpenPingDelay: Duration = PerformanceConstants.menuOpenPingDelay
11
11
 
12
12
  func menuCardWidth(for providers: [UsageProvider], menu: NSMenu? = nil) -> CGFloat {
@@ -148,8 +148,9 @@ extension StatusItemController {
148
148
  width: menuWidth,
149
149
  menu: menu)
150
150
  if let tabBarView {
151
- let hosting = MenuHostingView(rootView: tabBarView)
152
- let controller = NSHostingController(rootView: tabBarView)
151
+ let monoTabBar = tabBarView.runicTypography()
152
+ let hosting = MenuHostingView(rootView: monoTabBar)
153
+ let controller = NSHostingController(rootView: monoTabBar)
153
154
  let size = controller.sizeThatFits(in: CGSize(width: menuWidth, height: .greatestFiniteMagnitude))
154
155
  hosting.frame = NSRect(origin: .zero, size: NSSize(width: menuWidth, height: size.height))
155
156
  let tabItem = NSMenuItem()
@@ -172,8 +173,9 @@ extension StatusItemController {
172
173
  let overviewView = self.makeOverviewView(
173
174
  providers: enabledProviders,
174
175
  width: menuWidth)
175
- let hosting = MenuHostingView(rootView: overviewView)
176
- let controller = NSHostingController(rootView: overviewView)
176
+ let monoOverview = overviewView.runicTypography()
177
+ let hosting = MenuHostingView(rootView: monoOverview)
178
+ let controller = NSHostingController(rootView: monoOverview)
177
179
  let size = controller.sizeThatFits(in: CGSize(width: menuWidth, height: .greatestFiniteMagnitude))
178
180
  hosting.frame = NSRect(origin: .zero, size: NSSize(width: menuWidth, height: size.height))
179
181
  let overviewItem = NSMenuItem()
@@ -266,10 +268,10 @@ extension StatusItemController {
266
268
  let item = NSMenuItem(title: text, action: nil, keyEquivalent: "")
267
269
  item.isEnabled = false
268
270
  if style == .headline {
269
- let font = NSFont.systemFont(ofSize: NSFont.systemFontSize, weight: .semibold)
271
+ let font = RunicFont.nsFont(size: NSFont.systemFontSize, weight: .semibold)
270
272
  item.attributedTitle = NSAttributedString(string: text, attributes: [.font: font])
271
273
  } else if style == .secondary {
272
- let font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize)
274
+ let font = RunicFont.nsFont(size: NSFont.smallSystemFontSize)
273
275
  item.attributedTitle = NSAttributedString(
274
276
  string: text,
275
277
  attributes: [.font: font, .foregroundColor: NSColor.secondaryLabelColor])
@@ -306,6 +308,8 @@ extension StatusItemController {
306
308
  menu.addItem(.separator())
307
309
  }
308
310
  }
311
+
312
+ self.applyRunicFont(to: menu)
309
313
  }
310
314
 
311
315
  func makeMenu(for provider: UsageProvider?) -> NSMenu {
@@ -432,6 +436,13 @@ extension StatusItemController {
432
436
  totalToday += todayTokens
433
437
 
434
438
  let resetDesc = snapshot?.primary.resetDescription
439
+ let windowLabel = snapshot?.primary.label?.trimmingCharacters(in: .whitespacesAndNewlines)
440
+ let topModel = self.store.ledgerTopModel(for: provider)
441
+ let topModelContext: String? = if let model = topModel?.model {
442
+ UsageFormatter.modelContextLabel(for: model)
443
+ } else {
444
+ Self.defaultContextLabel(for: provider)
445
+ }
435
446
 
436
447
  summaries.append(OverviewMenuView.ProviderSummary(
437
448
  id: provider.rawValue,
@@ -441,7 +452,9 @@ extension StatusItemController {
441
452
  usedPercent: usedPercent,
442
453
  todayTokens: todayTokens,
443
454
  brandColor: brandColor,
444
- resetDescription: resetDesc))
455
+ resetDescription: resetDesc,
456
+ windowLabel: windowLabel,
457
+ topModelContext: topModelContext))
445
458
 
446
459
  // Chart data — last 7 days
447
460
  let dailySummaries = self.store.ledgerAllDailySummary(for: provider)
@@ -450,7 +463,8 @@ extension StatusItemController {
450
463
  id: "\(provider.rawValue)-\(summary.dayKey)",
451
464
  date: summary.dayStart,
452
465
  tokens: summary.totals.totalTokens,
453
- provider: meta.displayName))
466
+ provider: meta.displayName,
467
+ color: brandColor))
454
468
  }
455
469
  }
456
470
 
@@ -461,6 +475,7 @@ extension StatusItemController {
461
475
  summaries: activeSummaries.isEmpty ? summaries : activeSummaries,
462
476
  chartPoints: chartPoints,
463
477
  totalTodayTokens: totalToday,
478
+ totalProviders: UsageProvider.allCases.count,
464
479
  width: width)
465
480
  }
466
481
 
@@ -477,6 +492,23 @@ extension StatusItemController {
477
492
  return abbreviations[name] ?? String(name.prefix(6))
478
493
  }
479
494
 
495
+ /// Default flagship model context window per provider, loaded from
496
+ /// `Resources/provider-context-windows.json`. Update the JSON to add/change
497
+ /// context windows without recompiling.
498
+ private static let defaultContextWindows: [String: Int] = {
499
+ struct Entry: Decodable { let contextK: Int }
500
+ guard let url = Bundle.main.url(forResource: "provider-context-windows", withExtension: "json"),
501
+ let data = try? Data(contentsOf: url),
502
+ let dict = try? JSONDecoder().decode([String: Entry].self, from: data)
503
+ else { return [:] }
504
+ return dict.mapValues(\.contextK)
505
+ }()
506
+
507
+ private static func defaultContextLabel(for provider: UsageProvider) -> String? {
508
+ guard let k = self.defaultContextWindows[provider.rawValue] else { return nil }
509
+ return k >= 1000 ? "ctx \(k / 1000)M" : "ctx \(k)K"
510
+ }
511
+
480
512
  private func resolvedMenuProvider() -> UsageProvider? {
481
513
  let enabled = self.store.enabledProviders()
482
514
  if enabled.isEmpty { return .codex }
@@ -650,7 +682,7 @@ extension StatusItemController {
650
682
  }
651
683
 
652
684
  private func isHostedSubviewMenu(_ menu: NSMenu) -> Bool {
653
- let ids: Set<String> = [
685
+ let ids: Set = [
654
686
  "usageBreakdownChart",
655
687
  "creditsHistoryChart",
656
688
  "costHistoryChart",
@@ -810,7 +842,7 @@ private final class ProviderSwitcherView: NSView {
810
842
  button.bezelStyle = .regularSquare
811
843
  button.isBordered = false
812
844
  button.controlSize = .small
813
- button.font = NSFont.systemFont(ofSize: 12, weight: .medium)
845
+ button.font = RunicFont.nsFont(size: 12, weight: .medium)
814
846
  button.setButtonType(.toggle)
815
847
  button.contentTintColor = self.unselectedTextColor
816
848
  button.alignment = .center
@@ -1196,7 +1228,7 @@ private final class ProviderSwitcherView: NSView {
1196
1228
  if self.stackedIcons,
1197
1229
  self.segments.indices.contains(index)
1198
1230
  {
1199
- let font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize)
1231
+ let font = RunicFont.nsFont(size: NSFont.smallSystemFontSize)
1200
1232
  let titleWidth = ceil((self.segments[index].title as NSString).size(withAttributes: [.font: font])
1201
1233
  .width)
1202
1234
  let contentPadding: CGFloat = 6 + 6
@@ -1330,7 +1362,8 @@ private final class ProviderSwitcherView: NSView {
1330
1362
 
1331
1363
  let track = NSView()
1332
1364
  track.wantsLayer = true
1333
- track.layer?.backgroundColor = NSColor.tertiaryLabelColor.withAlphaComponent(CGFloat(RunicColors.Opacity.medium)).cgColor
1365
+ track.layer?.backgroundColor = NSColor.tertiaryLabelColor
1366
+ .withAlphaComponent(CGFloat(RunicColors.Opacity.medium)).cgColor
1334
1367
  track.layer?.cornerRadius = 3
1335
1368
  track.layer?.masksToBounds = true
1336
1369
  track.translatesAutoresizingMaskIntoConstraints = false
@@ -64,8 +64,8 @@ extension StatusItemController {
64
64
  {
65
65
  let menuMode = self.settings.menuMode
66
66
  let includeSummarySections = menuMode != .glance
67
- let includeInsights = menuMode == .`operator`
68
- let includeActions = menuMode == .`operator`
67
+ let includeInsights = menuMode == .operator
68
+ let includeActions = menuMode == .operator
69
69
  let hasUsageBlock = !model.metrics.isEmpty || model.placeholder != nil
70
70
  let hasCredits = includeSummarySections && model.creditsText != nil
71
71
  let hasExtraUsage = includeSummarySections && model.providerCost != nil
@@ -282,7 +282,9 @@ extension StatusItemController {
282
282
  formatter.dateFormat = "yyyy-MM-dd"
283
283
  formatter.timeZone = TimeZone.current
284
284
  var tokensByDay: [String: Int] = [:]
285
- for d in allDaily { tokensByDay[d.dayKey, default: 0] += d.totals.totalTokens }
285
+ for d in allDaily {
286
+ tokensByDay[d.dayKey, default: 0] += d.totals.totalTokens
287
+ }
286
288
  return (0..<7).map { offset in
287
289
  guard let date = calendar.date(byAdding: .day, value: offset - 6, to: todayStart) else { return 0 }
288
290
  return tokensByDay[formatter.string(from: date)] ?? 0
@@ -306,7 +308,7 @@ extension StatusItemController {
306
308
  let updatedAt: Date? = [
307
309
  self.store.snapshot(for: provider)?.updatedAt,
308
310
  self.store.ledgerUpdatedAt[provider],
309
- ].compactMap { $0 }.max()
311
+ ].compactMap(\.self).max()
310
312
 
311
313
  if let updatedAt {
312
314
  let timestampView = self.menuCardContent(width: width, sidebar: sidebar, showIcons: true) {
@@ -36,16 +36,26 @@ extension StatusItemController {
36
36
  }
37
37
 
38
38
  final class MenuHostingView<Content: View>: NSHostingView<Content> {
39
- override var allowsVibrancy: Bool { true }
40
- override var isOpaque: Bool { false }
39
+ override var allowsVibrancy: Bool {
40
+ true
41
+ }
42
+
43
+ override var isOpaque: Bool {
44
+ false
45
+ }
41
46
  }
42
47
 
43
48
  @MainActor
44
49
  final class MenuCardItemHostingView<Content: View>: NSHostingView<Content>, MenuCardHighlighting,
45
50
  MenuCardMeasuring {
46
51
  private let highlightState: MenuCardHighlightState
47
- override var allowsVibrancy: Bool { true }
48
- override var isOpaque: Bool { false }
52
+ override var allowsVibrancy: Bool {
53
+ true
54
+ }
55
+
56
+ override var isOpaque: Bool {
57
+ false
58
+ }
49
59
 
50
60
  override var intrinsicContentSize: NSSize {
51
61
  let size = super.intrinsicContentSize
@@ -98,6 +108,7 @@ extension StatusItemController {
98
108
 
99
109
  var body: some View {
100
110
  self.content
111
+ .runicTypography()
101
112
  .environment(\.menuItemHighlighted, self.highlightState.isHighlighted)
102
113
  .foregroundStyle(MenuHighlightStyle.primary(self.highlightState.isHighlighted))
103
114
  .background(alignment: .topLeading) {
@@ -111,7 +122,7 @@ extension StatusItemController {
111
122
  .overlay(alignment: .topTrailing) {
112
123
  if self.showsSubmenuIndicator {
113
124
  Image(systemName: "chevron.right")
114
- .font(.caption2.weight(.semibold))
125
+ .font(RunicFont.caption2.weight(.semibold))
115
126
  .foregroundStyle(MenuHighlightStyle.secondary(self.highlightState.isHighlighted))
116
127
  .padding(.top, RunicSpacing.xs)
117
128
  .padding(.trailing, RunicSpacing.xs)
@@ -142,7 +153,7 @@ extension StatusItemController {
142
153
  self.iconView.imageScaling = .scaleNone
143
154
  self.iconView.translatesAutoresizingMaskIntoConstraints = false
144
155
 
145
- self.titleField.font = NSFont.menuFont(ofSize: NSFont.systemFontSize)
156
+ self.titleField.font = RunicFont.nsFont(size: NSFont.systemFontSize)
146
157
  self.titleField.lineBreakMode = .byTruncatingTail
147
158
  self.titleField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
148
159
 
@@ -217,11 +228,11 @@ extension StatusItemController {
217
228
  // MARK: - Sidebar content builder
218
229
 
219
230
  @ViewBuilder
220
- func menuCardContent<Content: View>(
231
+ func menuCardContent(
221
232
  width: CGFloat,
222
233
  sidebar: MenuCardSidebarConfig?,
223
234
  showIcons: Bool,
224
- @ViewBuilder content: @escaping (CGFloat) -> Content) -> some View
235
+ @ViewBuilder content: @escaping (CGFloat) -> some View) -> some View
225
236
  {
226
237
  if let sidebar {
227
238
  ProviderSidebarMenuCardView(
@@ -325,8 +336,8 @@ extension StatusItemController {
325
336
  let image = NSImage(
326
337
  systemSymbolName: MenuDescriptor.MenuActionSystemImage.refresh.rawValue,
327
338
  accessibilityDescription: nil) ?? NSImage(
328
- systemSymbolName: "arrow.triangle.2.circlepath",
329
- accessibilityDescription: nil)
339
+ systemSymbolName: "arrow.triangle.2.circlepath",
340
+ accessibilityDescription: nil)
330
341
  image?.isTemplate = true
331
342
  image?.size = NSSize(width: 16, height: 16)
332
343
  let view = MenuActionButtonView(title: title, image: image) { [weak self] in
@@ -363,14 +374,14 @@ extension StatusItemController {
363
374
  container.alphaValue = isEnabled ? 1.0 : 0.7
364
375
 
365
376
  let titleField = NSTextField(labelWithString: title)
366
- titleField.font = NSFont.menuFont(ofSize: NSFont.systemFontSize)
377
+ titleField.font = RunicFont.nsFont(size: NSFont.systemFontSize)
367
378
  titleField.textColor = NSColor.labelColor
368
379
  titleField.lineBreakMode = .byTruncatingTail
369
380
  titleField.maximumNumberOfLines = 1
370
381
  titleField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
371
382
 
372
383
  let subtitleField = NSTextField(labelWithString: subtitle)
373
- subtitleField.font = NSFont.menuFont(ofSize: NSFont.smallSystemFontSize)
384
+ subtitleField.font = RunicFont.nsFont(size: NSFont.smallSystemFontSize)
374
385
  subtitleField.textColor = NSColor.secondaryLabelColor
375
386
  subtitleField.lineBreakMode = .byTruncatingTail
376
387
  subtitleField.maximumNumberOfLines = 1
@@ -392,4 +403,21 @@ extension StatusItemController {
392
403
 
393
404
  return container
394
405
  }
406
+
407
+ // MARK: - Fira Code menu font post-processor
408
+
409
+ /// Recursively apply Fira Code to every NSMenuItem that uses a plain title (no custom view).
410
+ func applyRunicFont(to menu: NSMenu) {
411
+ for item in menu.items {
412
+ guard !item.isSeparatorItem, item.view == nil, !item.title.isEmpty else { continue }
413
+ if item.attributedTitle == nil {
414
+ item.attributedTitle = NSAttributedString(
415
+ string: item.title,
416
+ attributes: [.font: RunicFont.nsFont(size: NSFont.systemFontSize)])
417
+ }
418
+ if let submenu = item.submenu {
419
+ self.applyRunicFont(to: submenu)
420
+ }
421
+ }
422
+ }
395
423
  }
@@ -1,51 +1,51 @@
1
1
  import AppKit
2
- import RunicCore
3
2
  import Observation
4
3
  import QuartzCore
4
+ import RunicCore
5
5
  import SwiftUI
6
6
 
7
- /// **StatusItemController** - Manages macOS menubar status item and dropdown menu
8
- ///
9
- /// **Purpose:**
10
- /// Coordinates the menubar icon, animations, menu construction, and user interactions.
11
- /// Acts as the bridge between AppKit (NSStatusItem) and SwiftUI (menu cards).
12
- ///
13
- /// **Responsibilities:**
14
- /// - Create and manage NSStatusItem in menubar
15
- /// - Coordinate icon rendering via IconRenderer
16
- /// - Build rich NSMenu with embedded SwiftUI views
17
- /// - Handle animation lifecycle (60 FPS during pings)
18
- /// - Schedule background "ping" operations when menu opens
19
- /// - Manage keyboard shortcuts and user actions
20
- ///
21
- /// **Architecture Pattern:**
22
- /// This file is split across 4 files via extensions:
23
- /// - `StatusItemController.swift` - Core setup (318 lines)
24
- /// - `StatusItemController+Menu.swift` - Menu construction (1878 lines)
25
- /// - `StatusItemController+Actions.swift` - User actions (200 lines)
26
- /// - `StatusItemController+Animation.swift` - Animation system (469 lines)
27
- ///
28
- /// **Performance:**
29
- /// - **60 FPS cap** during active pings (PerformanceConstants.menubarFPS)
30
- /// - **Auto-stop animations** when data loaded or error encountered
31
- /// - **1.2s delay** before background ping when menu opens (reduce perceived lag)
32
- ///
33
- /// **Dependencies:**
34
- /// - `UsageStore` - Data source
35
- /// - `SettingsStore` - User preferences
36
- /// - `IconRenderer` - Icon generation
37
- /// - `MenuCardView` - SwiftUI menu content
38
- ///
39
- /// **Usage:**
40
- /// ```swift
41
- /// let controller = StatusItemController(
42
- /// store: usageStore,
43
- /// settings: settingsStore,
44
- /// account: accountInfo,
45
- /// updater: updater,
46
- /// preferencesSelection: selection
47
- /// )
48
- /// ```
7
+ // **StatusItemController** - Manages macOS menubar status item and dropdown menu
8
+ //
9
+ // **Purpose:**
10
+ // Coordinates the menubar icon, animations, menu construction, and user interactions.
11
+ // Acts as the bridge between AppKit (NSStatusItem) and SwiftUI (menu cards).
12
+ //
13
+ // **Responsibilities:**
14
+ // - Create and manage NSStatusItem in menubar
15
+ // - Coordinate icon rendering via IconRenderer
16
+ // - Build rich NSMenu with embedded SwiftUI views
17
+ // - Handle animation lifecycle (60 FPS during pings)
18
+ // - Schedule background "ping" operations when menu opens
19
+ // - Manage keyboard shortcuts and user actions
20
+ //
21
+ // **Architecture Pattern:**
22
+ // This file is split across 4 files via extensions:
23
+ // - `StatusItemController.swift` - Core setup (318 lines)
24
+ // - `StatusItemController+Menu.swift` - Menu construction (1878 lines)
25
+ // - `StatusItemController+Actions.swift` - User actions (200 lines)
26
+ // - `StatusItemController+Animation.swift` - Animation system (469 lines)
27
+ //
28
+ // **Performance:**
29
+ // - **60 FPS cap** during active pings (PerformanceConstants.menubarFPS)
30
+ // - **Auto-stop animations** when data loaded or error encountered
31
+ // - **1.2s delay** before background ping when menu opens (reduce perceived lag)
32
+ //
33
+ // **Dependencies:**
34
+ // - `UsageStore` - Data source
35
+ // - `SettingsStore` - User preferences
36
+ // - `IconRenderer` - Icon generation
37
+ // - `MenuCardView` - SwiftUI menu content
38
+ //
39
+ // **Usage:**
40
+ // ```swift
41
+ // let controller = StatusItemController(
42
+ // store: usageStore,
43
+ // settings: settingsStore,
44
+ // account: accountInfo,
45
+ // updater: updater,
46
+ // preferencesSelection: selection
47
+ // )
48
+ // ```
49
49
 
50
50
  // MARK: - Status item controller (AppKit-hosted icons, SwiftUI popovers)
51
51
 
@@ -59,7 +59,8 @@ final class BudgetNotificationManager {
59
59
  let title = "Budget Alert: \(projectName)"
60
60
  let body = "Projected to exceed \(limitStr) limit by \(overshootStr) this month"
61
61
 
62
- self.logger.info("posting budget breach notification",
62
+ self.logger.info(
63
+ "posting budget breach notification",
63
64
  metadata: [
64
65
  "provider": provider.rawValue,
65
66
  "project": projectKey,
@@ -1,9 +1,9 @@
1
- import RunicCore
2
1
  import Darwin
3
2
  import Foundation
3
+ import RunicCore
4
4
 
5
5
  struct ClaudeLoginRunner {
6
- enum Phase: Sendable {
6
+ enum Phase {
7
7
  case requesting
8
8
  case waitingBrowser
9
9
  }
@@ -1,6 +1,6 @@
1
- import RunicCore
2
1
  import Darwin
3
2
  import Foundation
3
+ import RunicCore
4
4
 
5
5
  struct CodexLoginRunner {
6
6
  struct Result {
@@ -1,21 +1,21 @@
1
1
  import AppKit
2
- import RunicCore
3
2
  import Foundation
3
+ import RunicCore
4
4
  import WebKit
5
5
 
6
6
  /// Handles Cursor login flow using a WebKit-based browser window.
7
7
  /// Captures session cookies after successful authentication.
8
8
  @MainActor
9
9
  final class CursorLoginRunner: NSObject {
10
- enum Phase: Sendable {
10
+ enum Phase {
11
11
  case loading
12
12
  case waitingLogin
13
13
  case success
14
14
  case failed(String)
15
15
  }
16
16
 
17
- struct Result: Sendable {
18
- enum Outcome: Sendable {
17
+ struct Result {
18
+ enum Outcome {
19
19
  case success
20
20
  case cancelled
21
21
  case failed(String)
@@ -1,6 +1,6 @@
1
1
  import AppKit
2
- import RunicCore
3
2
  import Foundation
3
+ import RunicCore
4
4
 
5
5
  enum GeminiLoginRunner {
6
6
  private static let geminiConfigDir = FileManager.default.homeDirectoryForCurrentUser
@@ -45,11 +45,8 @@ enum GeminiLoginRunner {
45
45
  }
46
46
 
47
47
  // Create a temporary shell script that runs gemini (auto-prompts for auth when no creds)
48
- let scriptContent = """
49
- #!/bin/bash
50
- cd ~
51
- "\(binary)"
52
- """
48
+ let escaped = binary.replacingOccurrences(of: "'", with: "'\\''")
49
+ let scriptContent = "#!/bin/bash\ncd ~\n'\(escaped)'\n"
53
50
 
54
51
  let tempDir = FileManager.default.temporaryDirectory
55
52
  let scriptURL = tempDir.appendingPathComponent("gemini_login_\(UUID().uuidString).command")