@sriinnu/runic 1.2.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/README.md +8 -1
- 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/Sources/Runic/About.swift +3 -3
- package/Sources/Runic/AppNotifications.swift +5 -3
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +7 -3
- 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 +18 -13
- 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 +1 -1
- 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 +1 -3
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/SettingsStore.swift +89 -51
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +1 -3
- 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 +187 -161
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +1 -2
- 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/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 +12 -10
- 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 +18 -15
- 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 +22 -17
- 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 +44 -39
- 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 +73 -75
- 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/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 +3 -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/CodexOAuthUsageFetcher.swift +1 -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/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 +2 -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 +5 -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 +7 -7
- 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 +23 -10
- 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 +26 -13
- 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 +48 -21
- 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 +27 -13
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +11 -3
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +5 -2
- 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 -76
- package/package.json +1 -1
- package/runic.png +0 -0
- package/runic.svg +15 -0
- package/version.env +2 -2
|
@@ -4,19 +4,19 @@ import SwiftUI
|
|
|
4
4
|
|
|
5
5
|
enum MenuContentMetrics {
|
|
6
6
|
/// Horizontal padding for menu content
|
|
7
|
-
static let horizontalPadding: CGFloat = RunicSpacing.sm
|
|
7
|
+
static let horizontalPadding: CGFloat = RunicSpacing.sm // 12
|
|
8
8
|
|
|
9
9
|
/// Vertical padding for menu content
|
|
10
|
-
static let verticalPadding: CGFloat = RunicSpacing.xs
|
|
10
|
+
static let verticalPadding: CGFloat = RunicSpacing.xs // 8
|
|
11
11
|
|
|
12
12
|
/// Spacing between menu sections
|
|
13
|
-
static let sectionSpacing: CGFloat = RunicSpacing.xs
|
|
13
|
+
static let sectionSpacing: CGFloat = RunicSpacing.xs // 8
|
|
14
14
|
|
|
15
15
|
/// Spacing between menu entries
|
|
16
|
-
static let entrySpacing: CGFloat = RunicSpacing.xxs
|
|
16
|
+
static let entrySpacing: CGFloat = RunicSpacing.xxs // 4
|
|
17
17
|
|
|
18
18
|
/// Minimum menu width
|
|
19
|
-
static let minWidth: CGFloat =
|
|
19
|
+
static let minWidth: CGFloat = 290
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
@MainActor
|
|
@@ -52,6 +52,7 @@ struct MenuContent: View {
|
|
|
52
52
|
.padding(.horizontal, MenuContentMetrics.horizontalPadding)
|
|
53
53
|
.padding(.vertical, MenuContentMetrics.verticalPadding)
|
|
54
54
|
.frame(minWidth: MenuContentMetrics.minWidth, alignment: .leading)
|
|
55
|
+
.runicTypography()
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
@ViewBuilder
|
|
@@ -60,11 +61,11 @@ struct MenuContent: View {
|
|
|
60
61
|
case let .text(text, style):
|
|
61
62
|
switch style {
|
|
62
63
|
case .headline:
|
|
63
|
-
Text(text).font(.headline)
|
|
64
|
+
Text(text).font(RunicFont.headline)
|
|
64
65
|
case .primary:
|
|
65
66
|
Text(text)
|
|
66
67
|
case .secondary:
|
|
67
|
-
Text(text).foregroundStyle(.secondary).font(.footnote)
|
|
68
|
+
Text(text).foregroundStyle(.secondary).font(RunicFont.footnote)
|
|
68
69
|
}
|
|
69
70
|
case let .action(title, action):
|
|
70
71
|
Button {
|
|
@@ -89,7 +90,9 @@ struct MenuContent: View {
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
private func iconName(for action: MenuDescriptor.MenuAction) -> String? {
|
|
93
|
+
private func iconName(for action: MenuDescriptor.MenuAction) -> String? {
|
|
94
|
+
action.systemImageName
|
|
95
|
+
}
|
|
93
96
|
|
|
94
97
|
private func perform(_ action: MenuDescriptor.MenuAction) {
|
|
95
98
|
switch action {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicCore
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicCore
|
|
3
3
|
|
|
4
4
|
@MainActor
|
|
5
5
|
struct MenuDescriptor {
|
|
@@ -351,7 +351,9 @@ private enum AccountFormatter {
|
|
|
351
351
|
return cleaned.isEmpty ? text : cleaned
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
static func email(_ text: String) -> String {
|
|
354
|
+
static func email(_ text: String) -> String {
|
|
355
|
+
text
|
|
356
|
+
}
|
|
355
357
|
}
|
|
356
358
|
|
|
357
359
|
extension MenuDescriptor.MenuAction {
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import SwiftUI
|
|
2
2
|
|
|
3
|
-
private struct MenuItemHighlightedKey: EnvironmentKey {
|
|
4
|
-
static let defaultValue = false
|
|
5
|
-
}
|
|
6
|
-
|
|
7
3
|
extension EnvironmentValues {
|
|
8
|
-
var menuItemHighlighted: Bool
|
|
9
|
-
get { self[MenuItemHighlightedKey.self] }
|
|
10
|
-
set { self[MenuItemHighlightedKey.self] = newValue }
|
|
11
|
-
}
|
|
4
|
+
@Entry var menuItemHighlighted: Bool = false
|
|
12
5
|
}
|
|
13
6
|
|
|
14
7
|
enum MenuHighlightStyle {
|
|
@@ -20,16 +20,18 @@ struct ModelBreakdownMenuView: View {
|
|
|
20
20
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
21
21
|
if model.items.isEmpty {
|
|
22
22
|
Text("No model data.")
|
|
23
|
-
.font(.footnote)
|
|
23
|
+
.font(RunicFont.footnote)
|
|
24
24
|
.foregroundStyle(.secondary)
|
|
25
25
|
} else {
|
|
26
26
|
// MARK: - Title
|
|
27
|
+
|
|
27
28
|
Text("Models")
|
|
28
|
-
.font(.caption)
|
|
29
|
+
.font(RunicFont.caption)
|
|
29
30
|
.fontWeight(.semibold)
|
|
30
31
|
.foregroundStyle(.primary)
|
|
31
32
|
|
|
32
33
|
// MARK: - Donut chart
|
|
34
|
+
|
|
33
35
|
Chart {
|
|
34
36
|
ForEach(model.chartItems) { item in
|
|
35
37
|
SectorMark(
|
|
@@ -45,6 +47,7 @@ struct ModelBreakdownMenuView: View {
|
|
|
45
47
|
.accessibilityLabel(Self.chartAccessibilityLabel(model: model))
|
|
46
48
|
|
|
47
49
|
// MARK: - Model list
|
|
50
|
+
|
|
48
51
|
VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
|
|
49
52
|
ForEach(model.chartItems) { item in
|
|
50
53
|
Self.modelRow(item: item)
|
|
@@ -52,28 +55,31 @@ struct ModelBreakdownMenuView: View {
|
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
// MARK: - Overflow note
|
|
58
|
+
|
|
55
59
|
if model.overflowCount > 0 {
|
|
56
60
|
Text("and \(model.overflowCount) more")
|
|
57
|
-
.font(.caption2)
|
|
61
|
+
.font(RunicFont.caption2)
|
|
58
62
|
.foregroundStyle(.tertiary)
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
// MARK: - Cache hit rate
|
|
66
|
+
|
|
62
67
|
if let cacheRate = model.cacheHitRateText {
|
|
63
68
|
Divider()
|
|
64
69
|
HStack(spacing: RunicSpacing.xxs) {
|
|
65
70
|
Text("Cache hit rate")
|
|
66
|
-
.font(.caption)
|
|
71
|
+
.font(RunicFont.caption)
|
|
67
72
|
.foregroundStyle(.secondary)
|
|
68
73
|
Spacer(minLength: RunicSpacing.xxs)
|
|
69
74
|
Text(cacheRate)
|
|
70
|
-
.font(.caption)
|
|
75
|
+
.font(RunicFont.caption)
|
|
71
76
|
.fontWeight(.medium)
|
|
72
77
|
.foregroundStyle(.primary)
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
// MARK: - Total summary
|
|
82
|
+
|
|
77
83
|
Divider()
|
|
78
84
|
Self.totalRow(model: model)
|
|
79
85
|
}
|
|
@@ -91,27 +97,27 @@ struct ModelBreakdownMenuView: View {
|
|
|
91
97
|
.fill(item.color)
|
|
92
98
|
.frame(width: RunicSpacing.chartLegendDot, height: RunicSpacing.chartLegendDot)
|
|
93
99
|
Text(item.displayName)
|
|
94
|
-
.font(.caption)
|
|
100
|
+
.font(RunicFont.caption)
|
|
95
101
|
.foregroundStyle(.primary)
|
|
96
102
|
.lineLimit(1)
|
|
97
103
|
.truncationMode(.tail)
|
|
98
104
|
Spacer(minLength: RunicSpacing.xxs)
|
|
99
105
|
VStack(alignment: .trailing, spacing: 0) {
|
|
100
106
|
Text("\(UsageFormatter.tokenCountString(item.totalTokens)) tokens")
|
|
101
|
-
.font(.
|
|
107
|
+
.font(RunicFont.caption2)
|
|
102
108
|
.foregroundStyle(.secondary)
|
|
103
109
|
if let context = UsageFormatter.modelContextLabel(for: item.model) {
|
|
104
110
|
Text(context)
|
|
105
|
-
.font(.
|
|
111
|
+
.font(RunicFont.caption2)
|
|
106
112
|
.foregroundStyle(.tertiary)
|
|
107
113
|
}
|
|
108
114
|
HStack(spacing: RunicSpacing.xxxs) {
|
|
109
115
|
Text("\(item.requestCount) req\(item.requestCount == 1 ? "" : "s")")
|
|
110
|
-
.font(.
|
|
116
|
+
.font(RunicFont.caption2)
|
|
111
117
|
.foregroundStyle(.tertiary)
|
|
112
118
|
if let cost = item.costText {
|
|
113
119
|
Text(cost)
|
|
114
|
-
.font(.
|
|
120
|
+
.font(RunicFont.caption2)
|
|
115
121
|
.foregroundStyle(.tertiary)
|
|
116
122
|
}
|
|
117
123
|
}
|
|
@@ -122,21 +128,21 @@ struct ModelBreakdownMenuView: View {
|
|
|
122
128
|
private static func totalRow(model: ModelModel) -> some View {
|
|
123
129
|
HStack {
|
|
124
130
|
Text("Total")
|
|
125
|
-
.font(.caption)
|
|
131
|
+
.font(RunicFont.caption)
|
|
126
132
|
.fontWeight(.medium)
|
|
127
133
|
.foregroundStyle(.primary)
|
|
128
134
|
Spacer(minLength: RunicSpacing.xxs)
|
|
129
135
|
VStack(alignment: .trailing, spacing: 0) {
|
|
130
136
|
Text("\(UsageFormatter.tokenCountString(model.grandTotalTokens)) tokens")
|
|
131
|
-
.font(.
|
|
137
|
+
.font(RunicFont.caption2)
|
|
132
138
|
.foregroundStyle(.secondary)
|
|
133
139
|
HStack(spacing: RunicSpacing.xxxs) {
|
|
134
140
|
Text("\(model.grandTotalRequests) requests")
|
|
135
|
-
.font(.
|
|
141
|
+
.font(RunicFont.caption2)
|
|
136
142
|
.foregroundStyle(.tertiary)
|
|
137
143
|
if let cost = model.grandTotalCostText {
|
|
138
144
|
Text(cost)
|
|
139
|
-
.font(.
|
|
145
|
+
.font(RunicFont.caption2)
|
|
140
146
|
.foregroundStyle(.tertiary)
|
|
141
147
|
}
|
|
142
148
|
}
|
|
@@ -16,8 +16,8 @@ struct OverviewMenuView: View {
|
|
|
16
16
|
let todayTokens: Int
|
|
17
17
|
let brandColor: Color
|
|
18
18
|
let resetDescription: String?
|
|
19
|
-
let windowLabel: String?
|
|
20
|
-
let topModelContext: String?
|
|
19
|
+
let windowLabel: String? // e.g. "5h" or "Weekly"
|
|
20
|
+
let topModelContext: String? // e.g. "200K ctx"
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
struct DailyPoint: Identifiable {
|
|
@@ -37,6 +37,7 @@ struct OverviewMenuView: View {
|
|
|
37
37
|
var body: some View {
|
|
38
38
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
39
39
|
// MARK: - Hero header
|
|
40
|
+
|
|
40
41
|
HStack(alignment: .center, spacing: RunicSpacing.xs) {
|
|
41
42
|
// Ring indicator showing overall usage
|
|
42
43
|
ZStack {
|
|
@@ -55,9 +56,9 @@ struct OverviewMenuView: View {
|
|
|
55
56
|
|
|
56
57
|
VStack(alignment: .leading, spacing: 0) {
|
|
57
58
|
Text(UsageFormatter.tokenCountString(self.totalTodayTokens))
|
|
58
|
-
.font(.system(size: 22, weight: .bold
|
|
59
|
+
.font(RunicFont.system(size: 22, weight: .bold))
|
|
59
60
|
Text("\(self.summaries.count) of \(self.totalProviders) active")
|
|
60
|
-
.font(.
|
|
61
|
+
.font(RunicFont.caption2)
|
|
61
62
|
.foregroundStyle(.tertiary)
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -65,10 +66,10 @@ struct OverviewMenuView: View {
|
|
|
65
66
|
|
|
66
67
|
VStack(alignment: .trailing, spacing: 2) {
|
|
67
68
|
Text("today")
|
|
68
|
-
.font(.
|
|
69
|
+
.font(RunicFont.caption2)
|
|
69
70
|
.foregroundStyle(.tertiary)
|
|
70
71
|
Text("\(Int(self.averageUsedPercent))% avg")
|
|
71
|
-
.font(.
|
|
72
|
+
.font(RunicFont.caption)
|
|
72
73
|
.fontWeight(.medium)
|
|
73
74
|
.foregroundStyle(.secondary)
|
|
74
75
|
}
|
|
@@ -77,9 +78,10 @@ struct OverviewMenuView: View {
|
|
|
77
78
|
Divider().opacity(0.5)
|
|
78
79
|
|
|
79
80
|
// MARK: - Provider cards
|
|
81
|
+
|
|
80
82
|
if self.summaries.isEmpty {
|
|
81
83
|
Text("No active providers.")
|
|
82
|
-
.font(.
|
|
84
|
+
.font(RunicFont.footnote)
|
|
83
85
|
.foregroundStyle(.secondary)
|
|
84
86
|
.frame(maxWidth: .infinity, alignment: .center)
|
|
85
87
|
.padding(.vertical, RunicSpacing.md)
|
|
@@ -92,12 +94,13 @@ struct OverviewMenuView: View {
|
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
// MARK: - Combined 7-day chart
|
|
97
|
+
|
|
95
98
|
if !self.chartPoints.isEmpty {
|
|
96
99
|
Divider().opacity(0.5)
|
|
97
100
|
|
|
98
101
|
HStack {
|
|
99
102
|
Text("7-day activity")
|
|
100
|
-
.font(.
|
|
103
|
+
.font(RunicFont.caption2)
|
|
101
104
|
.foregroundStyle(.tertiary)
|
|
102
105
|
Spacer()
|
|
103
106
|
// Mini legend dots
|
|
@@ -109,7 +112,7 @@ struct OverviewMenuView: View {
|
|
|
109
112
|
}
|
|
110
113
|
if self.summaries.count > 4 {
|
|
111
114
|
Text("+\(self.summaries.count - 4)")
|
|
112
|
-
.font(.system(size: 8
|
|
115
|
+
.font(RunicFont.system(size: 8))
|
|
113
116
|
.foregroundStyle(.quaternary)
|
|
114
117
|
}
|
|
115
118
|
}
|
|
@@ -133,7 +136,7 @@ struct OverviewMenuView: View {
|
|
|
133
136
|
AxisValueLabel {
|
|
134
137
|
if let tokens = value.as(Int.self) {
|
|
135
138
|
Text(UsageFormatter.tokenCountString(tokens))
|
|
136
|
-
.font(.system(size: 8
|
|
139
|
+
.font(RunicFont.system(size: 8))
|
|
137
140
|
.foregroundStyle(Color(nsColor: .tertiaryLabelColor))
|
|
138
141
|
}
|
|
139
142
|
}
|
|
@@ -142,7 +145,7 @@ struct OverviewMenuView: View {
|
|
|
142
145
|
.chartXAxis {
|
|
143
146
|
AxisMarks(values: .automatic(desiredCount: 7)) { _ in
|
|
144
147
|
AxisValueLabel(format: .dateTime.weekday(.narrow))
|
|
145
|
-
.font(.system(size: 8, weight: .medium
|
|
148
|
+
.font(RunicFont.system(size: 8, weight: .medium))
|
|
146
149
|
.foregroundStyle(Color(nsColor: .tertiaryLabelColor))
|
|
147
150
|
}
|
|
148
151
|
}
|
|
@@ -182,7 +185,7 @@ private struct ProviderRow: View {
|
|
|
182
185
|
|
|
183
186
|
// Name
|
|
184
187
|
Text(self.summary.name)
|
|
185
|
-
.font(.
|
|
188
|
+
.font(RunicFont.caption)
|
|
186
189
|
.fontWeight(.medium)
|
|
187
190
|
.frame(width: 58, alignment: .leading)
|
|
188
191
|
.lineLimit(1)
|
|
@@ -224,14 +227,14 @@ private struct ProviderRow: View {
|
|
|
224
227
|
|
|
225
228
|
// Percentage
|
|
226
229
|
Text("\(Int(self.summary.usedPercent))%")
|
|
227
|
-
.font(.system(size: 9, weight: .semibold
|
|
230
|
+
.font(RunicFont.system(size: 9, weight: .semibold))
|
|
228
231
|
.foregroundStyle(self.summary.usedPercent > 80 ? .primary : .secondary)
|
|
229
232
|
.frame(width: 28, alignment: .trailing)
|
|
230
233
|
|
|
231
234
|
// Today's tokens (if any)
|
|
232
235
|
if self.summary.todayTokens > 0 {
|
|
233
236
|
Text(UsageFormatter.tokenCountString(self.summary.todayTokens))
|
|
234
|
-
.font(.system(size: 8
|
|
237
|
+
.font(RunicFont.system(size: 8))
|
|
235
238
|
.foregroundStyle(.tertiary)
|
|
236
239
|
.frame(width: 32, alignment: .trailing)
|
|
237
240
|
}
|
|
@@ -267,7 +270,7 @@ private struct InfoPill: View {
|
|
|
267
270
|
|
|
268
271
|
var body: some View {
|
|
269
272
|
Text(self.text)
|
|
270
|
-
.font(.system(size: 8, weight: .medium
|
|
273
|
+
.font(RunicFont.system(size: 8, weight: .medium))
|
|
271
274
|
.foregroundStyle(.tertiary)
|
|
272
275
|
.padding(.horizontal, 4)
|
|
273
276
|
.padding(.vertical, 1)
|
|
@@ -20,16 +20,18 @@ struct ProjectBreakdownMenuView: View {
|
|
|
20
20
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
21
21
|
if model.items.isEmpty {
|
|
22
22
|
Text("No project data.")
|
|
23
|
-
.font(.footnote)
|
|
23
|
+
.font(RunicFont.footnote)
|
|
24
24
|
.foregroundStyle(.secondary)
|
|
25
25
|
} else {
|
|
26
26
|
// MARK: - Title
|
|
27
|
+
|
|
27
28
|
Text("Projects")
|
|
28
|
-
.font(.caption)
|
|
29
|
+
.font(RunicFont.caption)
|
|
29
30
|
.fontWeight(.semibold)
|
|
30
31
|
.foregroundStyle(.primary)
|
|
31
32
|
|
|
32
33
|
// MARK: - Horizontal bar chart
|
|
34
|
+
|
|
33
35
|
Chart {
|
|
34
36
|
ForEach(model.chartItems) { item in
|
|
35
37
|
BarMark(
|
|
@@ -43,7 +45,7 @@ struct ProjectBreakdownMenuView: View {
|
|
|
43
45
|
.chartYAxis {
|
|
44
46
|
AxisMarks { _ in
|
|
45
47
|
AxisValueLabel()
|
|
46
|
-
.font(.caption2)
|
|
48
|
+
.font(RunicFont.caption2)
|
|
47
49
|
.foregroundStyle(Color(nsColor: .secondaryLabelColor))
|
|
48
50
|
}
|
|
49
51
|
}
|
|
@@ -53,6 +55,7 @@ struct ProjectBreakdownMenuView: View {
|
|
|
53
55
|
.accessibilityLabel(Self.chartAccessibilityLabel(model: model))
|
|
54
56
|
|
|
55
57
|
// MARK: - Project list
|
|
58
|
+
|
|
56
59
|
VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
|
|
57
60
|
ForEach(model.chartItems) { item in
|
|
58
61
|
Self.projectRow(item: item)
|
|
@@ -60,13 +63,15 @@ struct ProjectBreakdownMenuView: View {
|
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
// MARK: - Overflow note
|
|
66
|
+
|
|
63
67
|
if model.overflowCount > 0 {
|
|
64
68
|
Text("and \(model.overflowCount) more")
|
|
65
|
-
.font(.caption2)
|
|
69
|
+
.font(RunicFont.caption2)
|
|
66
70
|
.foregroundStyle(.tertiary)
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
// MARK: - Total summary
|
|
74
|
+
|
|
70
75
|
Divider()
|
|
71
76
|
Self.totalRow(model: model)
|
|
72
77
|
}
|
|
@@ -84,24 +89,24 @@ struct ProjectBreakdownMenuView: View {
|
|
|
84
89
|
.fill(item.color)
|
|
85
90
|
.frame(width: RunicSpacing.chartLegendDot, height: RunicSpacing.chartLegendDot)
|
|
86
91
|
Text(item.name)
|
|
87
|
-
.font(.caption)
|
|
92
|
+
.font(RunicFont.caption)
|
|
88
93
|
.foregroundStyle(.primary)
|
|
89
94
|
.lineLimit(1)
|
|
90
95
|
.truncationMode(.middle)
|
|
91
96
|
Spacer(minLength: RunicSpacing.xxs)
|
|
92
97
|
VStack(alignment: .trailing, spacing: 0) {
|
|
93
98
|
Text("\(UsageFormatter.tokenCountString(item.totalTokens)) tokens")
|
|
94
|
-
.font(.
|
|
99
|
+
.font(RunicFont.caption2)
|
|
95
100
|
.foregroundStyle(.secondary)
|
|
96
101
|
HStack(spacing: RunicSpacing.xxxs) {
|
|
97
102
|
if let cost = item.costText {
|
|
98
103
|
Text(cost)
|
|
99
|
-
.font(.
|
|
104
|
+
.font(RunicFont.caption2)
|
|
100
105
|
.foregroundStyle(.tertiary)
|
|
101
106
|
}
|
|
102
107
|
if item.modelCount > 0 {
|
|
103
108
|
Text("\(item.modelCount) model\(item.modelCount == 1 ? "" : "s")")
|
|
104
|
-
.font(.
|
|
109
|
+
.font(RunicFont.caption2)
|
|
105
110
|
.foregroundStyle(.tertiary)
|
|
106
111
|
}
|
|
107
112
|
}
|
|
@@ -112,17 +117,17 @@ struct ProjectBreakdownMenuView: View {
|
|
|
112
117
|
private static func totalRow(model: ProjectModel) -> some View {
|
|
113
118
|
HStack {
|
|
114
119
|
Text("Total")
|
|
115
|
-
.font(.caption)
|
|
120
|
+
.font(RunicFont.caption)
|
|
116
121
|
.fontWeight(.medium)
|
|
117
122
|
.foregroundStyle(.primary)
|
|
118
123
|
Spacer(minLength: RunicSpacing.xxs)
|
|
119
124
|
VStack(alignment: .trailing, spacing: 0) {
|
|
120
125
|
Text("\(UsageFormatter.tokenCountString(model.grandTotalTokens)) tokens")
|
|
121
|
-
.font(.
|
|
126
|
+
.font(RunicFont.caption2)
|
|
122
127
|
.foregroundStyle(.secondary)
|
|
123
128
|
if let cost = model.grandTotalCostText {
|
|
124
129
|
Text(cost)
|
|
125
|
-
.font(.
|
|
130
|
+
.font(RunicFont.caption2)
|
|
126
131
|
.foregroundStyle(.tertiary)
|
|
127
132
|
}
|
|
128
133
|
}
|
|
@@ -14,11 +14,11 @@ struct ProjectBudgetMenuView: View {
|
|
|
14
14
|
|
|
15
15
|
var statusColor: Color {
|
|
16
16
|
if self.isOverBudget {
|
|
17
|
-
|
|
17
|
+
Color(red: 0.94, green: 0.36, blue: 0.36) // Red
|
|
18
18
|
} else if self.isNearLimit {
|
|
19
|
-
|
|
19
|
+
Color(red: 0.94, green: 0.74, blue: 0.26) // Yellow
|
|
20
20
|
} else {
|
|
21
|
-
|
|
21
|
+
Color(red: 0.46, green: 0.75, blue: 0.36) // Green
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -44,7 +44,7 @@ struct ProjectBudgetMenuView: View {
|
|
|
44
44
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
45
45
|
HStack {
|
|
46
46
|
Text("Project Budgets")
|
|
47
|
-
.font(.headline)
|
|
47
|
+
.font(RunicFont.headline)
|
|
48
48
|
.fontWeight(.semibold)
|
|
49
49
|
Spacer()
|
|
50
50
|
Button {
|
|
@@ -52,9 +52,9 @@ struct ProjectBudgetMenuView: View {
|
|
|
52
52
|
} label: {
|
|
53
53
|
HStack(spacing: RunicSpacing.xxs) {
|
|
54
54
|
Image(systemName: "gearshape")
|
|
55
|
-
.font(.caption)
|
|
55
|
+
.font(RunicFont.caption)
|
|
56
56
|
Text("Configure")
|
|
57
|
-
.font(.caption)
|
|
57
|
+
.font(RunicFont.caption)
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
.buttonStyle(.plain)
|
|
@@ -64,13 +64,13 @@ struct ProjectBudgetMenuView: View {
|
|
|
64
64
|
if model.isEmpty {
|
|
65
65
|
VStack(alignment: .leading, spacing: RunicSpacing.xs) {
|
|
66
66
|
Text("No budgets configured.")
|
|
67
|
-
.font(.footnote)
|
|
67
|
+
.font(RunicFont.footnote)
|
|
68
68
|
.foregroundStyle(.secondary)
|
|
69
69
|
Button {
|
|
70
70
|
self.onOpenPreferences()
|
|
71
71
|
} label: {
|
|
72
72
|
Text("Set up budgets in Preferences")
|
|
73
|
-
.font(.caption)
|
|
73
|
+
.font(RunicFont.caption)
|
|
74
74
|
}
|
|
75
75
|
.buttonStyle(.plain)
|
|
76
76
|
.foregroundStyle(.blue)
|
|
@@ -88,8 +88,8 @@ struct ProjectBudgetMenuView: View {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// Summary
|
|
91
|
-
let overBudget = model.
|
|
92
|
-
let nearLimit = model.
|
|
91
|
+
let overBudget = model.count(where: { $0.isOverBudget })
|
|
92
|
+
let nearLimit = model.count(where: { $0.isNearLimit && !$0.isOverBudget })
|
|
93
93
|
if overBudget > 0 || nearLimit > 0 {
|
|
94
94
|
Divider()
|
|
95
95
|
.padding(.vertical, RunicSpacing.xxs)
|
|
@@ -98,20 +98,20 @@ struct ProjectBudgetMenuView: View {
|
|
|
98
98
|
if overBudget > 0 {
|
|
99
99
|
HStack(spacing: RunicSpacing.xxs) {
|
|
100
100
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
101
|
-
.font(.caption2)
|
|
101
|
+
.font(RunicFont.caption2)
|
|
102
102
|
.foregroundStyle(Color(red: 0.94, green: 0.36, blue: 0.36))
|
|
103
103
|
Text("\(overBudget) project\(overBudget == 1 ? "" : "s") over budget")
|
|
104
|
-
.font(.caption)
|
|
104
|
+
.font(RunicFont.caption)
|
|
105
105
|
.foregroundStyle(.secondary)
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
if nearLimit > 0 {
|
|
109
109
|
HStack(spacing: RunicSpacing.xxs) {
|
|
110
110
|
Image(systemName: "exclamationmark.circle.fill")
|
|
111
|
-
.font(.caption2)
|
|
111
|
+
.font(RunicFont.caption2)
|
|
112
112
|
.foregroundStyle(Color(red: 0.94, green: 0.74, blue: 0.26))
|
|
113
113
|
Text("\(nearLimit) project\(nearLimit == 1 ? "" : "s") near limit")
|
|
114
|
-
.font(.caption)
|
|
114
|
+
.font(RunicFont.caption)
|
|
115
115
|
.foregroundStyle(.secondary)
|
|
116
116
|
}
|
|
117
117
|
}
|
|
@@ -167,34 +167,34 @@ private struct ProjectBudgetRow: View {
|
|
|
167
167
|
var body: some View {
|
|
168
168
|
VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
|
|
169
169
|
HStack(alignment: .firstTextBaseline) {
|
|
170
|
-
Text(project.projectName)
|
|
171
|
-
.font(.body)
|
|
170
|
+
Text(self.project.projectName)
|
|
171
|
+
.font(RunicFont.body)
|
|
172
172
|
.fontWeight(.medium)
|
|
173
173
|
.lineLimit(1)
|
|
174
174
|
Spacer()
|
|
175
175
|
Text(self.budgetText)
|
|
176
|
-
.font(.caption)
|
|
176
|
+
.font(RunicFont.caption)
|
|
177
177
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
UsageProgressBar(
|
|
181
|
-
percent: project.percentUsed,
|
|
182
|
-
tint: project.statusColor,
|
|
181
|
+
percent: self.project.percentUsed,
|
|
182
|
+
tint: self.project.statusColor,
|
|
183
183
|
accessibilityLabel: "Budget usage")
|
|
184
184
|
|
|
185
185
|
HStack(alignment: .firstTextBaseline) {
|
|
186
|
-
Text(String(format: "%.0f%%", project.percentUsed))
|
|
187
|
-
.font(.caption)
|
|
186
|
+
Text(String(format: "%.0f%%", self.project.percentUsed))
|
|
187
|
+
.font(RunicFont.caption)
|
|
188
188
|
.foregroundStyle(self.statusTextColor)
|
|
189
189
|
Spacer()
|
|
190
|
-
if project.isOverBudget {
|
|
190
|
+
if self.project.isOverBudget {
|
|
191
191
|
Text("Over budget")
|
|
192
|
-
.font(.caption)
|
|
192
|
+
.font(RunicFont.caption)
|
|
193
193
|
.foregroundStyle(Color(red: 0.94, green: 0.36, blue: 0.36))
|
|
194
194
|
.fontWeight(.medium)
|
|
195
|
-
} else if project.isNearLimit {
|
|
195
|
+
} else if self.project.isNearLimit {
|
|
196
196
|
Text("Near limit")
|
|
197
|
-
.font(.caption)
|
|
197
|
+
.font(RunicFont.caption)
|
|
198
198
|
.foregroundStyle(Color(red: 0.94, green: 0.74, blue: 0.26))
|
|
199
199
|
.fontWeight(.medium)
|
|
200
200
|
}
|
|
@@ -208,13 +208,13 @@ private struct ProjectBudgetRow: View {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
private var budgetText: String {
|
|
211
|
-
"\(UsageFormatter.usdString(project.spent)) / \(UsageFormatter.usdString(project.budget.monthlyLimit))"
|
|
211
|
+
"\(UsageFormatter.usdString(self.project.spent)) / \(UsageFormatter.usdString(self.project.budget.monthlyLimit))"
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
private var statusTextColor: Color {
|
|
215
215
|
if self.isHighlighted {
|
|
216
216
|
return MenuHighlightStyle.selectionText
|
|
217
217
|
}
|
|
218
|
-
return project.statusColor
|
|
218
|
+
return self.project.statusColor
|
|
219
219
|
}
|
|
220
220
|
}
|
|
@@ -33,11 +33,11 @@ struct ProviderTabBarView: View {
|
|
|
33
33
|
.aspectRatio(contentMode: .fit)
|
|
34
34
|
.frame(width: 18, height: 18)
|
|
35
35
|
} else if tab.provider == nil {
|
|
36
|
-
|
|
36
|
+
Image(systemName: "square.grid.2x2")
|
|
37
37
|
.font(.system(size: 14, weight: .medium))
|
|
38
38
|
}
|
|
39
39
|
Text(tab.label)
|
|
40
|
-
.font(.
|
|
40
|
+
.font(RunicFont.caption)
|
|
41
41
|
.fontWeight(tab.isSelected ? .semibold : .regular)
|
|
42
42
|
.lineLimit(1)
|
|
43
43
|
}
|
|
@@ -47,16 +47,14 @@ struct ProviderTabBarView: View {
|
|
|
47
47
|
Capsule(style: .continuous)
|
|
48
48
|
.fill(tab.isSelected
|
|
49
49
|
? tab.brandColor.opacity(RunicColors.Opacity.medium)
|
|
50
|
-
: Color(nsColor: .controlBackgroundColor).opacity(RunicColors.Opacity.subtle))
|
|
51
|
-
)
|
|
50
|
+
: Color(nsColor: .controlBackgroundColor).opacity(RunicColors.Opacity.subtle)))
|
|
52
51
|
.overlay(
|
|
53
52
|
Capsule(style: .continuous)
|
|
54
53
|
.stroke(
|
|
55
54
|
tab.isSelected
|
|
56
55
|
? tab.brandColor.opacity(RunicColors.Opacity.strong)
|
|
57
56
|
: Color(nsColor: .separatorColor).opacity(RunicColors.Opacity.light),
|
|
58
|
-
lineWidth: 0.5)
|
|
59
|
-
)
|
|
57
|
+
lineWidth: 0.5))
|
|
60
58
|
.foregroundStyle(tab.isSelected ? tab.brandColor : .secondary)
|
|
61
59
|
.shadow(
|
|
62
60
|
color: tab.isSelected ? tab.brandColor.opacity(0.15) : .clear,
|