@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.
- package/CHANGELOG.md +11 -0
- package/LICENSE +1 -1
- package/README.md +47 -29
- package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
- package/RuniciOS/Views/ProviderDetailView.swift +9 -9
- package/RuniciOS/Views/ProviderListView.swift +10 -10
- package/RuniciOS/Widgets/LargeWidget.swift +14 -14
- package/RuniciOS/Widgets/LiveActivity.swift +19 -19
- package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
- package/RuniciOS/Widgets/MediumWidget.swift +10 -10
- package/RuniciOS/Widgets/RunicWidget.swift +5 -5
- package/RuniciOS/Widgets/SmallWidget.swift +9 -9
- package/Scripts/package_app.sh +2 -2
- package/Sources/Runic/About.swift +3 -3
- package/Sources/Runic/AppNotifications.swift +5 -3
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +17 -4
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +47 -14
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
- package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
- package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
- package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
- package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
- package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
- package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +3 -6
- package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
- package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
- package/Sources/Runic/Core/RunicColors.swift +6 -6
- package/Sources/Runic/Core/RunicSpacing.swift +7 -7
- package/Sources/Runic/Core/RunicTypography.swift +206 -0
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/SettingsStore.swift +99 -52
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
- package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore.swift +234 -167
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +10 -36
- package/Sources/Runic/Core/UsageExporter.swift +8 -4
- package/Sources/Runic/ProviderRegistry.swift +1 -1
- package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
- package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
- package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
- package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/provider-context-windows.json +20 -0
- package/Sources/Runic/RunicApp.swift +46 -36
- package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
- package/Sources/Runic/UsagePaceText.swift +1 -1
- package/Sources/Runic/UsageProgressBar.swift +13 -9
- package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
- package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
- package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
- package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +15 -20
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
- package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
- package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +198 -58
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +28 -76
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
- package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
- package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
- package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
- package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +108 -33
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
- package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
- package/Sources/RunicCLI/CLIEntry.swift +83 -42
- package/Sources/RunicCLI/CLIRenderer.swift +1 -1
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
- package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
- package/Sources/RunicClaudeWebProbe/main.swift +1 -1
- package/Sources/RunicCore/AlertRuleStore.swift +20 -20
- package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
- package/Sources/RunicCore/CostUsageModels.swift +1 -1
- package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +80 -71
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
- package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
- package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
- package/Sources/RunicCore/Logging/OSLogLogHandler.swift +6 -6
- package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
- package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
- package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
- package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
- package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +5 -3
- package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
- package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
- package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +1 -0
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +12 -1
- package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
- package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +1 -0
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
- package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +3 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +6 -2
- package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +8 -11
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
- package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +25 -16
- package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
- package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +27 -16
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
- package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
- package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +51 -30
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
- package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
- package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
- package/Sources/RunicCore/Team/TeamModels.swift +111 -110
- package/Sources/RunicCore/Team/TeamStore.swift +126 -109
- package/Sources/RunicCore/UsageFetcher.swift +2 -2
- package/Sources/RunicCore/UsageFormatter.swift +25 -25
- package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +141 -29
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +59 -5
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +161 -0
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
- package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
- package/Tests/RunicTests/AppDelegateTests.swift +8 -3
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
- package/Tests/RunicTests/CLICostTests.swift +6 -7
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
- package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
- package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
- package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
- package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
- package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
- package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
- package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
- package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
- package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
- package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
- package/Tests/RunicTests/InstallOriginTests.swift +1 -2
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
- package/Tests/RunicTests/LiveAccountTests.swift +3 -3
- package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
- package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
- package/Tests/RunicTests/PathBuilderTests.swift +14 -15
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
- package/Tests/RunicTests/RunicTests.swift +6 -7
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
- package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
- package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
- package/Tests/RunicTests/StatusMenuTests.swift +63 -22
- package/Tests/RunicTests/StatusProbeTests.swift +28 -29
- package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
- package/Tests/RunicTests/TextParsingTests.swift +1 -2
- package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
- package/Tests/RunicTests/UsagePaceTests.swift +5 -6
- package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
- package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
- package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
- package/appcast.xml +36 -37
- package/package.json +1 -1
- package/runic.png +0 -0
- package/runic.svg +15 -0
- package/version.env +2 -2
|
@@ -7,45 +7,45 @@ import SwiftUI
|
|
|
7
7
|
/// These values are carefully tuned for readability and visual hierarchy.
|
|
8
8
|
enum MenuCardMetrics {
|
|
9
9
|
/// Horizontal padding on left/right edges of card content
|
|
10
|
-
static let horizontalPadding: CGFloat = RunicSpacing.sm
|
|
10
|
+
static let horizontalPadding: CGFloat = RunicSpacing.sm // 12
|
|
11
11
|
|
|
12
12
|
/// Top padding for the header section (provider name, email)
|
|
13
|
-
static let headerTopPadding: CGFloat = RunicSpacing.xxs
|
|
13
|
+
static let headerTopPadding: CGFloat = RunicSpacing.xxs // 4
|
|
14
14
|
|
|
15
15
|
/// Bottom padding for the header section
|
|
16
|
-
static let headerBottomPadding: CGFloat = RunicSpacing.xxs
|
|
16
|
+
static let headerBottomPadding: CGFloat = RunicSpacing.xxs // 4
|
|
17
17
|
|
|
18
18
|
/// Top padding for content sections (usage, credits, cost)
|
|
19
|
-
static let sectionTopPadding: CGFloat = RunicSpacing.xs
|
|
19
|
+
static let sectionTopPadding: CGFloat = RunicSpacing.xs // 8
|
|
20
20
|
|
|
21
21
|
/// Bottom padding for content sections
|
|
22
|
-
static let sectionBottomPadding: CGFloat = RunicSpacing.xxs
|
|
22
|
+
static let sectionBottomPadding: CGFloat = RunicSpacing.xxs // 4
|
|
23
23
|
|
|
24
24
|
/// Vertical spacing between major sections (e.g., Session vs Weekly)
|
|
25
|
-
static let sectionSpacing: CGFloat = RunicSpacing.xs
|
|
25
|
+
static let sectionSpacing: CGFloat = RunicSpacing.xs // 8
|
|
26
26
|
|
|
27
27
|
/// Spacing between related blocks of content
|
|
28
|
-
static let blockSpacing: CGFloat = RunicSpacing.xs
|
|
28
|
+
static let blockSpacing: CGFloat = RunicSpacing.xs // 8
|
|
29
29
|
|
|
30
30
|
/// Spacing between individual text lines within a section
|
|
31
|
-
static let lineSpacing: CGFloat = RunicSpacing.xxs
|
|
31
|
+
static let lineSpacing: CGFloat = RunicSpacing.xxs // 4
|
|
32
32
|
|
|
33
33
|
/// Base padding for menu items
|
|
34
|
-
static let menuItemBasePadding: CGFloat = RunicSpacing.xxs
|
|
34
|
+
static let menuItemBasePadding: CGFloat = RunicSpacing.xxs // 4
|
|
35
35
|
|
|
36
36
|
/// Additional padding for text with descenders (y, g, p, q, j)
|
|
37
|
-
static let menuItemDescenderPadding: CGFloat = RunicSpacing.xxxs
|
|
37
|
+
static let menuItemDescenderPadding: CGFloat = RunicSpacing.xxxs // 2
|
|
38
38
|
|
|
39
39
|
/// Tail padding at the end of sections
|
|
40
|
-
static let tailPadding: CGFloat = RunicSpacing.xxs
|
|
41
|
-
static let metricCardPadding: CGFloat = RunicSpacing.sm
|
|
40
|
+
static let tailPadding: CGFloat = RunicSpacing.xxs // 4
|
|
41
|
+
static let metricCardPadding: CGFloat = RunicSpacing.sm // 8
|
|
42
42
|
static let metricCardCornerRadius: CGFloat = RunicCornerRadius.sm
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/// SwiftUI card used inside the NSMenu to mirror Apple's rich menu panels.
|
|
46
46
|
struct UsageMenuCardView: View {
|
|
47
47
|
struct Model {
|
|
48
|
-
enum PercentStyle: String
|
|
48
|
+
enum PercentStyle: String {
|
|
49
49
|
case left
|
|
50
50
|
case used
|
|
51
51
|
|
|
@@ -83,7 +83,7 @@ struct UsageMenuCardView: View {
|
|
|
83
83
|
case error
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
struct TokenUsageSection
|
|
86
|
+
struct TokenUsageSection {
|
|
87
87
|
let sessionLine: String
|
|
88
88
|
let sessionDetailLine: String?
|
|
89
89
|
let monthLine: String
|
|
@@ -94,13 +94,13 @@ struct UsageMenuCardView: View {
|
|
|
94
94
|
let errorCopyText: String?
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
struct ProviderCostSection
|
|
97
|
+
struct ProviderCostSection {
|
|
98
98
|
let title: String
|
|
99
99
|
let percentUsed: Double
|
|
100
100
|
let spendLine: String
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
struct InsightsSection
|
|
103
|
+
struct InsightsSection {
|
|
104
104
|
let title: String
|
|
105
105
|
let todayLine: String?
|
|
106
106
|
let todayDetail: String?
|
|
@@ -157,11 +157,6 @@ struct UsageMenuCardView: View {
|
|
|
157
157
|
let width: CGFloat
|
|
158
158
|
@Environment(\.menuItemHighlighted) private var isHighlighted
|
|
159
159
|
|
|
160
|
-
init(model: Model, width: CGFloat) {
|
|
161
|
-
self.model = model
|
|
162
|
-
self.width = width
|
|
163
|
-
}
|
|
164
|
-
|
|
165
160
|
var body: some View {
|
|
166
161
|
VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
|
|
167
162
|
UsageMenuCardHeaderView(model: self.model)
|
|
@@ -181,7 +176,7 @@ struct UsageMenuCardView: View {
|
|
|
181
176
|
} else {
|
|
182
177
|
let hasUsage = !self.model.metrics.isEmpty
|
|
183
178
|
let includeSummarySections = self.model.menuMode != .glance
|
|
184
|
-
let includeInsightSections = self.model.menuMode ==
|
|
179
|
+
let includeInsightSections = self.model.menuMode == .operator
|
|
185
180
|
let hasCredits = includeSummarySections && self.model.creditsText != nil
|
|
186
181
|
let hasExtraUsage = includeSummarySections && self.model.providerCost != nil
|
|
187
182
|
let hasTokenCost = includeSummarySections && self.model.tokenUsage != nil
|
|
@@ -229,28 +224,28 @@ struct UsageMenuCardView: View {
|
|
|
229
224
|
if hasTokenCost, let tokenUsage = self.model.tokenUsage {
|
|
230
225
|
VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
|
|
231
226
|
Text("Cost")
|
|
232
|
-
.font(.body)
|
|
227
|
+
.font(RunicFont.body)
|
|
233
228
|
.fontWeight(.medium)
|
|
234
229
|
Text(tokenUsage.sessionLine)
|
|
235
|
-
.font(.footnote)
|
|
230
|
+
.font(RunicFont.footnote)
|
|
236
231
|
if let sessionDetail = tokenUsage.sessionDetailLine {
|
|
237
232
|
Text(sessionDetail)
|
|
238
|
-
.font(.footnote)
|
|
233
|
+
.font(RunicFont.footnote)
|
|
239
234
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
240
235
|
}
|
|
241
236
|
Text(tokenUsage.monthLine)
|
|
242
|
-
.font(.footnote)
|
|
237
|
+
.font(RunicFont.footnote)
|
|
243
238
|
if let monthDetail = tokenUsage.monthDetailLine {
|
|
244
239
|
Text(monthDetail)
|
|
245
|
-
.font(.footnote)
|
|
240
|
+
.font(RunicFont.footnote)
|
|
246
241
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
247
242
|
}
|
|
248
243
|
Text(tokenUsage.updatedLine)
|
|
249
|
-
.font(.caption)
|
|
244
|
+
.font(RunicFont.caption)
|
|
250
245
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
251
246
|
if let hint = tokenUsage.hintLine, !hint.isEmpty {
|
|
252
247
|
Text(hint)
|
|
253
|
-
.font(.footnote)
|
|
248
|
+
.font(RunicFont.footnote)
|
|
254
249
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
255
250
|
.lineLimit(4)
|
|
256
251
|
.fixedSize(horizontal: false, vertical: true)
|
|
@@ -258,10 +253,10 @@ struct UsageMenuCardView: View {
|
|
|
258
253
|
if let error = tokenUsage.errorLine, !error.isEmpty {
|
|
259
254
|
HStack(alignment: .top, spacing: RunicSpacing.xs) {
|
|
260
255
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
261
|
-
.font(.footnote)
|
|
256
|
+
.font(RunicFont.footnote)
|
|
262
257
|
.foregroundStyle(RunicColors.error)
|
|
263
258
|
Text(error)
|
|
264
|
-
.font(.footnote)
|
|
259
|
+
.font(RunicFont.footnote)
|
|
265
260
|
.foregroundStyle(MenuHighlightStyle.error(self.isHighlighted))
|
|
266
261
|
.lineLimit(4)
|
|
267
262
|
.fixedSize(horizontal: false, vertical: true)
|
|
@@ -295,7 +290,7 @@ struct UsageMenuCardView: View {
|
|
|
295
290
|
|
|
296
291
|
private var hasDetails: Bool {
|
|
297
292
|
let includeSummarySections = self.model.menuMode != .glance
|
|
298
|
-
let includeInsightSections = self.model.menuMode ==
|
|
293
|
+
let includeInsightSections = self.model.menuMode == .operator
|
|
299
294
|
return !self.model.metrics.isEmpty || self.model.placeholder != nil || (includeSummarySections && (
|
|
300
295
|
self.model.tokenUsage != nil || self.model.providerCost != nil)) || (includeInsightSections &&
|
|
301
296
|
self.model.insights != nil)
|
|
@@ -313,7 +308,7 @@ private struct UsageMenuCardHeaderView: View {
|
|
|
313
308
|
|
|
314
309
|
VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
|
|
315
310
|
Text(self.model.providerName)
|
|
316
|
-
.font(.headline)
|
|
311
|
+
.font(RunicFont.headline)
|
|
317
312
|
.fontWeight(.semibold)
|
|
318
313
|
if !self.model.email.isEmpty {
|
|
319
314
|
ProfilePill(
|
|
@@ -344,7 +339,7 @@ private struct UsageMenuCardHeaderView: View {
|
|
|
344
339
|
let subtitleAlignment: VerticalAlignment = self.model.subtitleStyle == .error ? .top : .firstTextBaseline
|
|
345
340
|
HStack(alignment: subtitleAlignment) {
|
|
346
341
|
Text(self.model.subtitleText)
|
|
347
|
-
.font(.footnote.weight(.medium))
|
|
342
|
+
.font(RunicFont.footnote.weight(.medium))
|
|
348
343
|
.foregroundStyle(self.subtitleColor)
|
|
349
344
|
.lineLimit(self.model.subtitleStyle == .error ? 4 : 1)
|
|
350
345
|
.multilineTextAlignment(.leading)
|
|
@@ -359,7 +354,7 @@ private struct UsageMenuCardHeaderView: View {
|
|
|
359
354
|
|
|
360
355
|
if let topModelLine = self.model.topModelLine {
|
|
361
356
|
Text(topModelLine)
|
|
362
|
-
.font(.footnote)
|
|
357
|
+
.font(RunicFont.footnote)
|
|
363
358
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
364
359
|
.lineLimit(1)
|
|
365
360
|
}
|
|
@@ -394,13 +389,17 @@ private struct UsageMenuCardHeaderView: View {
|
|
|
394
389
|
}
|
|
395
390
|
|
|
396
391
|
private var headerBorder: some View {
|
|
397
|
-
|
|
392
|
+
RoundedRectangle(cornerRadius: RunicCornerRadius.lg, style: .continuous)
|
|
398
393
|
.stroke(Color.white.opacity(RunicColors.Opacity.medium), lineWidth: 0.7)
|
|
399
394
|
}
|
|
400
395
|
|
|
401
396
|
private var brandNSColor: NSColor {
|
|
402
397
|
let color = ProviderDescriptorRegistry.descriptor(for: self.model.provider).branding.color
|
|
403
|
-
return NSColor(
|
|
398
|
+
return NSColor(
|
|
399
|
+
calibratedRed: CGFloat(color.red),
|
|
400
|
+
green: CGFloat(color.green),
|
|
401
|
+
blue: CGFloat(color.blue),
|
|
402
|
+
alpha: 1)
|
|
404
403
|
}
|
|
405
404
|
}
|
|
406
405
|
|
|
@@ -455,7 +454,7 @@ private struct ProviderAvatarView: View {
|
|
|
455
454
|
.shadow(color: Color.black.opacity(0.18), radius: 1, x: 0, y: 1)
|
|
456
455
|
} else {
|
|
457
456
|
Text(self.fallbackInitials)
|
|
458
|
-
.font(.caption.weight(.semibold))
|
|
457
|
+
.font(RunicFont.caption.weight(.semibold))
|
|
459
458
|
.foregroundStyle(Color.white.opacity(0.95))
|
|
460
459
|
}
|
|
461
460
|
}
|
|
@@ -467,7 +466,11 @@ private struct ProviderAvatarView: View {
|
|
|
467
466
|
|
|
468
467
|
private var brandNSColor: NSColor {
|
|
469
468
|
let color = ProviderDescriptorRegistry.descriptor(for: self.provider).branding.color
|
|
470
|
-
return NSColor(
|
|
469
|
+
return NSColor(
|
|
470
|
+
calibratedRed: CGFloat(color.red),
|
|
471
|
+
green: CGFloat(color.green),
|
|
472
|
+
blue: CGFloat(color.blue),
|
|
473
|
+
alpha: 1)
|
|
471
474
|
}
|
|
472
475
|
|
|
473
476
|
private var fallbackInitials: String {
|
|
@@ -491,9 +494,9 @@ private struct ProfilePill: View {
|
|
|
491
494
|
var body: some View {
|
|
492
495
|
HStack(spacing: RunicSpacing.xxxs) {
|
|
493
496
|
Image(systemName: self.systemImage)
|
|
494
|
-
.font(.caption2.weight(.semibold))
|
|
497
|
+
.font(RunicFont.caption2.weight(.semibold))
|
|
495
498
|
Text(self.text)
|
|
496
|
-
.font(.caption.weight(.medium))
|
|
499
|
+
.font(RunicFont.caption.weight(.medium))
|
|
497
500
|
}
|
|
498
501
|
.padding(.horizontal, RunicSpacing.compact)
|
|
499
502
|
.padding(.vertical, RunicSpacing.xxxs)
|
|
@@ -509,32 +512,31 @@ private struct ProfilePill: View {
|
|
|
509
512
|
private var foregroundColor: Color {
|
|
510
513
|
switch self.style {
|
|
511
514
|
case .email:
|
|
512
|
-
|
|
515
|
+
.secondary
|
|
513
516
|
case .plan:
|
|
514
|
-
|
|
517
|
+
self.tint.opacity(RunicColors.Opacity.vivid)
|
|
515
518
|
}
|
|
516
519
|
}
|
|
517
520
|
|
|
518
521
|
private var backgroundColor: Color {
|
|
519
522
|
switch self.style {
|
|
520
523
|
case .email:
|
|
521
|
-
|
|
524
|
+
Color(nsColor: .quaternaryLabelColor).opacity(RunicColors.Opacity.emphasis)
|
|
522
525
|
case .plan:
|
|
523
|
-
|
|
526
|
+
self.tint.opacity(RunicColors.Opacity.light)
|
|
524
527
|
}
|
|
525
528
|
}
|
|
526
529
|
|
|
527
530
|
private var borderColor: Color {
|
|
528
531
|
switch self.style {
|
|
529
532
|
case .email:
|
|
530
|
-
|
|
533
|
+
Color(nsColor: .separatorColor).opacity(RunicColors.Opacity.strong)
|
|
531
534
|
case .plan:
|
|
532
|
-
|
|
535
|
+
self.tint.opacity(RunicColors.Opacity.medium)
|
|
533
536
|
}
|
|
534
537
|
}
|
|
535
538
|
}
|
|
536
539
|
|
|
537
|
-
|
|
538
540
|
private struct MenuHeaderBadgeView: View {
|
|
539
541
|
let badge: UsageMenuCardView.Model.HeaderBadge
|
|
540
542
|
let isHighlighted: Bool
|
|
@@ -548,10 +550,10 @@ private struct MenuHeaderBadgeView: View {
|
|
|
548
550
|
.frame(width: 10, height: 10)
|
|
549
551
|
} else {
|
|
550
552
|
Image(systemName: self.badgeIcon)
|
|
551
|
-
.font(.caption2.weight(.semibold))
|
|
553
|
+
.font(RunicFont.caption2.weight(.semibold))
|
|
552
554
|
}
|
|
553
555
|
Text(self.badge.text)
|
|
554
|
-
.font(.caption2.weight(.semibold))
|
|
556
|
+
.font(RunicFont.caption2.weight(.semibold))
|
|
555
557
|
}
|
|
556
558
|
.padding(.horizontal, RunicSpacing.compact)
|
|
557
559
|
.padding(.vertical, RunicSpacing.xxxs)
|
|
@@ -566,42 +568,42 @@ private struct MenuHeaderBadgeView: View {
|
|
|
566
568
|
|
|
567
569
|
private var badgeIcon: String {
|
|
568
570
|
switch self.badge.style {
|
|
569
|
-
case .info:
|
|
570
|
-
case .warning:
|
|
571
|
-
case .error:
|
|
571
|
+
case .info: "arrow.triangle.2.circlepath"
|
|
572
|
+
case .warning: "exclamationmark.triangle.fill"
|
|
573
|
+
case .error: "xmark.circle.fill"
|
|
572
574
|
}
|
|
573
575
|
}
|
|
574
576
|
|
|
575
577
|
private var backgroundColor: Color {
|
|
576
578
|
switch self.badge.style {
|
|
577
579
|
case .info:
|
|
578
|
-
|
|
580
|
+
Color(nsColor: .systemBlue).opacity(self.isHighlighted ? 0.25 : 0.12)
|
|
579
581
|
case .warning:
|
|
580
|
-
|
|
582
|
+
Color(nsColor: .systemOrange).opacity(self.isHighlighted ? 0.35 : 0.15)
|
|
581
583
|
case .error:
|
|
582
|
-
|
|
584
|
+
Color(nsColor: .systemRed).opacity(self.isHighlighted ? 0.35 : 0.15)
|
|
583
585
|
}
|
|
584
586
|
}
|
|
585
587
|
|
|
586
588
|
private var foregroundColor: Color {
|
|
587
589
|
switch self.badge.style {
|
|
588
590
|
case .info:
|
|
589
|
-
|
|
591
|
+
Color(nsColor: .systemBlue).opacity(RunicColors.Opacity.vivid)
|
|
590
592
|
case .warning:
|
|
591
|
-
|
|
593
|
+
Color(nsColor: .systemOrange)
|
|
592
594
|
case .error:
|
|
593
|
-
|
|
595
|
+
Color(nsColor: .systemRed)
|
|
594
596
|
}
|
|
595
597
|
}
|
|
596
598
|
|
|
597
599
|
private var borderColor: Color {
|
|
598
600
|
switch self.badge.style {
|
|
599
601
|
case .info:
|
|
600
|
-
|
|
602
|
+
Color(nsColor: .systemBlue).opacity(RunicColors.Opacity.medium)
|
|
601
603
|
case .warning:
|
|
602
|
-
|
|
604
|
+
Color(nsColor: .systemOrange).opacity(RunicColors.Opacity.strong)
|
|
603
605
|
case .error:
|
|
604
|
-
|
|
606
|
+
Color(nsColor: .systemRed).opacity(RunicColors.Opacity.strong)
|
|
605
607
|
}
|
|
606
608
|
}
|
|
607
609
|
}
|
|
@@ -615,15 +617,15 @@ private struct MenuEmptyStateView: View {
|
|
|
615
617
|
VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
|
|
616
618
|
HStack(spacing: RunicSpacing.xxs) {
|
|
617
619
|
Image(systemName: "sparkles")
|
|
618
|
-
.font(.caption.weight(.semibold))
|
|
620
|
+
.font(RunicFont.caption.weight(.semibold))
|
|
619
621
|
Text("Connect \(self.providerName)")
|
|
620
|
-
.font(.subheadline.weight(.semibold))
|
|
622
|
+
.font(RunicFont.subheadline.weight(.semibold))
|
|
621
623
|
}
|
|
622
624
|
Text(self.placeholder)
|
|
623
|
-
.font(.footnote)
|
|
625
|
+
.font(RunicFont.footnote)
|
|
624
626
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
625
627
|
Text("Open Settings → Providers, add credentials, then refresh.")
|
|
626
|
-
.font(.footnote)
|
|
628
|
+
.font(RunicFont.footnote)
|
|
627
629
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
628
630
|
.fixedSize(horizontal: false, vertical: true)
|
|
629
631
|
}
|
|
@@ -667,7 +669,7 @@ private struct CopyIconButton: View {
|
|
|
667
669
|
}
|
|
668
670
|
} label: {
|
|
669
671
|
Image(systemName: self.didCopy ? "checkmark" : "doc.on.doc")
|
|
670
|
-
.font(.caption2.weight(.semibold))
|
|
672
|
+
.font(RunicFont.caption2.weight(.semibold))
|
|
671
673
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
672
674
|
.frame(width: 18, height: 18)
|
|
673
675
|
}
|
|
@@ -690,7 +692,7 @@ private struct ProviderCostContent: View {
|
|
|
690
692
|
var body: some View {
|
|
691
693
|
VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
|
|
692
694
|
Text(self.section.title)
|
|
693
|
-
.font(.body)
|
|
695
|
+
.font(RunicFont.body)
|
|
694
696
|
.fontWeight(.medium)
|
|
695
697
|
UsageProgressBar(
|
|
696
698
|
percent: self.section.percentUsed,
|
|
@@ -698,10 +700,10 @@ private struct ProviderCostContent: View {
|
|
|
698
700
|
accessibilityLabel: "Extra usage spent")
|
|
699
701
|
HStack(alignment: .firstTextBaseline) {
|
|
700
702
|
Text(self.section.spendLine)
|
|
701
|
-
.font(.footnote)
|
|
703
|
+
.font(RunicFont.footnote)
|
|
702
704
|
Spacer()
|
|
703
705
|
Text(String(format: "%.0f%% used", min(100, max(0, self.section.percentUsed))))
|
|
704
|
-
.font(.
|
|
706
|
+
.font(RunicFont.footnote)
|
|
705
707
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
706
708
|
}
|
|
707
709
|
}
|
|
@@ -715,88 +717,88 @@ private struct InsightsContent: View {
|
|
|
715
717
|
var body: some View {
|
|
716
718
|
VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
|
|
717
719
|
Text(self.section.title)
|
|
718
|
-
.font(.body)
|
|
720
|
+
.font(RunicFont.body)
|
|
719
721
|
.fontWeight(.medium)
|
|
720
722
|
if let today = self.section.todayLine {
|
|
721
723
|
Text(today)
|
|
722
|
-
.font(.footnote)
|
|
724
|
+
.font(RunicFont.footnote)
|
|
723
725
|
}
|
|
724
726
|
if let detail = self.section.todayDetail {
|
|
725
727
|
Text(detail)
|
|
726
|
-
.font(.footnote)
|
|
728
|
+
.font(RunicFont.footnote)
|
|
727
729
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
728
730
|
}
|
|
729
731
|
if let forecastLine = self.section.forecastLine {
|
|
730
732
|
Text(forecastLine)
|
|
731
|
-
.font(.footnote)
|
|
733
|
+
.font(RunicFont.footnote)
|
|
732
734
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
733
735
|
}
|
|
734
736
|
if let block = self.section.blockLine {
|
|
735
737
|
Text(block)
|
|
736
|
-
.font(.footnote)
|
|
738
|
+
.font(RunicFont.footnote)
|
|
737
739
|
}
|
|
738
740
|
if let blockDetail = self.section.blockDetail {
|
|
739
741
|
Text(blockDetail)
|
|
740
|
-
.font(.footnote)
|
|
742
|
+
.font(RunicFont.footnote)
|
|
741
743
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
742
744
|
}
|
|
743
745
|
if let modelLine = self.section.modelLine {
|
|
744
746
|
Text(modelLine)
|
|
745
|
-
.font(.footnote)
|
|
747
|
+
.font(RunicFont.footnote)
|
|
746
748
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
747
749
|
}
|
|
748
750
|
if let projectLine = self.section.projectLine {
|
|
749
751
|
Text(projectLine)
|
|
750
|
-
.font(.footnote)
|
|
752
|
+
.font(RunicFont.footnote)
|
|
751
753
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
752
754
|
}
|
|
753
755
|
if let projectDetail = self.section.projectDetail {
|
|
754
756
|
Text(projectDetail)
|
|
755
|
-
.font(.footnote)
|
|
757
|
+
.font(RunicFont.footnote)
|
|
756
758
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
757
759
|
}
|
|
758
760
|
if let anomalyLine = self.section.anomalyLine {
|
|
759
761
|
Text(anomalyLine)
|
|
760
|
-
.font(.footnote)
|
|
762
|
+
.font(RunicFont.footnote)
|
|
761
763
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
762
764
|
}
|
|
763
765
|
if let anomalyDetail = self.section.anomalyDetail {
|
|
764
766
|
Text(anomalyDetail)
|
|
765
|
-
.font(.footnote)
|
|
767
|
+
.font(RunicFont.footnote)
|
|
766
768
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
767
769
|
}
|
|
768
770
|
if let reliabilityLine = self.section.reliabilityLine {
|
|
769
771
|
Text(reliabilityLine)
|
|
770
|
-
.font(.footnote)
|
|
772
|
+
.font(RunicFont.footnote)
|
|
771
773
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
772
774
|
}
|
|
773
775
|
if let reliabilityDetail = self.section.reliabilityDetail {
|
|
774
776
|
Text(reliabilityDetail)
|
|
775
|
-
.font(.footnote)
|
|
777
|
+
.font(RunicFont.footnote)
|
|
776
778
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
777
779
|
}
|
|
778
780
|
if let routingLine = self.section.routingLine {
|
|
779
781
|
Text(routingLine)
|
|
780
|
-
.font(.footnote)
|
|
782
|
+
.font(RunicFont.footnote)
|
|
781
783
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
782
784
|
}
|
|
783
785
|
if let routingDetail = self.section.routingDetail {
|
|
784
786
|
Text(routingDetail)
|
|
785
|
-
.font(.footnote)
|
|
787
|
+
.font(RunicFont.footnote)
|
|
786
788
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
787
789
|
}
|
|
788
790
|
if let updated = self.section.updatedLine {
|
|
789
791
|
Text(updated)
|
|
790
|
-
.font(.caption)
|
|
792
|
+
.font(RunicFont.caption)
|
|
791
793
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
792
794
|
}
|
|
793
795
|
if let error = self.section.errorLine {
|
|
794
796
|
HStack(alignment: .top, spacing: RunicSpacing.xs) {
|
|
795
797
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
796
|
-
.font(.footnote)
|
|
798
|
+
.font(RunicFont.footnote)
|
|
797
799
|
.foregroundStyle(RunicColors.error)
|
|
798
800
|
Text(error)
|
|
799
|
-
.font(.footnote)
|
|
801
|
+
.font(RunicFont.footnote)
|
|
800
802
|
.foregroundStyle(MenuHighlightStyle.error(self.isHighlighted))
|
|
801
803
|
.lineLimit(4)
|
|
802
804
|
.fixedSize(horizontal: false, vertical: true)
|
|
@@ -841,12 +843,12 @@ private struct UsageMenuMetricCard: View {
|
|
|
841
843
|
VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
|
|
842
844
|
HStack(alignment: .firstTextBaseline, spacing: RunicSpacing.xs) {
|
|
843
845
|
Text(self.metric.title)
|
|
844
|
-
.font(.caption.weight(.semibold))
|
|
846
|
+
.font(RunicFont.caption.weight(.semibold))
|
|
845
847
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
846
848
|
Spacer(minLength: RunicSpacing.xs)
|
|
847
849
|
if let reset = self.metric.resetText {
|
|
848
850
|
Text(reset)
|
|
849
|
-
.font(.caption2)
|
|
851
|
+
.font(RunicFont.caption2)
|
|
850
852
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
851
853
|
}
|
|
852
854
|
}
|
|
@@ -861,7 +863,7 @@ private struct UsageMenuMetricCard: View {
|
|
|
861
863
|
if self.displayMode.showsPercent {
|
|
862
864
|
HStack(alignment: .firstTextBaseline, spacing: RunicSpacing.xs) {
|
|
863
865
|
Text(self.metric.percentLabel)
|
|
864
|
-
.font(.
|
|
866
|
+
.font(RunicFont.caption)
|
|
865
867
|
.fontWeight(.medium)
|
|
866
868
|
Spacer()
|
|
867
869
|
}
|
|
@@ -869,7 +871,7 @@ private struct UsageMenuMetricCard: View {
|
|
|
869
871
|
|
|
870
872
|
if let detail = self.metric.detailText {
|
|
871
873
|
Text(detail)
|
|
872
|
-
.font(.caption)
|
|
874
|
+
.font(RunicFont.caption)
|
|
873
875
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
874
876
|
.lineLimit(1)
|
|
875
877
|
.textCase(.none)
|
|
@@ -880,14 +882,12 @@ private struct UsageMenuMetricCard: View {
|
|
|
880
882
|
RoundedRectangle(
|
|
881
883
|
cornerRadius: MenuCardMetrics.metricCardCornerRadius,
|
|
882
884
|
style: .continuous)
|
|
883
|
-
.fill(Color(nsColor: .controlBackgroundColor).opacity(self.isHighlighted ? 0.2 : 0.14))
|
|
884
|
-
)
|
|
885
|
+
.fill(Color(nsColor: .controlBackgroundColor).opacity(self.isHighlighted ? 0.2 : 0.14)))
|
|
885
886
|
.overlay(
|
|
886
887
|
RoundedRectangle(
|
|
887
888
|
cornerRadius: MenuCardMetrics.metricCardCornerRadius,
|
|
888
889
|
style: .continuous)
|
|
889
|
-
.strokeBorder(Color(nsColor: .separatorColor).opacity(0.25), lineWidth: 1)
|
|
890
|
-
)
|
|
890
|
+
.strokeBorder(Color(nsColor: .separatorColor).opacity(0.25), lineWidth: 1))
|
|
891
891
|
}
|
|
892
892
|
}
|
|
893
893
|
|
|
@@ -936,20 +936,6 @@ struct UsageMenuCardCreditsSectionView: View {
|
|
|
936
936
|
let bottomPadding: CGFloat
|
|
937
937
|
let width: CGFloat
|
|
938
938
|
|
|
939
|
-
init(
|
|
940
|
-
model: UsageMenuCardView.Model,
|
|
941
|
-
showBottomDivider: Bool,
|
|
942
|
-
topPadding: CGFloat,
|
|
943
|
-
bottomPadding: CGFloat,
|
|
944
|
-
width: CGFloat)
|
|
945
|
-
{
|
|
946
|
-
self.model = model
|
|
947
|
-
self.showBottomDivider = showBottomDivider
|
|
948
|
-
self.topPadding = topPadding
|
|
949
|
-
self.bottomPadding = bottomPadding
|
|
950
|
-
self.width = width
|
|
951
|
-
}
|
|
952
|
-
|
|
953
939
|
var body: some View {
|
|
954
940
|
if let credits = self.model.creditsText {
|
|
955
941
|
VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
|
|
@@ -996,7 +982,7 @@ private struct CreditsBarContent: View {
|
|
|
996
982
|
var body: some View {
|
|
997
983
|
VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
|
|
998
984
|
Text("Credits")
|
|
999
|
-
.font(.body)
|
|
985
|
+
.font(RunicFont.body)
|
|
1000
986
|
.fontWeight(.medium)
|
|
1001
987
|
if let percentLeft {
|
|
1002
988
|
UsageProgressBar(
|
|
@@ -1005,19 +991,19 @@ private struct CreditsBarContent: View {
|
|
|
1005
991
|
accessibilityLabel: "Credits remaining")
|
|
1006
992
|
HStack(alignment: .firstTextBaseline) {
|
|
1007
993
|
Text(self.creditsText)
|
|
1008
|
-
.font(.
|
|
994
|
+
.font(RunicFont.caption)
|
|
1009
995
|
Spacer()
|
|
1010
996
|
Text(self.scaleText)
|
|
1011
|
-
.font(.
|
|
997
|
+
.font(RunicFont.caption)
|
|
1012
998
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
1013
999
|
}
|
|
1014
1000
|
} else {
|
|
1015
1001
|
Text(self.creditsText)
|
|
1016
|
-
.font(.
|
|
1002
|
+
.font(RunicFont.caption)
|
|
1017
1003
|
}
|
|
1018
1004
|
if let hintText, !hintText.isEmpty {
|
|
1019
1005
|
Text(hintText)
|
|
1020
|
-
.font(.footnote)
|
|
1006
|
+
.font(RunicFont.footnote)
|
|
1021
1007
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
1022
1008
|
.lineLimit(4)
|
|
1023
1009
|
.fixedSize(horizontal: false, vertical: true)
|
|
@@ -1061,28 +1047,28 @@ struct UsageMenuCardCostSectionView: View {
|
|
|
1061
1047
|
if let tokenUsage = self.model.tokenUsage {
|
|
1062
1048
|
VStack(alignment: .leading, spacing: MenuCardMetrics.lineSpacing) {
|
|
1063
1049
|
Text("Cost")
|
|
1064
|
-
.font(.body)
|
|
1050
|
+
.font(RunicFont.body)
|
|
1065
1051
|
.fontWeight(.medium)
|
|
1066
1052
|
Text(tokenUsage.sessionLine)
|
|
1067
|
-
.font(.
|
|
1053
|
+
.font(RunicFont.caption)
|
|
1068
1054
|
if let sessionDetail = tokenUsage.sessionDetailLine {
|
|
1069
1055
|
Text(sessionDetail)
|
|
1070
|
-
.font(.footnote)
|
|
1056
|
+
.font(RunicFont.footnote)
|
|
1071
1057
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
1072
1058
|
}
|
|
1073
1059
|
Text(tokenUsage.monthLine)
|
|
1074
|
-
.font(.
|
|
1060
|
+
.font(RunicFont.caption)
|
|
1075
1061
|
if let monthDetail = tokenUsage.monthDetailLine {
|
|
1076
1062
|
Text(monthDetail)
|
|
1077
|
-
.font(.footnote)
|
|
1063
|
+
.font(RunicFont.footnote)
|
|
1078
1064
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
1079
1065
|
}
|
|
1080
1066
|
Text(tokenUsage.updatedLine)
|
|
1081
|
-
.font(.caption)
|
|
1067
|
+
.font(RunicFont.caption)
|
|
1082
1068
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
1083
1069
|
if let hint = tokenUsage.hintLine, !hint.isEmpty {
|
|
1084
1070
|
Text(hint)
|
|
1085
|
-
.font(.footnote)
|
|
1071
|
+
.font(RunicFont.footnote)
|
|
1086
1072
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
1087
1073
|
.lineLimit(4)
|
|
1088
1074
|
.fixedSize(horizontal: false, vertical: true)
|
|
@@ -1090,10 +1076,10 @@ struct UsageMenuCardCostSectionView: View {
|
|
|
1090
1076
|
if let error = tokenUsage.errorLine, !error.isEmpty {
|
|
1091
1077
|
HStack(alignment: .top, spacing: RunicSpacing.xs) {
|
|
1092
1078
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
1093
|
-
.font(.footnote)
|
|
1079
|
+
.font(RunicFont.footnote)
|
|
1094
1080
|
.foregroundStyle(RunicColors.error)
|
|
1095
1081
|
Text(error)
|
|
1096
|
-
.font(.footnote)
|
|
1082
|
+
.font(RunicFont.footnote)
|
|
1097
1083
|
.foregroundStyle(MenuHighlightStyle.error(self.isHighlighted))
|
|
1098
1084
|
.lineLimit(4)
|
|
1099
1085
|
.fixedSize(horizontal: false, vertical: true)
|
|
@@ -1496,9 +1482,9 @@ extension UsageMenuCardView.Model {
|
|
|
1496
1482
|
}
|
|
1497
1483
|
|
|
1498
1484
|
private static func observedUsageDays(_ snapshot: CostUsageTokenSnapshot) -> Int {
|
|
1499
|
-
let nonEmptyDays = snapshot.daily.
|
|
1485
|
+
let nonEmptyDays = snapshot.daily.count(where: { entry in
|
|
1500
1486
|
(entry.totalTokens ?? 0) > 0 || (entry.costUSD ?? 0) > 0
|
|
1501
|
-
}
|
|
1487
|
+
})
|
|
1502
1488
|
if nonEmptyDays > 0 {
|
|
1503
1489
|
return min(30, nonEmptyDays)
|
|
1504
1490
|
}
|
|
@@ -1537,8 +1523,9 @@ extension UsageMenuCardView.Model {
|
|
|
1537
1523
|
? input.ledgerTopProjectSpendForecast
|
|
1538
1524
|
: nil
|
|
1539
1525
|
let anomaly = input.ledgerAnomaly?.provider == input.provider ? input.ledgerAnomaly : nil
|
|
1540
|
-
let hasData = daily != nil || block != nil || topModel != nil || topProject != nil || spendForecast != nil ||
|
|
1541
|
-
|
|
1526
|
+
let hasData = daily != nil || block != nil || topModel != nil || topProject != nil || spendForecast != nil ||
|
|
1527
|
+
anomaly != nil
|
|
1528
|
+
if !hasData, error?.isEmpty ?? true {
|
|
1542
1529
|
return nil
|
|
1543
1530
|
}
|
|
1544
1531
|
|
|
@@ -1820,10 +1807,10 @@ extension UsageMenuCardView.Model {
|
|
|
1820
1807
|
return nil
|
|
1821
1808
|
}
|
|
1822
1809
|
|
|
1823
|
-
var hash: UInt64 =
|
|
1810
|
+
var hash: UInt64 = 0xCBF2_9CE4_8422_2325
|
|
1824
1811
|
for byte in projectID.lowercased().utf8 {
|
|
1825
1812
|
hash ^= UInt64(byte)
|
|
1826
|
-
hash = hash &*
|
|
1813
|
+
hash = hash &* 0x100_0000_01B3
|
|
1827
1814
|
}
|
|
1828
1815
|
return String(format: "%08llx", hash)
|
|
1829
1816
|
}
|
|
@@ -1889,7 +1876,9 @@ private final class ClickToCopyView: NSView {
|
|
|
1889
1876
|
fatalError("init(coder:) has not been implemented")
|
|
1890
1877
|
}
|
|
1891
1878
|
|
|
1892
|
-
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
|
|
1879
|
+
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
|
|
1880
|
+
true
|
|
1881
|
+
}
|
|
1893
1882
|
|
|
1894
1883
|
override func mouseDown(with event: NSEvent) {
|
|
1895
1884
|
_ = event
|