@sriinnu/runic 1.2.0 → 2.11.1

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 (586) hide show
  1. package/CHANGELOG.md +267 -0
  2. package/LICENSE +373 -21
  3. package/README.md +98 -84
  4. package/THIRD-PARTY-NOTICES.md +651 -0
  5. package/package.json +14 -4
  6. package/.swiftformat +0 -50
  7. package/.swiftlint.yml +0 -160
  8. package/CONTRIBUTING.md +0 -456
  9. package/Icon.icns +0 -0
  10. package/Icon.icon/Assets/runic.png +0 -0
  11. package/Icon.icon/icon.json +0 -36
  12. package/Package.resolved +0 -51
  13. package/Package.swift +0 -136
  14. package/Packages/Helix/LICENSE +0 -21
  15. package/Packages/Helix/Package.swift +0 -29
  16. package/Packages/Helix/README.md +0 -976
  17. package/Packages/Helix/Sources/Helix/Binding.swift +0 -111
  18. package/Packages/Helix/Sources/Helix/CommandParser.swift +0 -160
  19. package/Packages/Helix/Sources/Helix/CommandSignature.swift +0 -117
  20. package/Packages/Helix/Sources/Helix/ExpressibleFromArgument.swift +0 -72
  21. package/Packages/Helix/Sources/Helix/HelpGenerator.swift +0 -175
  22. package/Packages/Helix/Sources/Helix/NameSpecification.swift +0 -98
  23. package/Packages/Helix/Sources/Helix/ParsableCommand.swift +0 -204
  24. package/Packages/Helix/Sources/Helix/Platform/DarwinPlatformContext.swift +0 -44
  25. package/Packages/Helix/Sources/Helix/Platform/LinuxPlatformContext.swift +0 -34
  26. package/Packages/Helix/Sources/Helix/Platform/PlatformContext+Default.swift +0 -91
  27. package/Packages/Helix/Sources/Helix/Platform/PlatformContext.swift +0 -94
  28. package/Packages/Helix/Sources/Helix/Platform/PlatformPath.swift +0 -167
  29. package/Packages/Helix/Sources/Helix/Platform/Stdio.swift +0 -99
  30. package/Packages/Helix/Sources/Helix/Platform/WebPlatformContext.swift +0 -146
  31. package/Packages/Helix/Sources/Helix/Platform/WindowsPlatformContext.swift +0 -97
  32. package/Packages/Helix/Sources/Helix/Program.swift +0 -204
  33. package/Packages/Helix/Sources/Helix/PropertyWrappers.swift +0 -367
  34. package/Packages/Silo/LICENSE +0 -21
  35. package/Packages/Silo/Package.swift +0 -34
  36. package/Packages/Silo/README.md +0 -466
  37. package/Packages/Silo/Sources/Silo/BinaryCookiesReader.swift +0 -169
  38. package/Packages/Silo/Sources/Silo/BrowserCookieAnalytics.swift +0 -365
  39. package/Packages/Silo/Sources/Silo/BrowserCookieBackends.swift +0 -594
  40. package/Packages/Silo/Sources/Silo/BrowserCookieClient.swift +0 -326
  41. package/Packages/Silo/Sources/Silo/BrowserCookieExport.swift +0 -154
  42. package/Packages/Silo/Sources/Silo/BrowserCookieImport.swift +0 -119
  43. package/Packages/Silo/Sources/Silo/BrowserCookieMockStore.swift +0 -121
  44. package/Packages/Silo/Sources/Silo/BrowserCookieModels.swift +0 -391
  45. package/Packages/Silo/Sources/Silo/ChromiumCookieReader.swift +0 -240
  46. package/Packages/Silo/Sources/Silo/CryptoSupport.swift +0 -86
  47. package/Packages/Silo/Sources/Silo/FirefoxCookieReader.swift +0 -172
  48. package/Packages/Silo/Sources/Silo/LinuxChromiumCookieReader.swift +0 -172
  49. package/Packages/Silo/Sources/Silo/LinuxSecretService.swift +0 -105
  50. package/Packages/Silo/Sources/Silo/MacOSChromiumCookieReader.swift +0 -191
  51. package/Packages/Silo/Sources/Silo/SQLiteSnapshot.swift +0 -41
  52. package/Packages/Silo/Sources/Silo/WindowsChromiumCookieReader.swift +0 -110
  53. package/RuniciOS/App/RuniciOSApp.swift +0 -105
  54. package/RuniciOS/Core/iOSUsageStore.swift +0 -192
  55. package/RuniciOS/Views/ProviderDetailView.swift +0 -312
  56. package/RuniciOS/Views/ProviderListView.swift +0 -228
  57. package/RuniciOS/Widgets/LargeWidget.swift +0 -367
  58. package/RuniciOS/Widgets/LiveActivity.swift +0 -342
  59. package/RuniciOS/Widgets/LockScreenWidget.swift +0 -273
  60. package/RuniciOS/Widgets/MediumWidget.swift +0 -370
  61. package/RuniciOS/Widgets/RunicWidget.swift +0 -274
  62. package/RuniciOS/Widgets/SmallWidget.swift +0 -281
  63. package/RuniciOS/Widgets/WidgetModels.swift +0 -285
  64. package/RuniciOS/Widgets/WidgetTimeline.swift +0 -270
  65. package/Scripts/CREATION-SUMMARY.md +0 -367
  66. package/Scripts/DIRECTORY-STRUCTURE.md +0 -358
  67. package/Scripts/GETTING-STARTED-CHECKLIST.md +0 -364
  68. package/Scripts/INDEX.md +0 -365
  69. package/Scripts/QUICK-REFERENCE.md +0 -164
  70. package/Scripts/README-SCRIPTS.md +0 -926
  71. package/Scripts/build-android.sh +0 -526
  72. package/Scripts/build-ios.sh +0 -529
  73. package/Scripts/build-macos.sh +0 -603
  74. package/Scripts/build-windows.sh +0 -504
  75. package/Scripts/build_icon.sh +0 -49
  76. package/Scripts/changelog-to-html.sh +0 -89
  77. package/Scripts/check-release-assets.sh +0 -10
  78. package/Scripts/ci-config.yml +0 -474
  79. package/Scripts/compile_and_run.sh +0 -219
  80. package/Scripts/compile_and_run_adhoc.sh +0 -4
  81. package/Scripts/docs-list.mjs +0 -119
  82. package/Scripts/make_appcast.sh +0 -58
  83. package/Scripts/package_app.sh +0 -409
  84. package/Scripts/release-lib.sh +0 -214
  85. package/Scripts/release.sh +0 -76
  86. package/Scripts/setup-all.sh +0 -387
  87. package/Scripts/setup-ios.sh +0 -404
  88. package/Scripts/setup-react-native.sh +0 -523
  89. package/Scripts/sign-and-notarize.sh +0 -122
  90. package/Scripts/test_live_update.sh +0 -38
  91. package/Scripts/validate_changelog.sh +0 -23
  92. package/Scripts/verify_appcast.sh +0 -92
  93. package/Sources/Runic/About.swift +0 -93
  94. package/Sources/Runic/AppNotifications.swift +0 -115
  95. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +0 -666
  96. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +0 -518
  97. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +0 -810
  98. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +0 -87
  99. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +0 -1407
  100. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +0 -337
  101. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +0 -395
  102. package/Sources/Runic/Controllers/StatusItemController.swift +0 -360
  103. package/Sources/Runic/Core/BudgetNotificationManager.swift +0 -80
  104. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +0 -103
  105. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +0 -154
  106. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +0 -206
  107. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +0 -92
  108. package/Sources/Runic/Core/Rendering/IconRenderer.swift +0 -1216
  109. package/Sources/Runic/Core/Rendering/IconView.swift +0 -120
  110. package/Sources/Runic/Core/Rendering/ProviderBrandIcon.swift +0 -71
  111. package/Sources/Runic/Core/RunicAnimation.swift +0 -53
  112. package/Sources/Runic/Core/RunicColors.swift +0 -73
  113. package/Sources/Runic/Core/RunicCornerRadius.swift +0 -15
  114. package/Sources/Runic/Core/RunicSpacing.swift +0 -68
  115. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +0 -119
  116. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +0 -119
  117. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +0 -119
  118. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +0 -119
  119. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +0 -119
  120. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +0 -158
  121. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +0 -119
  122. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +0 -119
  123. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +0 -119
  124. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +0 -119
  125. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +0 -119
  126. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +0 -119
  127. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +0 -119
  128. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +0 -119
  129. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +0 -119
  130. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +0 -119
  131. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +0 -30
  132. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +0 -119
  133. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +0 -119
  134. package/Sources/Runic/Core/Stores/SettingsStore.swift +0 -1711
  135. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +0 -119
  136. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +0 -20
  137. package/Sources/Runic/Core/Stores/UsageStore+Status.swift +0 -189
  138. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +0 -33
  139. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +0 -67
  140. package/Sources/Runic/Core/Stores/UsageStore.swift +0 -3605
  141. package/Sources/Runic/Core/Stores/XAITokenStore.swift +0 -119
  142. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +0 -118
  143. package/Sources/Runic/Core/UsageExporter.swift +0 -193
  144. package/Sources/Runic/Date+RelativeDescription.swift +0 -21
  145. package/Sources/Runic/HiddenWindowView.swift +0 -32
  146. package/Sources/Runic/InstallOrigin.swift +0 -9
  147. package/Sources/Runic/KeyboardShortcuts+Names.swift +0 -6
  148. package/Sources/Runic/MouseLocationReader.swift +0 -65
  149. package/Sources/Runic/Notifications+Runic.swift +0 -6
  150. package/Sources/Runic/OpenAICreditsPurchaseWindowController.swift +0 -503
  151. package/Sources/Runic/ProviderRegistry.swift +0 -100
  152. package/Sources/Runic/ProviderSwitcherButtons.swift +0 -267
  153. package/Sources/Runic/Providers/Antigravity/AntigravityLoginFlow.swift +0 -11
  154. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +0 -14
  155. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +0 -24
  156. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +0 -52
  157. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +0 -42
  158. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +0 -24
  159. package/Sources/Runic/Providers/Claude/ClaudeLoginFlow.swift +0 -26
  160. package/Sources/Runic/Providers/Claude/ClaudeProviderImplementation.swift +0 -14
  161. package/Sources/Runic/Providers/Codex/CodexLoginFlow.swift +0 -18
  162. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +0 -15
  163. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +0 -24
  164. package/Sources/Runic/Providers/Copilot/CopilotLoginFlow.swift +0 -193
  165. package/Sources/Runic/Providers/Copilot/CopilotProviderImplementation.swift +0 -110
  166. package/Sources/Runic/Providers/Cursor/CursorLoginFlow.swift +0 -28
  167. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +0 -15
  168. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +0 -24
  169. package/Sources/Runic/Providers/Factory/FactoryLoginFlow.swift +0 -12
  170. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +0 -15
  171. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +0 -24
  172. package/Sources/Runic/Providers/Gemini/GeminiLoginFlow.swift +0 -20
  173. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +0 -15
  174. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +0 -24
  175. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +0 -24
  176. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +0 -32
  177. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +0 -24
  178. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +0 -24
  179. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +0 -24
  180. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +0 -24
  181. package/Sources/Runic/Providers/Shared/ProviderCatalog.swift +0 -15
  182. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +0 -48
  183. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +0 -66
  184. package/Sources/Runic/Providers/Shared/ProviderLoginFlow.swift +0 -12
  185. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +0 -99
  186. package/Sources/Runic/Providers/Shared/SystemSettingsLinks.swift +0 -18
  187. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +0 -24
  188. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +0 -24
  189. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +0 -24
  190. package/Sources/Runic/Resources/ProviderIcon-antigravity.svg +0 -2
  191. package/Sources/Runic/Resources/ProviderIcon-auggie.svg +0 -3
  192. package/Sources/Runic/Resources/ProviderIcon-azure.svg +0 -5
  193. package/Sources/Runic/Resources/ProviderIcon-bedrock.svg +0 -7
  194. package/Sources/Runic/Resources/ProviderIcon-cerebras.svg +0 -3
  195. package/Sources/Runic/Resources/ProviderIcon-claude.svg +0 -1
  196. package/Sources/Runic/Resources/ProviderIcon-codex.svg +0 -1
  197. package/Sources/Runic/Resources/ProviderIcon-cohere.svg +0 -3
  198. package/Sources/Runic/Resources/ProviderIcon-copilot.svg +0 -1
  199. package/Sources/Runic/Resources/ProviderIcon-cursor.svg +0 -3
  200. package/Sources/Runic/Resources/ProviderIcon-deepseek.svg +0 -4
  201. package/Sources/Runic/Resources/ProviderIcon-factory.svg +0 -3
  202. package/Sources/Runic/Resources/ProviderIcon-fireworks.svg +0 -3
  203. package/Sources/Runic/Resources/ProviderIcon-gemini.svg +0 -1
  204. package/Sources/Runic/Resources/ProviderIcon-groq.svg +0 -1
  205. package/Sources/Runic/Resources/ProviderIcon-kimi.svg +0 -4
  206. package/Sources/Runic/Resources/ProviderIcon-minimax.svg +0 -1
  207. package/Sources/Runic/Resources/ProviderIcon-mistral.svg +0 -5
  208. package/Sources/Runic/Resources/ProviderIcon-openrouter.svg +0 -7
  209. package/Sources/Runic/Resources/ProviderIcon-perplexity.svg +0 -4
  210. package/Sources/Runic/Resources/ProviderIcon-qwen.svg +0 -9
  211. package/Sources/Runic/Resources/ProviderIcon-sambanova.svg +0 -4
  212. package/Sources/Runic/Resources/ProviderIcon-together.svg +0 -5
  213. package/Sources/Runic/Resources/ProviderIcon-vertexai.svg +0 -17
  214. package/Sources/Runic/Resources/ProviderIcon-xai.svg +0 -3
  215. package/Sources/Runic/Resources/ProviderIcon-zai.svg +0 -5
  216. package/Sources/Runic/Resources/RunicMenubarIcon.svg +0 -7
  217. package/Sources/Runic/Resources/provider-context-windows.json +0 -20
  218. package/Sources/Runic/RunicApp.swift +0 -357
  219. package/Sources/Runic/SessionQuotaNotifications.swift +0 -58
  220. package/Sources/Runic/UsagePaceText.swift +0 -52
  221. package/Sources/Runic/UsageProgressBar.swift +0 -224
  222. package/Sources/Runic/Utilities/DisplayLink.swift +0 -96
  223. package/Sources/Runic/Utilities/LoadingPattern.swift +0 -60
  224. package/Sources/Runic/Utilities/PerformanceConstants.swift +0 -85
  225. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +0 -256
  226. package/Sources/Runic/Views/Menu/ChartPanelStyle.swift +0 -22
  227. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +0 -275
  228. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +0 -377
  229. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +0 -312
  230. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +0 -312
  231. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +0 -228
  232. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +0 -228
  233. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +0 -155
  234. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +0 -64
  235. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +0 -268
  236. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +0 -104
  237. package/Sources/Runic/Views/Menu/MenuCardView.swift +0 -1900
  238. package/Sources/Runic/Views/Menu/MenuContent.swift +0 -150
  239. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +0 -369
  240. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +0 -42
  241. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +0 -235
  242. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +0 -278
  243. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +0 -209
  244. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +0 -220
  245. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +0 -88
  246. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +0 -201
  247. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +0 -398
  248. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +0 -232
  249. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +0 -364
  250. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +0 -190
  251. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +0 -132
  252. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +0 -618
  253. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +0 -305
  254. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +0 -271
  255. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +0 -268
  256. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +0 -422
  257. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +0 -641
  258. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +0 -344
  259. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +0 -230
  260. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +0 -322
  261. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +0 -469
  262. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +0 -231
  263. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +0 -50
  264. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +0 -567
  265. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +0 -288
  266. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +0 -3435
  267. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +0 -25
  268. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +0 -59
  269. package/Sources/Runic/Views/Preferences/PreferencesView.swift +0 -206
  270. package/Sources/Runic/Views/Rating/QualityRatingView.swift +0 -238
  271. package/Sources/Runic/Views/Team/TeamDashboardView.swift +0 -371
  272. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +0 -175
  273. package/Sources/Runic/Views/Team/TeamManagementView.swift +0 -761
  274. package/Sources/Runic/Views/Team/TeamMemberRow.swift +0 -181
  275. package/Sources/RunicCLI/CLIEntry.swift +0 -942
  276. package/Sources/RunicCLI/CLIRenderer.swift +0 -87
  277. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +0 -588
  278. package/Sources/RunicCLI/Commands/ModelsCommand.swift +0 -362
  279. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +0 -439
  280. package/Sources/RunicCLI/Commands/ResetCommand.swift +0 -429
  281. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +0 -168
  282. package/Sources/RunicClaudeWatchdog/main.swift +0 -122
  283. package/Sources/RunicClaudeWebProbe/main.swift +0 -62
  284. package/Sources/RunicCore/AlertRuleStore.swift +0 -187
  285. package/Sources/RunicCore/BrowserCookieImportOrder.swift +0 -25
  286. package/Sources/RunicCore/CopilotUsageModels.swift +0 -84
  287. package/Sources/RunicCore/CostUsageFetcher.swift +0 -119
  288. package/Sources/RunicCore/CostUsageModels.swift +0 -479
  289. package/Sources/RunicCore/CreditsModels.swift +0 -50
  290. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +0 -269
  291. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +0 -633
  292. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +0 -500
  293. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +0 -679
  294. package/Sources/RunicCore/Host/Process/SubprocessRunner.swift +0 -135
  295. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +0 -173
  296. package/Sources/RunicCore/Logging/JSONStderrLogHandler.swift +0 -79
  297. package/Sources/RunicCore/Logging/OSLogLogHandler.swift +0 -86
  298. package/Sources/RunicCore/Logging/RunicLog.swift +0 -123
  299. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +0 -371
  300. package/Sources/RunicCore/OpenAIDashboardModels.swift +0 -210
  301. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +0 -676
  302. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +0 -492
  303. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardNavigationDelegate.swift +0 -51
  304. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardParser.swift +0 -483
  305. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift +0 -558
  306. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebViewCache.swift +0 -204
  307. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebsiteDataStore.swift +0 -78
  308. package/Sources/RunicCore/PathEnvironment.swift +0 -482
  309. package/Sources/RunicCore/Performance/PerformanceModels.swift +0 -371
  310. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +0 -409
  311. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +0 -178
  312. package/Sources/RunicCore/ProjectBudgetStore.swift +0 -99
  313. package/Sources/RunicCore/ProviderCostSnapshot.swift +0 -29
  314. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +0 -66
  315. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +0 -685
  316. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +0 -83
  317. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +0 -142
  318. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +0 -141
  319. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +0 -188
  320. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +0 -126
  321. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +0 -168
  322. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +0 -84
  323. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +0 -75
  324. package/Sources/RunicCore/Providers/Claude/ClaudeCLISession.swift +0 -332
  325. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift +0 -180
  326. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +0 -137
  327. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +0 -207
  328. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +0 -654
  329. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +0 -28
  330. package/Sources/RunicCore/Providers/Claude/ClaudeUsageFetcher.swift +0 -480
  331. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +0 -780
  332. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +0 -147
  333. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +0 -280
  334. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexTokenRefresher.swift +0 -100
  335. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +0 -243
  336. package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +0 -125
  337. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +0 -24
  338. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +0 -181
  339. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +0 -83
  340. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +0 -85
  341. package/Sources/RunicCore/Providers/Copilot/CopilotDeviceFlow.swift +0 -123
  342. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +0 -114
  343. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +0 -100
  344. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +0 -291
  345. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +0 -386
  346. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +0 -65
  347. package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +0 -595
  348. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +0 -85
  349. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +0 -108
  350. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +0 -398
  351. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +0 -65
  352. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +0 -1276
  353. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +0 -84
  354. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +0 -75
  355. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +0 -65
  356. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +0 -836
  357. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +0 -83
  358. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +0 -71
  359. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +0 -83
  360. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +0 -77
  361. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +0 -92
  362. package/Sources/RunicCore/Providers/MiniMax/MiniMaxSettingsReader.swift +0 -54
  363. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +0 -168
  364. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +0 -911
  365. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +0 -84
  366. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +0 -75
  367. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +0 -87
  368. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +0 -203
  369. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +0 -84
  370. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +0 -75
  371. package/Sources/RunicCore/Providers/ProviderBranding.swift +0 -25
  372. package/Sources/RunicCore/Providers/ProviderCLIConfig.swift +0 -17
  373. package/Sources/RunicCore/Providers/ProviderDescriptor.swift +0 -129
  374. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +0 -263
  375. package/Sources/RunicCore/Providers/ProviderSettingsSnapshot.swift +0 -98
  376. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +0 -501
  377. package/Sources/RunicCore/Providers/ProviderVersionDetector.swift +0 -68
  378. package/Sources/RunicCore/Providers/Providers.swift +0 -184
  379. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +0 -84
  380. package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +0 -30
  381. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +0 -315
  382. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +0 -84
  383. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +0 -75
  384. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +0 -83
  385. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +0 -75
  386. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +0 -123
  387. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +0 -48
  388. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +0 -144
  389. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +0 -83
  390. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +0 -75
  391. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +0 -72
  392. package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +0 -41
  393. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +0 -718
  394. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +0 -385
  395. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +0 -310
  396. package/Sources/RunicCore/Sync/SyncProtocol.swift +0 -288
  397. package/Sources/RunicCore/Sync/SyncRecord.swift +0 -405
  398. package/Sources/RunicCore/Sync/SyncUsageExample.swift +0 -294
  399. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +0 -775
  400. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +0 -333
  401. package/Sources/RunicCore/Team/TeamModels.swift +0 -583
  402. package/Sources/RunicCore/Team/TeamShowbackExporter.swift +0 -495
  403. package/Sources/RunicCore/Team/TeamStore.swift +0 -792
  404. package/Sources/RunicCore/TextParsing.swift +0 -55
  405. package/Sources/RunicCore/UsageFetcher.swift +0 -694
  406. package/Sources/RunicCore/UsageFormatter.swift +0 -410
  407. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +0 -427
  408. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +0 -494
  409. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +0 -158
  410. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +0 -550
  411. package/Sources/RunicCore/UsageLedger/UsageLedger.swift +0 -29
  412. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +0 -660
  413. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +0 -209
  414. package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +0 -417
  415. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +0 -245
  416. package/Sources/RunicCore/UsagePace.swift +0 -81
  417. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +0 -73
  418. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +0 -74
  419. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +0 -208
  420. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Claude.swift +0 -455
  421. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Timestamp.swift +0 -132
  422. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +0 -589
  423. package/Sources/RunicCore/WidgetSnapshot.swift +0 -183
  424. package/Sources/RunicMacroSupport/ProviderRegistrationMacros.swift +0 -14
  425. package/Sources/RunicMacros/ProviderRegistrationMacros.swift +0 -216
  426. package/Sources/RunicWidget/RunicWidgetBundle.swift +0 -79
  427. package/Sources/RunicWidget/RunicWidgetProvider.swift +0 -283
  428. package/Sources/RunicWidget/RunicWidgetViews.swift +0 -680
  429. package/Tests/RunicTests/AntigravityStatusProbeTests.swift +0 -50
  430. package/Tests/RunicTests/AppDelegateTests.swift +0 -43
  431. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +0 -22
  432. package/Tests/RunicTests/CLIArgumentParsingTests.swift +0 -70
  433. package/Tests/RunicTests/CLICostTests.swift +0 -65
  434. package/Tests/RunicTests/CLIProviderSelectionTests.swift +0 -22
  435. package/Tests/RunicTests/CLISnapshotTests.swift +0 -72
  436. package/Tests/RunicTests/CLIWebFallbackTests.swift +0 -58
  437. package/Tests/RunicTests/ClaudeOAuthTests.swift +0 -164
  438. package/Tests/RunicTests/ClaudeResilienceTests.swift +0 -30
  439. package/Tests/RunicTests/ClaudeUsageTests.swift +0 -370
  440. package/Tests/RunicTests/CodexOAuthTests.swift +0 -122
  441. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +0 -82
  442. package/Tests/RunicTests/CopilotTokenResolverTests.swift +0 -75
  443. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +0 -75
  444. package/Tests/RunicTests/CostUsageDecodingTests.swift +0 -394
  445. package/Tests/RunicTests/CostUsagePricingTests.swift +0 -50
  446. package/Tests/RunicTests/CostUsageScannerTests.swift +0 -700
  447. package/Tests/RunicTests/CursorStatusProbeTests.swift +0 -323
  448. package/Tests/RunicTests/FactoryStatusProbeTests.swift +0 -69
  449. package/Tests/RunicTests/GeminiAPITestHelpers.swift +0 -94
  450. package/Tests/RunicTests/GeminiLoginAlertTests.swift +0 -28
  451. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +0 -283
  452. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +0 -279
  453. package/Tests/RunicTests/GeminiStatusProbeTests.swift +0 -233
  454. package/Tests/RunicTests/GeminiTestEnvironment.swift +0 -87
  455. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +0 -73
  456. package/Tests/RunicTests/InstallOriginTests.swift +0 -18
  457. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +0 -9
  458. package/Tests/RunicTests/LiveAccountTests.swift +0 -24
  459. package/Tests/RunicTests/LoadingPatternTests.swift +0 -35
  460. package/Tests/RunicTests/MenuCardModelTests.swift +0 -545
  461. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +0 -8
  462. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +0 -8
  463. package/Tests/RunicTests/MiniMaxParsingTests.swift +0 -68
  464. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +0 -8
  465. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +0 -112
  466. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +0 -17
  467. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +0 -75
  468. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +0 -27
  469. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +0 -134
  470. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +0 -53
  471. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +0 -92
  472. package/Tests/RunicTests/PathBuilderTests.swift +0 -237
  473. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +0 -160
  474. package/Tests/RunicTests/ProviderIconResourcesTests.swift +0 -38
  475. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +0 -16
  476. package/Tests/RunicTests/ProviderRegistryTests.swift +0 -29
  477. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +0 -202
  478. package/Tests/RunicTests/ProviderToggleStoreTests.swift +0 -51
  479. package/Tests/RunicTests/RunicTests.swift +0 -120
  480. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +0 -36
  481. package/Tests/RunicTests/SettingsStoreTests.swift +0 -161
  482. package/Tests/RunicTests/StatusItemAnimationTests.swift +0 -49
  483. package/Tests/RunicTests/StatusMenuTests.swift +0 -434
  484. package/Tests/RunicTests/StatusProbeTests.swift +0 -377
  485. package/Tests/RunicTests/SubprocessRunnerTests.swift +0 -20
  486. package/Tests/RunicTests/SubscriptionDetectionTests.swift +0 -65
  487. package/Tests/RunicTests/TTYCommandRunnerTests.swift +0 -148
  488. package/Tests/RunicTests/TTYIntegrationTests.swift +0 -41
  489. package/Tests/RunicTests/TeamShowbackExporterTests.swift +0 -159
  490. package/Tests/RunicTests/TextParsingTests.swift +0 -12
  491. package/Tests/RunicTests/UsageFormatterTests.swift +0 -112
  492. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +0 -172
  493. package/Tests/RunicTests/UsageLedgerForecastTests.swift +0 -213
  494. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +0 -83
  495. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +0 -170
  496. package/Tests/RunicTests/UsagePaceTests.swift +0 -77
  497. package/Tests/RunicTests/UsagePaceTextTests.swift +0 -109
  498. package/Tests/RunicTests/UsagePolicyEngineTests.swift +0 -91
  499. package/Tests/RunicTests/WidgetSnapshotTests.swift +0 -45
  500. package/Tests/RunicTests/ZaiProviderTests.swift +0 -61
  501. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +0 -6
  502. package/TestsLinux/PlatformGatingTests.swift +0 -46
  503. package/appcast.xml +0 -144
  504. package/assets/logo-concept-stack.svg +0 -24
  505. package/assets/logo-minimal-bars.svg +0 -15
  506. package/assets/logo.svg +0 -20
  507. package/assets/logo2.svg +0 -43
  508. package/bin/docs-list +0 -5
  509. package/build-and-run.sh +0 -369
  510. package/docs/.nojekyll +0 -1
  511. package/docs/CNAME +0 -1
  512. package/docs/antigravity.md +0 -75
  513. package/docs/architecture.md +0 -32
  514. package/docs/claude.md +0 -93
  515. package/docs/cli-commands/integration_guide.md +0 -371
  516. package/docs/cli-commands/quick_reference.md +0 -362
  517. package/docs/cli-commands/readme.md +0 -361
  518. package/docs/cli.md +0 -156
  519. package/docs/cloudkit_sync_guide.md +0 -495
  520. package/docs/codebase_review.md +0 -288
  521. package/docs/codex-oauth.md +0 -635
  522. package/docs/codex.md +0 -104
  523. package/docs/codexbar.png +0 -0
  524. package/docs/copilot.md +0 -44
  525. package/docs/cursor.md +0 -46
  526. package/docs/design/designcomparisonchart.md +0 -510
  527. package/docs/design/implementationguide.md +0 -792
  528. package/docs/design/menubaricondesignconcepts.md +0 -796
  529. package/docs/design/mockups/hybridiconconcept.svg +0 -62
  530. package/docs/design/mockups/infinityiconconcept.svg +0 -41
  531. package/docs/design/mockups/infinityiconstates.svg +0 -129
  532. package/docs/design/mockups/wavelogorefined.svg +0 -48
  533. package/docs/design/quick_summary.md +0 -426
  534. package/docs/design/readme.md +0 -478
  535. package/docs/design/sizespecifications.md +0 -575
  536. package/docs/design-system.md +0 -836
  537. package/docs/factory.md +0 -103
  538. package/docs/gemini.md +0 -73
  539. package/docs/icon.md +0 -37
  540. package/docs/icon.png +0 -0
  541. package/docs/index.html +0 -175
  542. package/docs/keychain_fix.md +0 -274
  543. package/docs/minimax.md +0 -71
  544. package/docs/packaging.md +0 -25
  545. package/docs/progress-bar-improvements.md +0 -240
  546. package/docs/progress-bar-quick-reference.md +0 -283
  547. package/docs/progress-bar-technical-details.md +0 -432
  548. package/docs/progress-bar-visual-test.swift +0 -375
  549. package/docs/provider.md +0 -202
  550. package/docs/providers.md +0 -86
  551. package/docs/refactor/macros.md +0 -35
  552. package/docs/refresh-loop.md +0 -22
  553. package/docs/releasing-homebrew.md +0 -35
  554. package/docs/releasing.md +0 -121
  555. package/docs/research/concrete_todo_2026-02-22.md +0 -47
  556. package/docs/research/core-feature-research.md +0 -248
  557. package/docs/research/feature_uiux_roadmap_2026-02-22.md +0 -202
  558. package/docs/research/parity_matrix.md +0 -49
  559. package/docs/research/provider_expansion_candidates_2026-02-22.md +0 -80
  560. package/docs/research/provider_model_token_project_matrix_2026-02-23.md +0 -46
  561. package/docs/research/release_gate_checklist.md +0 -57
  562. package/docs/site.css +0 -505
  563. package/docs/sparkle.md +0 -28
  564. package/docs/status.md +0 -28
  565. package/docs/sync/implementation.md +0 -393
  566. package/docs/sync/readme.md +0 -317
  567. package/docs/sync_files_summary.md +0 -391
  568. package/docs/sync_quick_reference.md +0 -225
  569. package/docs/todo.md +0 -11
  570. package/docs/ui.md +0 -29
  571. package/docs/web-integration.md +0 -9
  572. package/docs/widgets.md +0 -85
  573. package/docs/workflows/error-handling-improvements.md +0 -434
  574. package/docs/workflows/folder_structure.md +0 -299
  575. package/docs/workflows/loading_states.md +0 -446
  576. package/docs/workflows/loading_states_examples.md +0 -565
  577. package/docs/workflows/loading_states_flow.md +0 -430
  578. package/docs/workflows/performance_optimizations.md +0 -457
  579. package/docs/workflows/phase1-cli-testing-report.md +0 -313
  580. package/docs/workflows/quick_reference.md +0 -228
  581. package/docs/workflows/readme.md +0 -59
  582. package/docs/workflows/recent_improvements.md +0 -275
  583. package/docs/workflows/sync_enablement_summary.md +0 -400
  584. package/docs/zai.md +0 -43
  585. package/runic.png +0 -0
  586. package/version.env +0 -2
@@ -1,1216 +0,0 @@
1
- import AppKit
2
- import RunicCore
3
-
4
- /// **IconRenderer** - Generates menubar status item icons with usage visualization
5
- ///
6
- /// **Purpose:**
7
- /// Creates pixel-perfect 18×18pt (36×36px @2x) template images for the macOS menubar.
8
- /// Renders usage bars, stale indicators, and provider status into the icon.
9
- ///
10
- /// **Responsibilities:**
11
- /// - Generate icons showing session/weekly usage patterns
12
- /// - Apply stale data dimming when data is > 5 minutes old
13
- /// - Cache rendered icons for performance (64 icon cache, 512 morph cache)
14
- /// - Support template (adapts to menubar theme) and vibrant appearances
15
- /// - Render loading animations and error states
16
- ///
17
- /// **Performance:**
18
- /// - **O(1) icon cache lookup** via IconCacheKey hash
19
- /// - **O(1) morph cache** for animation frames
20
- /// - Cap cache sizes via `PerformanceConstants`
21
- /// - All rendering happens off main thread in `dispatchPrecondition(condition: .notOnQueue(.main))`
22
- ///
23
- /// **Dependencies:**
24
- /// - `PerformanceConstants` - Cache sizes and timing
25
- /// - `UsageProvider` - Provider-specific rendering
26
- /// - `AppKit/CoreGraphics` - Image generation
27
- ///
28
- /// **Usage:**
29
- /// ```swift
30
- /// let icon = IconRenderer.render(
31
- /// state: .usage(snap, status),
32
- /// indicator: .none,
33
- /// appearance: .template,
34
- /// dataMode: .remaining
35
- /// )
36
- /// statusItem.button?.image = icon
37
- /// ```
38
-
39
- enum IconAppearance: Sendable {
40
- case template // Adapts to menubar theme (light/dark mode)
41
- case vibrant // Full color rendering
42
- }
43
-
44
- enum IconDataMode: Sendable {
45
- case remaining // Show remaining quota
46
- case used // Show used quota
47
- }
48
-
49
- enum IconRenderer {
50
- private static let creditsCap: Double = 1000
51
- private static let baseSize = NSSize(width: 38, height: 22)
52
- // Render to a 38×22 pt template (76×44 px at 2×) for better visibility.
53
- private static let outputSize = NSSize(width: 38, height: 22)
54
- private static let outputScale: CGFloat = 2
55
- private static let canvasPx = Int(outputSize.width * outputScale)
56
-
57
- // Infinity symbol base template - loaded lazily from Resources
58
- private static let waveLogoTemplate: NSImage? = {
59
- guard let url = Bundle.main.url(forResource: "RunicMenubarIcon", withExtension: "svg"),
60
- let image = NSImage(contentsOf: url) else {
61
- return nil
62
- }
63
- return image
64
- }()
65
-
66
- private struct PixelGrid: Sendable {
67
- let scale: CGFloat
68
-
69
- func pt(_ px: Int) -> CGFloat {
70
- CGFloat(px) / self.scale
71
- }
72
-
73
- func rect(x: Int, y: Int, w: Int, h: Int) -> CGRect {
74
- CGRect(x: self.pt(x), y: self.pt(y), width: self.pt(w), height: self.pt(h))
75
- }
76
-
77
- func snapDelta(_ value: CGFloat) -> CGFloat {
78
- (value * self.scale).rounded() / self.scale
79
- }
80
- }
81
-
82
- private static let grid = PixelGrid(scale: outputScale)
83
-
84
- private struct IconCacheKey: Hashable {
85
- let primary: Int
86
- let weekly: Int
87
- let credits: Int
88
- let stale: Bool
89
- let style: Int
90
- let indicator: Int
91
- let appearance: Int
92
- let dataMode: Int
93
- }
94
-
95
- private final class IconCacheStore: @unchecked Sendable {
96
- private var cache: [IconCacheKey: NSImage] = [:]
97
- private var order: [IconCacheKey] = []
98
- private let lock = NSLock()
99
-
100
- func cachedIcon(for key: IconCacheKey) -> NSImage? {
101
- self.lock.lock()
102
- defer { self.lock.unlock() }
103
- guard let image = self.cache[key] else { return nil }
104
- if let idx = self.order.firstIndex(of: key) {
105
- self.order.remove(at: idx)
106
- self.order.append(key)
107
- }
108
- return image
109
- }
110
-
111
- func storeIcon(_ image: NSImage, for key: IconCacheKey, limit: Int) {
112
- self.lock.lock()
113
- defer { self.lock.unlock() }
114
- self.cache[key] = image
115
- self.order.removeAll { $0 == key }
116
- self.order.append(key)
117
- while self.order.count > limit {
118
- let oldest = self.order.removeFirst()
119
- self.cache.removeValue(forKey: oldest)
120
- }
121
- }
122
- }
123
-
124
- private static let iconCacheStore = IconCacheStore()
125
- private static let iconCacheLimit = PerformanceConstants.iconCacheSize
126
- private static let morphBucketCount = 200
127
- private static let morphCache = MorphCache(limit: PerformanceConstants.morphCacheSize)
128
-
129
- private final class MorphCache: @unchecked Sendable {
130
- private let cache = NSCache<NSNumber, NSImage>()
131
-
132
- init(limit: Int) {
133
- self.cache.countLimit = limit
134
- }
135
-
136
- func image(for key: NSNumber) -> NSImage? {
137
- self.cache.object(forKey: key)
138
- }
139
-
140
- func set(_ image: NSImage, for key: NSNumber) {
141
- self.cache.setObject(image, forKey: key)
142
- }
143
- }
144
-
145
- private struct RectPx: Hashable, Sendable {
146
- let x: Int
147
- let y: Int
148
- let w: Int
149
- let h: Int
150
-
151
- var midXPx: Int { self.x + self.w / 2 }
152
- var midYPx: Int { self.y + self.h / 2 }
153
-
154
- func rect() -> CGRect {
155
- Self.grid.rect(x: self.x, y: self.y, w: self.w, h: self.h)
156
- }
157
-
158
- private static let grid = IconRenderer.grid
159
- }
160
-
161
- private enum SigilStyle {
162
- case codex
163
- case claude
164
- case gemini
165
- case antigravity
166
- case cursor
167
- case factory
168
- case copilot
169
- case minimax
170
- case openrouter
171
- case groq
172
- case deepseek
173
- case fireworks
174
- case mistral
175
- case perplexity
176
- case kimi
177
- case auggie
178
- case together
179
- case cohere
180
- case xai
181
- case cerebras
182
- case sambanova
183
- case azure
184
- case bedrock
185
- case vertexai
186
- case zai
187
- case qwen
188
- case combined
189
- }
190
-
191
- // swiftlint:disable function_body_length
192
- static func makeIcon(
193
- primaryRemaining: Double?,
194
- weeklyRemaining: Double?,
195
- creditsRemaining: Double?,
196
- stale: Bool,
197
- style: IconStyle,
198
- blink: CGFloat = 0,
199
- wiggle: CGFloat = 0,
200
- tilt: CGFloat = 0,
201
- statusIndicator: ProviderStatusIndicator = .none,
202
- appearance: IconAppearance = .template,
203
- dataMode: IconDataMode = .remaining) -> NSImage
204
- {
205
- let shouldCache = blink <= 0.0001 && wiggle <= 0.0001 && tilt <= 0.0001
206
- let render = {
207
- self.renderImage(isTemplate: appearance == .template) {
208
- let topValue = primaryRemaining
209
- let bottomValue = weeklyRemaining
210
- let creditsRatio = creditsRemaining.map { min($0 / Self.creditsCap * 100, 100) }
211
- let pressure = Self.usagePressure(
212
- primary: topValue,
213
- weekly: bottomValue,
214
- credits: creditsRatio,
215
- dataMode: dataMode)
216
- let accentColor = Self.vibrantAccentColor(pressure: pressure, stale: stale)
217
- let baseColor = appearance == .template ? NSColor.labelColor : accentColor
218
-
219
- // Disable blink effect to prevent flicker
220
- let opacityMultiplier = 1.0
221
-
222
- let fillColor = baseColor.withAlphaComponent((stale ? 0.55 : 1.0) * opacityMultiplier)
223
- let baseAlpha: CGFloat = (stale ? 0.18 : (appearance == .template ? 0.28 : 0.38)) * opacityMultiplier
224
-
225
- let fillPercent = Self.iconFillPercent(
226
- primary: topValue,
227
- weekly: bottomValue,
228
- credits: creditsRatio,
229
- dataMode: dataMode)
230
-
231
- Self.drawWaveLogo(
232
- fillPercent: fillPercent,
233
- baseColor: baseColor.withAlphaComponent(baseAlpha),
234
- fillColor: fillColor)
235
-
236
- let overlayColor = appearance == .template
237
- ? NSColor.labelColor
238
- : NSColor.white.withAlphaComponent(0.92)
239
- Self.drawStatusOverlay(indicator: statusIndicator, color: overlayColor)
240
- }
241
- }
242
-
243
- if shouldCache {
244
- let key = IconCacheKey(
245
- primary: self.quantizedPercent(primaryRemaining),
246
- weekly: self.quantizedPercent(weeklyRemaining),
247
- credits: self.quantizedCredits(creditsRemaining),
248
- stale: stale,
249
- style: self.styleKey(style),
250
- indicator: self.indicatorKey(statusIndicator),
251
- appearance: self.appearanceKey(appearance),
252
- dataMode: self.dataModeKey(dataMode))
253
- if let cached = self.cachedIcon(for: key) {
254
- return cached
255
- }
256
- let image = render()
257
- self.storeIcon(image, for: key)
258
- return image
259
- }
260
-
261
- return render()
262
- }
263
-
264
- // swiftlint:enable function_body_length
265
-
266
- private static func sigilStyle(for style: IconStyle) -> SigilStyle {
267
- switch style {
268
- case .codex: return .codex
269
- case .claude: return .claude
270
- case .zai: return .zai
271
- case .gemini: return .gemini
272
- case .antigravity: return .antigravity
273
- case .cursor: return .cursor
274
- case .factory: return .factory
275
- case .copilot: return .copilot
276
- case .minimax: return .minimax
277
- case .openrouter: return .openrouter
278
- case .groq: return .groq
279
- case .deepseek: return .deepseek
280
- case .fireworks: return .fireworks
281
- case .mistral: return .mistral
282
- case .perplexity: return .perplexity
283
- case .kimi: return .kimi
284
- case .auggie: return .auggie
285
- case .together: return .together
286
- case .cohere: return .cohere
287
- case .xai: return .xai
288
- case .cerebras: return .cerebras
289
- case .sambanova: return .sambanova
290
- case .azure: return .azure
291
- case .bedrock: return .bedrock
292
- case .vertexai: return .vertexai
293
- case .qwen: return .qwen
294
- case .combined: return .combined
295
- }
296
- }
297
-
298
- private static func chamferedPath(rectPx: RectPx, chamferPx: Int) -> NSBezierPath {
299
- let rect = rectPx.rect()
300
- let chamfer = Self.grid.pt(chamferPx)
301
- let x = rect.minX
302
- let y = rect.minY
303
- let w = rect.width
304
- let h = rect.height
305
- let path = NSBezierPath()
306
- path.move(to: NSPoint(x: x + chamfer, y: y))
307
- path.line(to: NSPoint(x: x + w - chamfer, y: y))
308
- path.line(to: NSPoint(x: x + w, y: y + chamfer))
309
- path.line(to: NSPoint(x: x + w, y: y + h - chamfer))
310
- path.line(to: NSPoint(x: x + w - chamfer, y: y + h))
311
- path.line(to: NSPoint(x: x + chamfer, y: y + h))
312
- path.line(to: NSPoint(x: x, y: y + h - chamfer))
313
- path.line(to: NSPoint(x: x, y: y + chamfer))
314
- path.close()
315
- return path
316
- }
317
-
318
- private static func drawSigil(_ style: SigilStyle, in rectPx: RectPx) {
319
- guard let ctx = NSGraphicsContext.current?.cgContext else { return }
320
- let centerXPx = rectPx.midXPx
321
- let centerYPx = rectPx.y + rectPx.h / 2
322
-
323
- func point(x: Int, y: Int) -> NSPoint {
324
- NSPoint(x: Self.grid.pt(x), y: Self.grid.pt(y))
325
- }
326
-
327
- ctx.saveGState()
328
- ctx.setBlendMode(.clear)
329
- ctx.setShouldAntialias(true)
330
-
331
- switch style {
332
- case .codex:
333
- let eyeSizePx = 3
334
- let eyeOffsetPx = 5
335
- let leftEye = Self.grid.rect(
336
- x: centerXPx - eyeOffsetPx - eyeSizePx / 2,
337
- y: centerYPx - eyeSizePx / 2,
338
- w: eyeSizePx,
339
- h: eyeSizePx)
340
- let rightEye = Self.grid.rect(
341
- x: centerXPx + eyeOffsetPx - eyeSizePx / 2,
342
- y: centerYPx - eyeSizePx / 2,
343
- w: eyeSizePx,
344
- h: eyeSizePx)
345
- NSBezierPath(rect: leftEye).fill()
346
- NSBezierPath(rect: rightEye).fill()
347
- case .claude:
348
- let slitWidthPx = 2
349
- let slitHeightPx = 6
350
- let slitOffsetPx = 5
351
- let leftSlit = Self.grid.rect(
352
- x: centerXPx - slitOffsetPx - slitWidthPx / 2,
353
- y: centerYPx - slitHeightPx / 2,
354
- w: slitWidthPx,
355
- h: slitHeightPx)
356
- let rightSlit = Self.grid.rect(
357
- x: centerXPx + slitOffsetPx - slitWidthPx / 2,
358
- y: centerYPx - slitHeightPx / 2,
359
- w: slitWidthPx,
360
- h: slitHeightPx)
361
- NSBezierPath(rect: leftSlit).fill()
362
- NSBezierPath(rect: rightSlit).fill()
363
- case .gemini:
364
- let diamondRadiusPx = 4
365
- let cx = Self.grid.pt(centerXPx)
366
- let cy = Self.grid.pt(centerYPx)
367
- let r = Self.grid.pt(diamondRadiusPx)
368
- let path = NSBezierPath()
369
- path.move(to: NSPoint(x: cx, y: cy + r))
370
- path.line(to: NSPoint(x: cx + r, y: cy))
371
- path.line(to: NSPoint(x: cx, y: cy - r))
372
- path.line(to: NSPoint(x: cx - r, y: cy))
373
- path.close()
374
- path.fill()
375
- case .antigravity:
376
- let dotSizePx = 4
377
- let dotRect = Self.grid.rect(
378
- x: centerXPx - dotSizePx / 2,
379
- y: rectPx.y + rectPx.h - dotSizePx - 1,
380
- w: dotSizePx,
381
- h: dotSizePx)
382
- NSBezierPath(ovalIn: dotRect).fill()
383
- case .cursor:
384
- let tipPx = 6
385
- let baseXPx = centerXPx + 4
386
- let path = NSBezierPath()
387
- path.move(to: point(x: baseXPx, y: centerYPx))
388
- path.line(to: point(x: baseXPx - tipPx, y: centerYPx + tipPx / 2))
389
- path.line(to: point(x: baseXPx - tipPx, y: centerYPx - tipPx / 2))
390
- path.close()
391
- path.fill()
392
- case .factory:
393
- let armPx = 6
394
- let thicknessPx = 2
395
- let vertical = Self.grid.rect(
396
- x: centerXPx - thicknessPx / 2,
397
- y: centerYPx - armPx / 2,
398
- w: thicknessPx,
399
- h: armPx)
400
- let horizontal = Self.grid.rect(
401
- x: centerXPx - armPx / 2,
402
- y: centerYPx - thicknessPx / 2,
403
- w: armPx,
404
- h: thicknessPx)
405
- NSBezierPath(rect: vertical).fill()
406
- NSBezierPath(rect: horizontal).fill()
407
- case .copilot:
408
- let eyeRadiusPx = 3
409
- let eyeOffsetPx = 4
410
- let leftEye = Self.grid.rect(
411
- x: centerXPx - eyeOffsetPx - eyeRadiusPx,
412
- y: centerYPx - eyeRadiusPx,
413
- w: eyeRadiusPx * 2,
414
- h: eyeRadiusPx * 2)
415
- let rightEye = Self.grid.rect(
416
- x: centerXPx + eyeOffsetPx - eyeRadiusPx,
417
- y: centerYPx - eyeRadiusPx,
418
- w: eyeRadiusPx * 2,
419
- h: eyeRadiusPx * 2)
420
- let bridge = Self.grid.rect(
421
- x: centerXPx - 2,
422
- y: centerYPx - 1,
423
- w: 4,
424
- h: 2)
425
- NSBezierPath(ovalIn: leftEye).fill()
426
- NSBezierPath(ovalIn: rightEye).fill()
427
- NSBezierPath(rect: bridge).fill()
428
- case .minimax:
429
- let triWidthPx = 6
430
- let triHeightPx = 5
431
- let leftCx = centerXPx - 4
432
- let rightCx = centerXPx + 4
433
- let leftPath = NSBezierPath()
434
- leftPath.move(to: point(x: leftCx, y: centerYPx + triHeightPx / 2))
435
- leftPath.line(to: point(x: leftCx - triWidthPx / 2, y: centerYPx - triHeightPx / 2))
436
- leftPath.line(to: point(x: leftCx + triWidthPx / 2, y: centerYPx - triHeightPx / 2))
437
- leftPath.close()
438
- let rightPath = NSBezierPath()
439
- rightPath.move(to: point(x: rightCx, y: centerYPx + triHeightPx / 2))
440
- rightPath.line(to: point(x: rightCx - triWidthPx / 2, y: centerYPx - triHeightPx / 2))
441
- rightPath.line(to: point(x: rightCx + triWidthPx / 2, y: centerYPx - triHeightPx / 2))
442
- rightPath.close()
443
- leftPath.fill()
444
- rightPath.fill()
445
- case .openrouter:
446
- let outerRadiusPx = 4
447
- let innerRadiusPx = 2
448
- let outerRect = Self.grid.rect(
449
- x: centerXPx - outerRadiusPx,
450
- y: centerYPx - outerRadiusPx,
451
- w: outerRadiusPx * 2,
452
- h: outerRadiusPx * 2)
453
- let innerRect = Self.grid.rect(
454
- x: centerXPx - innerRadiusPx,
455
- y: centerYPx - innerRadiusPx,
456
- w: innerRadiusPx * 2,
457
- h: innerRadiusPx * 2)
458
- let ring = NSBezierPath()
459
- ring.appendOval(in: outerRect)
460
- ring.appendOval(in: innerRect)
461
- ring.windingRule = .evenOdd
462
- ring.fill()
463
- case .groq:
464
- let slashWidthPx = 2
465
- let slashHeightPx = 8
466
- let path = NSBezierPath()
467
- path.move(to: point(x: centerXPx - slashWidthPx, y: centerYPx - slashHeightPx / 2))
468
- path.line(to: point(x: centerXPx, y: centerYPx - slashHeightPx / 2))
469
- path.line(to: point(x: centerXPx + slashWidthPx, y: centerYPx + slashHeightPx / 2))
470
- path.line(to: point(x: centerXPx, y: centerYPx + slashHeightPx / 2))
471
- path.close()
472
- path.fill()
473
- case .deepseek:
474
- let radiusPx = 4
475
- let dotPx = 2
476
- let outerRect = Self.grid.rect(
477
- x: centerXPx - radiusPx,
478
- y: centerYPx - radiusPx,
479
- w: radiusPx * 2,
480
- h: radiusPx * 2)
481
- NSBezierPath(ovalIn: outerRect).fill()
482
- let innerRect = Self.grid.rect(
483
- x: centerXPx - dotPx / 2,
484
- y: centerYPx - dotPx / 2,
485
- w: dotPx,
486
- h: dotPx)
487
- NSBezierPath(ovalIn: innerRect).fill()
488
- case .fireworks:
489
- let armPx = 5
490
- let armWidthPx = 2
491
- let vertical = Self.grid.rect(
492
- x: centerXPx - armWidthPx / 2,
493
- y: centerYPx - armPx,
494
- w: armWidthPx,
495
- h: armPx * 2)
496
- let horizontal = Self.grid.rect(
497
- x: centerXPx - armPx,
498
- y: centerYPx - armWidthPx / 2,
499
- w: armPx * 2,
500
- h: armWidthPx)
501
- NSBezierPath(rect: vertical).fill()
502
- NSBezierPath(rect: horizontal).fill()
503
- let diag = NSBezierPath()
504
- diag.move(to: point(x: centerXPx - 3, y: centerYPx - 3))
505
- diag.line(to: point(x: centerXPx - 1, y: centerYPx - 3))
506
- diag.line(to: point(x: centerXPx + 3, y: centerYPx + 1))
507
- diag.line(to: point(x: centerXPx + 3, y: centerYPx + 3))
508
- diag.line(to: point(x: centerXPx + 1, y: centerYPx + 3))
509
- diag.line(to: point(x: centerXPx - 3, y: centerYPx - 1))
510
- diag.close()
511
- diag.fill()
512
- case .mistral:
513
- let barWidthPx = 2
514
- let barGapPx = 1
515
- let barHeightPx = 6
516
- let totalWidth = barWidthPx * 3 + barGapPx * 2
517
- let startX = centerXPx - totalWidth / 2
518
- for idx in 0 ..< 3 {
519
- let x = startX + idx * (barWidthPx + barGapPx)
520
- let rect = Self.grid.rect(
521
- x: x,
522
- y: centerYPx - barHeightPx / 2 + (idx == 1 ? 1 : 0),
523
- w: barWidthPx,
524
- h: barHeightPx - (idx == 1 ? 2 : 0))
525
- NSBezierPath(rect: rect).fill()
526
- }
527
- case .perplexity:
528
- let loopRadiusPx = 4
529
- let stemWidthPx = 2
530
- let stemHeightPx = 5
531
- let loopRect = Self.grid.rect(
532
- x: centerXPx - loopRadiusPx,
533
- y: centerYPx - loopRadiusPx,
534
- w: loopRadiusPx * 2,
535
- h: loopRadiusPx * 2)
536
- let innerRect = Self.grid.rect(
537
- x: centerXPx - 1,
538
- y: centerYPx - 1,
539
- w: 2,
540
- h: 2)
541
- let loop = NSBezierPath()
542
- loop.appendOval(in: loopRect)
543
- loop.appendOval(in: innerRect)
544
- loop.windingRule = .evenOdd
545
- loop.fill()
546
- let stemRect = Self.grid.rect(
547
- x: centerXPx + 1,
548
- y: centerYPx - loopRadiusPx - stemHeightPx + 1,
549
- w: stemWidthPx,
550
- h: stemHeightPx)
551
- NSBezierPath(rect: stemRect).fill()
552
- case .kimi:
553
- let ringRect = Self.grid.rect(
554
- x: centerXPx - 4,
555
- y: centerYPx - 4,
556
- w: 8,
557
- h: 8)
558
- let coreRect = Self.grid.rect(
559
- x: centerXPx - 1,
560
- y: centerYPx - 1,
561
- w: 2,
562
- h: 2)
563
- let ring = NSBezierPath()
564
- ring.appendOval(in: ringRect)
565
- ring.appendOval(in: coreRect)
566
- ring.windingRule = .evenOdd
567
- ring.fill()
568
- case .auggie:
569
- let armPx = 5
570
- let left = NSBezierPath()
571
- left.move(to: point(x: centerXPx - armPx, y: centerYPx - 3))
572
- left.line(to: point(x: centerXPx - 1, y: centerYPx + 4))
573
- left.line(to: point(x: centerXPx + 1, y: centerYPx + 4))
574
- left.line(to: point(x: centerXPx - 3, y: centerYPx - 3))
575
- left.close()
576
- left.fill()
577
- let right = NSBezierPath()
578
- right.move(to: point(x: centerXPx + armPx, y: centerYPx - 3))
579
- right.line(to: point(x: centerXPx + 1, y: centerYPx + 4))
580
- right.line(to: point(x: centerXPx - 1, y: centerYPx + 4))
581
- right.line(to: point(x: centerXPx + 3, y: centerYPx - 3))
582
- right.close()
583
- right.fill()
584
- case .together:
585
- let topRect = Self.grid.rect(
586
- x: centerXPx - 4,
587
- y: centerYPx + 1,
588
- w: 8,
589
- h: 2)
590
- let bottomRect = Self.grid.rect(
591
- x: centerXPx - 4,
592
- y: centerYPx - 3,
593
- w: 8,
594
- h: 2)
595
- let joinRect = Self.grid.rect(
596
- x: centerXPx - 1,
597
- y: centerYPx - 3,
598
- w: 2,
599
- h: 6)
600
- NSBezierPath(rect: topRect).fill()
601
- NSBezierPath(rect: bottomRect).fill()
602
- NSBezierPath(rect: joinRect).fill()
603
- case .cohere:
604
- let outerRect = Self.grid.rect(
605
- x: centerXPx - 4,
606
- y: centerYPx - 4,
607
- w: 8,
608
- h: 8)
609
- let innerRect = Self.grid.rect(
610
- x: centerXPx - 2,
611
- y: centerYPx - 2,
612
- w: 4,
613
- h: 4)
614
- let cPath = NSBezierPath()
615
- cPath.appendOval(in: outerRect)
616
- cPath.appendOval(in: innerRect)
617
- cPath.windingRule = .evenOdd
618
- cPath.fill()
619
- let cutRect = Self.grid.rect(
620
- x: centerXPx + 1,
621
- y: centerYPx - 4,
622
- w: 4,
623
- h: 8)
624
- NSBezierPath(rect: cutRect).fill()
625
- case .xai:
626
- let left = NSBezierPath()
627
- left.move(to: point(x: centerXPx - 4, y: centerYPx - 4))
628
- left.line(to: point(x: centerXPx - 1, y: centerYPx - 1))
629
- left.line(to: point(x: centerXPx - 4, y: centerYPx + 4))
630
- left.line(to: point(x: centerXPx - 2, y: centerYPx + 4))
631
- left.line(to: point(x: centerXPx + 1, y: centerYPx + 1))
632
- left.line(to: point(x: centerXPx + 4, y: centerYPx + 4))
633
- left.line(to: point(x: centerXPx + 4, y: centerYPx + 2))
634
- left.line(to: point(x: centerXPx + 1, y: centerYPx - 1))
635
- left.line(to: point(x: centerXPx + 4, y: centerYPx - 4))
636
- left.line(to: point(x: centerXPx + 2, y: centerYPx - 4))
637
- left.line(to: point(x: centerXPx - 1, y: centerYPx - 1))
638
- left.line(to: point(x: centerXPx - 4, y: centerYPx - 4))
639
- left.close()
640
- left.fill()
641
- case .cerebras:
642
- let outerRect = Self.grid.rect(
643
- x: centerXPx - 4,
644
- y: centerYPx - 4,
645
- w: 8,
646
- h: 8)
647
- let innerRect = Self.grid.rect(
648
- x: centerXPx - 2,
649
- y: centerYPx - 2,
650
- w: 4,
651
- h: 4)
652
- let ring = NSBezierPath()
653
- ring.appendOval(in: outerRect)
654
- ring.appendOval(in: innerRect)
655
- ring.windingRule = .evenOdd
656
- ring.fill()
657
- let notchRect = Self.grid.rect(
658
- x: centerXPx + 1,
659
- y: centerYPx - 2,
660
- w: 4,
661
- h: 4)
662
- NSBezierPath(rect: notchRect).fill()
663
- case .sambanova:
664
- let topArc = NSBezierPath()
665
- topArc.move(to: point(x: centerXPx - 4, y: centerYPx + 3))
666
- topArc.curve(
667
- to: point(x: centerXPx + 4, y: centerYPx + 3),
668
- controlPoint1: point(x: centerXPx - 1, y: centerYPx + 6),
669
- controlPoint2: point(x: centerXPx + 1, y: centerYPx + 6))
670
- topArc.line(to: point(x: centerXPx + 4, y: centerYPx + 1))
671
- topArc.curve(
672
- to: point(x: centerXPx - 4, y: centerYPx + 1),
673
- controlPoint1: point(x: centerXPx + 1, y: centerYPx + 4),
674
- controlPoint2: point(x: centerXPx - 1, y: centerYPx + 4))
675
- topArc.close()
676
- topArc.fill()
677
- let bottomArc = NSBezierPath()
678
- bottomArc.move(to: point(x: centerXPx + 4, y: centerYPx - 3))
679
- bottomArc.curve(
680
- to: point(x: centerXPx - 4, y: centerYPx - 3),
681
- controlPoint1: point(x: centerXPx + 1, y: centerYPx - 6),
682
- controlPoint2: point(x: centerXPx - 1, y: centerYPx - 6))
683
- bottomArc.line(to: point(x: centerXPx - 4, y: centerYPx - 1))
684
- bottomArc.curve(
685
- to: point(x: centerXPx + 4, y: centerYPx - 1),
686
- controlPoint1: point(x: centerXPx - 1, y: centerYPx - 4),
687
- controlPoint2: point(x: centerXPx + 1, y: centerYPx - 4))
688
- bottomArc.close()
689
- bottomArc.fill()
690
- case .azure:
691
- let outerRect = Self.grid.rect(
692
- x: centerXPx - 4,
693
- y: centerYPx - 4,
694
- w: 8,
695
- h: 8)
696
- let innerRect = Self.grid.rect(
697
- x: centerXPx - 2,
698
- y: centerYPx - 2,
699
- w: 4,
700
- h: 4)
701
- let ring = NSBezierPath()
702
- ring.appendOval(in: outerRect)
703
- ring.appendOval(in: innerRect)
704
- ring.windingRule = .evenOdd
705
- ring.fill()
706
- let slash = NSBezierPath()
707
- slash.move(to: point(x: centerXPx - 5, y: centerYPx - 1))
708
- slash.line(to: point(x: centerXPx - 3, y: centerYPx - 3))
709
- slash.line(to: point(x: centerXPx + 5, y: centerYPx + 5))
710
- slash.line(to: point(x: centerXPx + 3, y: centerYPx + 7))
711
- slash.close()
712
- slash.fill()
713
- case .bedrock:
714
- let top = NSBezierPath()
715
- top.move(to: point(x: centerXPx - 4, y: centerYPx + 2))
716
- top.line(to: point(x: centerXPx, y: centerYPx + 6))
717
- top.line(to: point(x: centerXPx + 4, y: centerYPx + 2))
718
- top.line(to: point(x: centerXPx + 2, y: centerYPx))
719
- top.line(to: point(x: centerXPx, y: centerYPx + 2))
720
- top.line(to: point(x: centerXPx - 2, y: centerYPx))
721
- top.close()
722
- top.fill()
723
- let bottom = NSBezierPath()
724
- bottom.move(to: point(x: centerXPx - 4, y: centerYPx - 2))
725
- bottom.line(to: point(x: centerXPx, y: centerYPx - 6))
726
- bottom.line(to: point(x: centerXPx + 4, y: centerYPx - 2))
727
- bottom.line(to: point(x: centerXPx + 2, y: centerYPx))
728
- bottom.line(to: point(x: centerXPx, y: centerYPx - 2))
729
- bottom.line(to: point(x: centerXPx - 2, y: centerYPx))
730
- bottom.close()
731
- bottom.fill()
732
- case .vertexai:
733
- let diamond = NSBezierPath()
734
- diamond.move(to: point(x: centerXPx, y: centerYPx + 6))
735
- diamond.line(to: point(x: centerXPx + 5, y: centerYPx))
736
- diamond.line(to: point(x: centerXPx, y: centerYPx - 6))
737
- diamond.line(to: point(x: centerXPx - 5, y: centerYPx))
738
- diamond.close()
739
- diamond.fill()
740
- case .zai:
741
- let slashGapPx = 3
742
- let slashWidthPx = 2
743
- let slashHeightPx = 7
744
- let leftPath = NSBezierPath()
745
- leftPath.move(to: point(x: centerXPx - slashGapPx, y: centerYPx - slashHeightPx / 2))
746
- leftPath.line(to: point(x: centerXPx - slashGapPx + slashWidthPx, y: centerYPx - slashHeightPx / 2))
747
- leftPath.line(to: point(x: centerXPx - slashGapPx + slashWidthPx + 2, y: centerYPx + slashHeightPx / 2))
748
- leftPath.line(to: point(x: centerXPx - slashGapPx + 2, y: centerYPx + slashHeightPx / 2))
749
- leftPath.close()
750
- let rightPath = NSBezierPath()
751
- rightPath.move(to: point(x: centerXPx + slashGapPx, y: centerYPx - slashHeightPx / 2))
752
- rightPath.line(to: point(x: centerXPx + slashGapPx + slashWidthPx, y: centerYPx - slashHeightPx / 2))
753
- rightPath.line(to: point(x: centerXPx + slashGapPx + slashWidthPx + 2, y: centerYPx + slashHeightPx / 2))
754
- rightPath.line(to: point(x: centerXPx + slashGapPx + 2, y: centerYPx + slashHeightPx / 2))
755
- rightPath.close()
756
- leftPath.fill()
757
- rightPath.fill()
758
- case .qwen:
759
- // Stylised "Q" ring -- outer circle punch with inner cutout.
760
- let ringOuter = 5
761
- let ringInner = 3
762
- let outerRect = Self.grid.rect(
763
- x: centerXPx - ringOuter,
764
- y: centerYPx - ringOuter,
765
- w: ringOuter * 2,
766
- h: ringOuter * 2)
767
- let innerRect = Self.grid.rect(
768
- x: centerXPx - ringInner,
769
- y: centerYPx - ringInner,
770
- w: ringInner * 2,
771
- h: ringInner * 2)
772
- NSBezierPath(ovalIn: outerRect).fill()
773
- ctx.setBlendMode(.normal)
774
- NSGraphicsContext.current?.cgContext.setFillColor(NSColor.clear.cgColor)
775
- ctx.setBlendMode(.clear)
776
- NSBezierPath(ovalIn: innerRect).fill()
777
- case .combined:
778
- let diamondRadiusPx = 4
779
- let cx = Self.grid.pt(centerXPx)
780
- let cy = Self.grid.pt(centerYPx)
781
- let r = Self.grid.pt(diamondRadiusPx)
782
- let path = NSBezierPath()
783
- path.move(to: NSPoint(x: cx, y: cy + r))
784
- path.line(to: NSPoint(x: cx + r, y: cy))
785
- path.line(to: NSPoint(x: cx, y: cy - r))
786
- path.line(to: NSPoint(x: cx - r, y: cy))
787
- path.close()
788
- path.fill()
789
- }
790
-
791
- ctx.restoreGState()
792
- }
793
-
794
- /// Morph helper: unbraids a simplified knot into our bar icon.
795
- static func makeMorphIcon(
796
- progress: Double,
797
- style: IconStyle,
798
- appearance: IconAppearance = .template) -> NSImage
799
- {
800
- let clamped = max(0, min(progress, 1))
801
- let key = self.morphCacheKey(progress: clamped, style: style, appearance: appearance)
802
- if let cached = self.morphCache.image(for: key) {
803
- return cached
804
- }
805
- let baseColor = appearance == .template
806
- ? NSColor.labelColor
807
- : self.vibrantAccentColor(pressure: 0.15, stale: false)
808
- let image = self.renderImage(isTemplate: appearance == .template) {
809
- self.drawUnbraidMorph(t: clamped, style: style, color: baseColor, appearance: appearance)
810
- }
811
- self.morphCache.set(image, for: key)
812
- return image
813
- }
814
-
815
- private static func iconFillPercent(
816
- primary: Double?,
817
- weekly: Double?,
818
- credits: Double?,
819
- dataMode: IconDataMode) -> Double
820
- {
821
- let value = primary ?? weekly ?? credits
822
- guard let value else { return 0 }
823
- let clamped = max(0, min(value / 100, 1))
824
- switch dataMode {
825
- case .remaining:
826
- return clamped
827
- case .used:
828
- return clamped
829
- }
830
- }
831
-
832
- private static func drawWaveLogo(
833
- fillPercent: Double,
834
- baseColor: NSColor,
835
- fillColor: NSColor)
836
- {
837
- guard let waveLogo = self.waveLogoTemplate,
838
- let ctx = NSGraphicsContext.current?.cgContext else {
839
- return
840
- }
841
-
842
- let rect = CGRect(origin: .zero, size: self.outputSize)
843
- let clamped = max(0, min(fillPercent, 1))
844
-
845
- // Base silhouette
846
- ctx.saveGState()
847
- waveLogo.draw(in: rect, from: .zero, operation: .sourceOver, fraction: 1.0)
848
- ctx.setBlendMode(.sourceIn)
849
- baseColor.setFill()
850
- ctx.fill(rect)
851
- ctx.restoreGState()
852
-
853
- // Usage fill: left-to-right reveal within the infinity symbol.
854
- guard clamped > 0 else { return }
855
- ctx.saveGState()
856
- waveLogo.draw(in: rect, from: .zero, operation: .sourceOver, fraction: 1.0)
857
- ctx.setBlendMode(.sourceIn)
858
- fillColor.setFill()
859
- let fillRect = CGRect(x: rect.minX, y: rect.minY, width: rect.width * clamped, height: rect.height)
860
- ctx.fill(fillRect)
861
- ctx.restoreGState()
862
- }
863
-
864
- private static func usagePressure(
865
- primary: Double?,
866
- weekly: Double?,
867
- credits: Double?,
868
- dataMode: IconDataMode) -> Double
869
- {
870
- let values = [primary, weekly, credits].compactMap { $0 }
871
- guard !values.isEmpty else { return 0 }
872
- let normalized = values.map { max(0, min($0 / 100, 1)) }
873
- switch dataMode {
874
- case .used:
875
- return normalized.max() ?? 0
876
- case .remaining:
877
- let minRemaining = normalized.min() ?? 1
878
- return max(0, min(1, 1 - minRemaining))
879
- }
880
- }
881
-
882
- private static func vibrantAccentColor(pressure: Double, stale: Bool) -> NSColor {
883
- let clamped = CGFloat(max(0, min(pressure, 1)))
884
- let safe = NSColor(calibratedRed: 0.12, green: 0.86, blue: 0.78, alpha: 1)
885
- let warn = NSColor(calibratedRed: 1.00, green: 0.72, blue: 0.30, alpha: 1)
886
- let hot = NSColor(calibratedRed: 1.00, green: 0.31, blue: 0.44, alpha: 1)
887
- let color: NSColor
888
- if clamped <= 0.5 {
889
- color = self.mixColor(safe, warn, p: clamped / 0.5)
890
- } else {
891
- color = self.mixColor(warn, hot, p: (clamped - 0.5) / 0.5)
892
- }
893
- if stale {
894
- return color.withAlphaComponent(0.72)
895
- }
896
- return color
897
- }
898
-
899
- private static func mixColor(_ a: NSColor, _ b: NSColor, p: CGFloat) -> NSColor {
900
- guard let ca = a.usingColorSpace(.deviceRGB),
901
- let cb = b.usingColorSpace(.deviceRGB)
902
- else {
903
- return a
904
- }
905
- let clamped = max(0, min(p, 1))
906
- let red = ca.redComponent + (cb.redComponent - ca.redComponent) * clamped
907
- let green = ca.greenComponent + (cb.greenComponent - ca.greenComponent) * clamped
908
- let blue = ca.blueComponent + (cb.blueComponent - ca.blueComponent) * clamped
909
- let alpha = ca.alphaComponent + (cb.alphaComponent - ca.alphaComponent) * clamped
910
- return NSColor(calibratedRed: red, green: green, blue: blue, alpha: alpha)
911
- }
912
-
913
- private static func quantizedPercent(_ value: Double?) -> Int {
914
- guard let value else { return -1 }
915
- return Int((value * 10).rounded())
916
- }
917
-
918
- private static func quantizedCredits(_ value: Double?) -> Int {
919
- guard let value else { return -1 }
920
- let clamped = max(0, min(value, self.creditsCap))
921
- return Int((clamped * 10).rounded())
922
- }
923
-
924
- private static func styleKey(_ style: IconStyle) -> Int {
925
- switch style {
926
- case .codex: 0
927
- case .claude: 1
928
- case .zai: 2
929
- case .gemini: 3
930
- case .antigravity: 4
931
- case .cursor: 5
932
- case .factory: 6
933
- case .copilot: 7
934
- case .minimax: 8
935
- case .openrouter: 9
936
- case .groq: 10
937
- case .deepseek: 11
938
- case .fireworks: 12
939
- case .mistral: 13
940
- case .perplexity: 14
941
- case .kimi: 15
942
- case .auggie: 16
943
- case .together: 17
944
- case .cohere: 18
945
- case .xai: 19
946
- case .cerebras: 20
947
- case .sambanova: 21
948
- case .azure: 22
949
- case .bedrock: 23
950
- case .vertexai: 24
951
- case .qwen: 25
952
- case .combined: 99
953
- }
954
- }
955
-
956
- private static func indicatorKey(_ indicator: ProviderStatusIndicator) -> Int {
957
- switch indicator {
958
- case .none: 0
959
- case .minor: 1
960
- case .major: 2
961
- case .critical: 3
962
- case .maintenance: 4
963
- case .unknown: 5
964
- }
965
- }
966
-
967
- private static func appearanceKey(_ appearance: IconAppearance) -> Int {
968
- switch appearance {
969
- case .template: 0
970
- case .vibrant: 1
971
- }
972
- }
973
-
974
- private static func dataModeKey(_ mode: IconDataMode) -> Int {
975
- switch mode {
976
- case .remaining: 0
977
- case .used: 1
978
- }
979
- }
980
-
981
- private static func morphCacheKey(
982
- progress: Double,
983
- style: IconStyle,
984
- appearance: IconAppearance) -> NSNumber
985
- {
986
- let bucket = Int((progress * Double(self.morphBucketCount)).rounded())
987
- let key = self.styleKey(style) * 10000 + self.appearanceKey(appearance) * 1000 + bucket
988
- return NSNumber(value: key)
989
- }
990
-
991
- private static func cachedIcon(for key: IconCacheKey) -> NSImage? {
992
- self.iconCacheStore.cachedIcon(for: key)
993
- }
994
-
995
- private static func storeIcon(_ image: NSImage, for key: IconCacheKey) {
996
- self.iconCacheStore.storeIcon(image, for: key, limit: self.iconCacheLimit)
997
- }
998
-
999
- private static func drawUnbraidMorph(
1000
- t: Double,
1001
- style: IconStyle,
1002
- color: NSColor,
1003
- appearance: IconAppearance)
1004
- {
1005
- let t = CGFloat(max(0, min(t, 1)))
1006
- let size = Self.baseSize
1007
- let center = CGPoint(x: size.width / 2, y: size.height / 2)
1008
- let baseColor = color
1009
-
1010
- struct Segment {
1011
- let startCenter: CGPoint
1012
- let endCenter: CGPoint
1013
- let startAngle: CGFloat
1014
- let endAngle: CGFloat
1015
- let startLength: CGFloat
1016
- let endLength: CGFloat
1017
- let startThickness: CGFloat
1018
- let endThickness: CGFloat
1019
- let fadeOut: Bool
1020
- }
1021
-
1022
- let segments: [Segment] = [
1023
- // Upper ribbon -> top bar
1024
- .init(
1025
- startCenter: center.offset(dx: 0, dy: 2),
1026
- endCenter: CGPoint(x: center.x, y: 9.0),
1027
- startAngle: -30,
1028
- endAngle: 0,
1029
- startLength: 16,
1030
- endLength: 14,
1031
- startThickness: 3.4,
1032
- endThickness: 3.0,
1033
- fadeOut: false),
1034
- // Lower ribbon -> bottom bar
1035
- .init(
1036
- startCenter: center.offset(dx: 0, dy: -2),
1037
- endCenter: CGPoint(x: center.x, y: 4.0),
1038
- startAngle: 210,
1039
- endAngle: 0,
1040
- startLength: 16,
1041
- endLength: 12,
1042
- startThickness: 3.4,
1043
- endThickness: 2.4,
1044
- fadeOut: false),
1045
- // Side ribbon fades away
1046
- .init(
1047
- startCenter: center,
1048
- endCenter: center.offset(dx: 0, dy: 6),
1049
- startAngle: 90,
1050
- endAngle: 0,
1051
- startLength: 16,
1052
- endLength: 8,
1053
- startThickness: 3.4,
1054
- endThickness: 1.8,
1055
- fadeOut: true),
1056
- ]
1057
-
1058
- for seg in segments {
1059
- let p = seg.fadeOut ? t * 1.1 : t
1060
- let c = seg.startCenter.lerp(to: seg.endCenter, p: p)
1061
- let angle = seg.startAngle.lerp(to: seg.endAngle, p: p)
1062
- let length = seg.startLength.lerp(to: seg.endLength, p: p)
1063
- let thickness = seg.startThickness.lerp(to: seg.endThickness, p: p)
1064
- let alpha = seg.fadeOut ? (1 - p) : 1
1065
-
1066
- self.drawRoundedRibbon(
1067
- center: c,
1068
- length: length,
1069
- thickness: thickness,
1070
- angle: angle,
1071
- color: baseColor.withAlphaComponent(alpha))
1072
- }
1073
-
1074
- // Cross-fade in bar fill emphasis near the end of the morph.
1075
- if t > 0.55 {
1076
- let barT = (t - 0.55) / 0.45
1077
- let bars = self.makeIcon(
1078
- primaryRemaining: 100,
1079
- weeklyRemaining: 100,
1080
- creditsRemaining: nil,
1081
- stale: false,
1082
- style: style,
1083
- appearance: appearance)
1084
- bars.draw(in: CGRect(origin: .zero, size: size), from: .zero, operation: .sourceOver, fraction: barT)
1085
- }
1086
- }
1087
-
1088
- private static func drawRoundedRibbon(
1089
- center: CGPoint,
1090
- length: CGFloat,
1091
- thickness: CGFloat,
1092
- angle: CGFloat,
1093
- color: NSColor)
1094
- {
1095
- var transform = AffineTransform.identity
1096
- transform.translate(x: center.x, y: center.y)
1097
- transform.rotate(byDegrees: angle)
1098
- transform.translate(x: -center.x, y: -center.y)
1099
-
1100
- let rect = CGRect(
1101
- x: center.x - length / 2,
1102
- y: center.y - thickness / 2,
1103
- width: length,
1104
- height: thickness)
1105
-
1106
- let path = NSBezierPath(roundedRect: rect, xRadius: thickness / 2, yRadius: thickness / 2)
1107
- path.transform(using: transform)
1108
- color.setFill()
1109
- path.fill()
1110
- }
1111
-
1112
- private static func drawStatusOverlay(indicator: ProviderStatusIndicator, color: NSColor) {
1113
- guard indicator.hasIssue else { return }
1114
- switch indicator {
1115
- case .minor, .maintenance:
1116
- let size: CGFloat = 4
1117
- let rect = Self.snapRect(
1118
- x: Self.baseSize.width - size - 2,
1119
- y: 2,
1120
- width: size,
1121
- height: size)
1122
- let path = NSBezierPath(ovalIn: rect)
1123
- color.setFill()
1124
- path.fill()
1125
- case .major, .critical, .unknown:
1126
- let lineRect = Self.snapRect(
1127
- x: Self.baseSize.width - 6,
1128
- y: 4,
1129
- width: 2.0,
1130
- height: 6)
1131
- let linePath = NSBezierPath(roundedRect: lineRect, xRadius: 1, yRadius: 1)
1132
- color.setFill()
1133
- linePath.fill()
1134
-
1135
- let dotRect = Self.snapRect(
1136
- x: Self.baseSize.width - 6,
1137
- y: 2,
1138
- width: 2.0,
1139
- height: 2.0)
1140
- NSBezierPath(ovalIn: dotRect).fill()
1141
- case .none:
1142
- break
1143
- }
1144
- }
1145
-
1146
- private static func withScaledContext(_ draw: () -> Void) {
1147
- guard let ctx = NSGraphicsContext.current?.cgContext else {
1148
- draw()
1149
- return
1150
- }
1151
- ctx.saveGState()
1152
- ctx.setShouldAntialias(true)
1153
- ctx.interpolationQuality = .none
1154
- draw()
1155
- ctx.restoreGState()
1156
- }
1157
-
1158
- private static func snap(_ value: CGFloat) -> CGFloat {
1159
- (value * self.outputScale).rounded() / self.outputScale
1160
- }
1161
-
1162
- private static func snapRect(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -> CGRect {
1163
- CGRect(x: self.snap(x), y: self.snap(y), width: self.snap(width), height: self.snap(height))
1164
- }
1165
-
1166
- private static func renderImage(isTemplate: Bool, _ draw: () -> Void) -> NSImage {
1167
- let image = NSImage(size: Self.outputSize)
1168
-
1169
- if let rep = NSBitmapImageRep(
1170
- bitmapDataPlanes: nil,
1171
- pixelsWide: Int(Self.outputSize.width * Self.outputScale),
1172
- pixelsHigh: Int(Self.outputSize.height * Self.outputScale),
1173
- bitsPerSample: 8,
1174
- samplesPerPixel: 4,
1175
- hasAlpha: true,
1176
- isPlanar: false,
1177
- colorSpaceName: .deviceRGB,
1178
- bytesPerRow: 0,
1179
- bitsPerPixel: 0)
1180
- {
1181
- rep.size = Self.outputSize // points
1182
- image.addRepresentation(rep)
1183
-
1184
- NSGraphicsContext.saveGraphicsState()
1185
- if let ctx = NSGraphicsContext(bitmapImageRep: rep) {
1186
- NSGraphicsContext.current = ctx
1187
- Self.withScaledContext(draw)
1188
- }
1189
- NSGraphicsContext.restoreGraphicsState()
1190
- } else {
1191
- // Fallback to legacy focus if the bitmap rep fails for any reason.
1192
- image.lockFocus()
1193
- Self.withScaledContext(draw)
1194
- image.unlockFocus()
1195
- }
1196
-
1197
- image.isTemplate = isTemplate
1198
- return image
1199
- }
1200
- }
1201
-
1202
- extension CGPoint {
1203
- fileprivate func lerp(to other: CGPoint, p: CGFloat) -> CGPoint {
1204
- CGPoint(x: self.x + (other.x - self.x) * p, y: self.y + (other.y - self.y) * p)
1205
- }
1206
-
1207
- fileprivate func offset(dx: CGFloat, dy: CGFloat) -> CGPoint {
1208
- CGPoint(x: self.x + dx, y: self.y + dy)
1209
- }
1210
- }
1211
-
1212
- extension CGFloat {
1213
- fileprivate func lerp(to other: CGFloat, p: CGFloat) -> CGFloat {
1214
- self + (other - self) * p
1215
- }
1216
- }