@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
|
@@ -6,62 +6,62 @@ import Foundation
|
|
|
6
6
|
/// The core philosophy: **Zero Token Leakage, Maximum Efficiency**
|
|
7
7
|
enum PerformanceConstants {
|
|
8
8
|
// MARK: - Animation Frame Rates
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
/// Menubar icon animation (smooth on modern Macs)
|
|
11
11
|
static let menubarFPS: Double = 60
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
/// Popover menu animations (full detail when user is actively viewing)
|
|
14
14
|
static let popoverFPS: Double = 60
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
/// Status pulse effect (minimal overhead for subtle feedback)
|
|
17
17
|
static let statusPulseFPS: Double = 15
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
// MARK: - Data Fetching Policy
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
/// **Zero Token Leakage**: Maximum pings per session
|
|
22
22
|
/// - Single ping on menu open
|
|
23
23
|
/// - Additional pings only if truly stale (>5min old)
|
|
24
24
|
/// - Use cookies/cached data first
|
|
25
25
|
static let maxPingsPerSession = 1
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
/// Time before data considered stale (use cookies/cache until then)
|
|
28
28
|
static let staleDuration: TimeInterval = 300 // 5 minutes
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
/// Delay before first ping after menu opens
|
|
31
31
|
static let menuOpenPingDelay: Duration = .seconds(1.2)
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
// MARK: - Caching
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
/// Icon render cache size (prevent redundant GPU work)
|
|
36
36
|
static let iconCacheSize = 64
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
/// Morph animation cache size
|
|
39
39
|
static let morphCacheSize = 512
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
// MARK: - Resource Limits
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
/// Maximum concurrent provider fetches (prevent API spam)
|
|
44
44
|
static let maxConcurrentFetches = 2
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
/// Network timeout for single ping
|
|
47
47
|
static let networkTimeout: TimeInterval = 10
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
// MARK: - Animation Lifecycle
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
|
|
51
|
+
// Animation stops when:
|
|
52
|
+
// - Data successfully loaded
|
|
53
|
+
// - Error state reached (stale flag set)
|
|
54
|
+
// - User manually cancels
|
|
55
|
+
// - App enters background
|
|
56
|
+
|
|
57
57
|
/// Blink animation duration (slower "breath" to avoid flicker)
|
|
58
58
|
static let blinkDuration: TimeInterval = 0.8
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
/// Chance of double-blink effect
|
|
61
61
|
static let doubleBlinkChance: Double = 0.05
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
// MARK: - Memory Management
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
/// Auto-cleanup idle resources after this duration
|
|
66
66
|
static let idleCleanupDelay: TimeInterval = 60
|
|
67
67
|
}
|
|
@@ -73,13 +73,13 @@ enum PerformanceConstants {
|
|
|
73
73
|
@propertyWrapper
|
|
74
74
|
struct PerformanceCritical<T> {
|
|
75
75
|
private var value: T
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
init(wrappedValue: T) {
|
|
78
78
|
self.value = wrappedValue
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
var wrappedValue: T {
|
|
82
|
-
get { value }
|
|
83
|
-
set { value = newValue }
|
|
82
|
+
get { self.value }
|
|
83
|
+
set { self.value = newValue }
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -5,7 +5,6 @@ import SwiftUI
|
|
|
5
5
|
/// Menu view displaying active alerts with severity indicators and acknowledgement
|
|
6
6
|
@MainActor
|
|
7
7
|
struct AlertsMenuView: View {
|
|
8
|
-
|
|
9
8
|
// MARK: - Types
|
|
10
9
|
|
|
11
10
|
typealias AlertEntry = AlertRuleStore.AlertHistoryEntry
|
|
@@ -49,7 +48,7 @@ struct AlertsMenuView: View {
|
|
|
49
48
|
private var headerSection: some View {
|
|
50
49
|
HStack(alignment: .firstTextBaseline) {
|
|
51
50
|
Text("Alerts")
|
|
52
|
-
.font(.headline)
|
|
51
|
+
.font(RunicFont.headline)
|
|
53
52
|
.fontWeight(.semibold)
|
|
54
53
|
|
|
55
54
|
if self.unacknowledgedCount > 0 {
|
|
@@ -67,7 +66,7 @@ struct AlertsMenuView: View {
|
|
|
67
66
|
Task { await self.loadAlerts() }
|
|
68
67
|
} label: {
|
|
69
68
|
Image(systemName: "arrow.clockwise")
|
|
70
|
-
.font(.caption)
|
|
69
|
+
.font(RunicFont.caption)
|
|
71
70
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
72
71
|
}
|
|
73
72
|
.buttonStyle(.plain)
|
|
@@ -78,17 +77,18 @@ struct AlertsMenuView: View {
|
|
|
78
77
|
|
|
79
78
|
private var unacknowledgedBadge: some View {
|
|
80
79
|
Text("\(self.unacknowledgedCount)")
|
|
81
|
-
.font(.caption2)
|
|
80
|
+
.font(RunicFont.caption2)
|
|
82
81
|
.fontWeight(.semibold)
|
|
83
82
|
.foregroundStyle(.white)
|
|
84
83
|
.padding(.horizontal, RunicSpacing.compact)
|
|
85
84
|
.padding(.vertical, RunicSpacing.xxxs)
|
|
86
85
|
.background(Capsule().fill(Color.red))
|
|
87
|
-
.accessibilityLabel(
|
|
86
|
+
.accessibilityLabel(
|
|
87
|
+
"\(self.unacknowledgedCount) unacknowledged alert\(self.unacknowledgedCount == 1 ? "" : "s")")
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
private var unacknowledgedCount: Int {
|
|
91
|
-
self.alerts.
|
|
91
|
+
self.alerts.count(where: { !$0.acknowledged })
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// MARK: - Alerts List
|
|
@@ -108,11 +108,11 @@ struct AlertsMenuView: View {
|
|
|
108
108
|
private var emptyStateView: some View {
|
|
109
109
|
VStack(spacing: RunicSpacing.xs) {
|
|
110
110
|
Image(systemName: "checkmark.shield")
|
|
111
|
-
.font(.title2)
|
|
111
|
+
.font(RunicFont.title2)
|
|
112
112
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
113
113
|
|
|
114
114
|
Text("No active alerts")
|
|
115
|
-
.font(.subheadline)
|
|
115
|
+
.font(RunicFont.subheadline)
|
|
116
116
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
117
117
|
}
|
|
118
118
|
.frame(maxWidth: .infinity)
|
|
@@ -158,13 +158,13 @@ private struct AlertRow: View {
|
|
|
158
158
|
|
|
159
159
|
VStack(alignment: .leading, spacing: 2) {
|
|
160
160
|
Text(self.alert.message)
|
|
161
|
-
.font(.footnote)
|
|
161
|
+
.font(RunicFont.footnote)
|
|
162
162
|
.foregroundStyle(MenuHighlightStyle.primary(self.isHighlighted))
|
|
163
163
|
.lineLimit(3)
|
|
164
164
|
.fixedSize(horizontal: false, vertical: true)
|
|
165
165
|
|
|
166
166
|
Text(self.timeAgo(from: self.alert.triggeredAt))
|
|
167
|
-
.font(.caption2)
|
|
167
|
+
.font(RunicFont.caption2)
|
|
168
168
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -184,33 +184,31 @@ private struct AlertRow: View {
|
|
|
184
184
|
let color = self.severityColor
|
|
185
185
|
|
|
186
186
|
return Image(systemName: icon)
|
|
187
|
-
.font(.caption)
|
|
187
|
+
.font(RunicFont.caption)
|
|
188
188
|
.foregroundStyle(color)
|
|
189
189
|
.frame(width: 16, alignment: .center)
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
private var severityIconName: String {
|
|
193
193
|
switch self.alert.severity {
|
|
194
|
-
case .info:
|
|
195
|
-
case .warning:
|
|
196
|
-
case .critical:
|
|
194
|
+
case .info: "info.circle.fill"
|
|
195
|
+
case .warning: "exclamationmark.triangle.fill"
|
|
196
|
+
case .critical: "exclamationmark.octagon.fill"
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
private var severityColor: Color {
|
|
201
201
|
switch self.alert.severity {
|
|
202
|
-
case .info:
|
|
203
|
-
case .warning:
|
|
204
|
-
case .critical:
|
|
202
|
+
case .info: .green
|
|
203
|
+
case .warning: .yellow
|
|
204
|
+
case .critical: .red
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
private var backgroundStyle: some View {
|
|
209
|
-
|
|
209
|
+
self.alert.acknowledged
|
|
210
210
|
? Color(nsColor: .controlBackgroundColor).opacity(0.5)
|
|
211
211
|
: Color(nsColor: .controlBackgroundColor)
|
|
212
|
-
|
|
213
|
-
return baseColor
|
|
214
212
|
}
|
|
215
213
|
|
|
216
214
|
private var acknowledgeButton: some View {
|
|
@@ -227,7 +225,7 @@ private struct AlertRow: View {
|
|
|
227
225
|
.frame(width: 12, height: 12)
|
|
228
226
|
} else {
|
|
229
227
|
Image(systemName: "checkmark.circle")
|
|
230
|
-
.font(.caption)
|
|
228
|
+
.font(RunicFont.caption)
|
|
231
229
|
.foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))
|
|
232
230
|
}
|
|
233
231
|
}
|
|
@@ -36,7 +36,7 @@ struct CostComparativeChartMenuView: View {
|
|
|
36
36
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
37
37
|
if model.isEmpty {
|
|
38
38
|
Text("No cost comparison data.")
|
|
39
|
-
.font(.footnote)
|
|
39
|
+
.font(RunicFont.footnote)
|
|
40
40
|
.foregroundStyle(.secondary)
|
|
41
41
|
} else {
|
|
42
42
|
Chart {
|
|
@@ -55,7 +55,7 @@ struct CostComparativeChartMenuView: View {
|
|
|
55
55
|
AxisValueLabel {
|
|
56
56
|
if let cost = value.as(Double.self) {
|
|
57
57
|
Text(UsageFormatter.usdString(cost))
|
|
58
|
-
.font(.caption2)
|
|
58
|
+
.font(RunicFont.caption2)
|
|
59
59
|
.foregroundStyle(Color(nsColor: .tertiaryLabelColor))
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -66,7 +66,7 @@ struct CostComparativeChartMenuView: View {
|
|
|
66
66
|
AxisGridLine().foregroundStyle(Color.clear)
|
|
67
67
|
AxisTick().foregroundStyle(Color.clear)
|
|
68
68
|
AxisValueLabel()
|
|
69
|
-
.font(.caption2)
|
|
69
|
+
.font(RunicFont.caption2)
|
|
70
70
|
.foregroundStyle(Color(nsColor: .labelColor))
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -94,14 +94,14 @@ struct CostComparativeChartMenuView: View {
|
|
|
94
94
|
let detail = self.detailLines(model: model)
|
|
95
95
|
VStack(alignment: .leading, spacing: 0) {
|
|
96
96
|
Text(detail.primary)
|
|
97
|
-
.font(.caption)
|
|
97
|
+
.font(RunicFont.caption)
|
|
98
98
|
.foregroundStyle(.secondary)
|
|
99
99
|
.lineLimit(2)
|
|
100
100
|
.truncationMode(.tail)
|
|
101
101
|
.frame(height: 32, alignment: .leading)
|
|
102
102
|
if let secondary = detail.secondary {
|
|
103
103
|
Text(secondary)
|
|
104
|
-
.font(.caption)
|
|
104
|
+
.font(RunicFont.caption)
|
|
105
105
|
.foregroundStyle(.secondary)
|
|
106
106
|
.lineLimit(1)
|
|
107
107
|
.truncationMode(.tail)
|
|
@@ -116,7 +116,7 @@ struct CostComparativeChartMenuView: View {
|
|
|
116
116
|
.fill(Self.cheapColor)
|
|
117
117
|
.frame(width: 8, height: 8)
|
|
118
118
|
Text("Low cost")
|
|
119
|
-
.font(.caption2)
|
|
119
|
+
.font(RunicFont.caption2)
|
|
120
120
|
.foregroundStyle(.secondary)
|
|
121
121
|
}
|
|
122
122
|
HStack(spacing: RunicSpacing.xxs) {
|
|
@@ -124,7 +124,7 @@ struct CostComparativeChartMenuView: View {
|
|
|
124
124
|
.fill(Self.mediumColor)
|
|
125
125
|
.frame(width: 8, height: 8)
|
|
126
126
|
Text("Medium cost")
|
|
127
|
-
.font(.caption2)
|
|
127
|
+
.font(RunicFont.caption2)
|
|
128
128
|
.foregroundStyle(.secondary)
|
|
129
129
|
}
|
|
130
130
|
HStack(spacing: RunicSpacing.xxs) {
|
|
@@ -132,7 +132,7 @@ struct CostComparativeChartMenuView: View {
|
|
|
132
132
|
.fill(Self.expensiveColor)
|
|
133
133
|
.frame(width: 8, height: 8)
|
|
134
134
|
Text("High cost")
|
|
135
|
-
.font(.caption2)
|
|
135
|
+
.font(RunicFont.caption2)
|
|
136
136
|
.foregroundStyle(.secondary)
|
|
137
137
|
}
|
|
138
138
|
}
|
|
@@ -151,12 +151,13 @@ struct CostComparativeChartMenuView: View {
|
|
|
151
151
|
private static func makeModel(from summaries: [UsageLedgerModelSummary]) -> [ModelCostData] {
|
|
152
152
|
let withCost = summaries.compactMap { summary -> ModelCostData? in
|
|
153
153
|
guard let cost = summary.totals.costUSD, cost > 0,
|
|
154
|
-
summary.totals.totalTokens > 0
|
|
154
|
+
summary.totals.totalTokens > 0
|
|
155
|
+
else {
|
|
155
156
|
return nil
|
|
156
157
|
}
|
|
157
158
|
// Calculate average cost per token across all models
|
|
158
|
-
let totalCost = summaries.compactMap
|
|
159
|
-
let totalTokens = summaries.map
|
|
159
|
+
let totalCost = summaries.compactMap(\.totals.costUSD).reduce(0, +)
|
|
160
|
+
let totalTokens = summaries.map(\.totals.totalTokens).reduce(0, +)
|
|
160
161
|
let averageCostPerToken = totalTokens > 0 ? totalCost / Double(totalTokens) : 0
|
|
161
162
|
|
|
162
163
|
return ModelCostData(
|
|
@@ -171,11 +172,11 @@ struct CostComparativeChartMenuView: View {
|
|
|
171
172
|
|
|
172
173
|
private static func colorForExpensiveness(_ relative: Double) -> Color {
|
|
173
174
|
if relative < 0.7 {
|
|
174
|
-
|
|
175
|
+
self.cheapColor
|
|
175
176
|
} else if relative < 1.5 {
|
|
176
|
-
|
|
177
|
+
self.mediumColor
|
|
177
178
|
} else {
|
|
178
|
-
|
|
179
|
+
self.expensiveColor
|
|
179
180
|
}
|
|
180
181
|
}
|
|
181
182
|
|
|
@@ -255,13 +256,12 @@ struct CostComparativeChartMenuView: View {
|
|
|
255
256
|
let costPer1M = data.costPerToken * 1_000_000
|
|
256
257
|
let primary = "\(displayName): \(UsageFormatter.usdString(costPer1M)) per 1M tokens"
|
|
257
258
|
|
|
258
|
-
let relativeText
|
|
259
|
-
|
|
260
|
-
relativeText = String(format: "%.1fx more expensive than average", data.relativeExpensiveness)
|
|
259
|
+
let relativeText = if data.relativeExpensiveness > 1.1 {
|
|
260
|
+
String(format: "%.1fx more expensive than average", data.relativeExpensiveness)
|
|
261
261
|
} else if data.relativeExpensiveness < 0.9 {
|
|
262
|
-
|
|
262
|
+
String(format: "%.1fx cheaper than average", 1.0 / data.relativeExpensiveness)
|
|
263
263
|
} else {
|
|
264
|
-
|
|
264
|
+
"Close to average cost"
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
let tokens = UsageFormatter.tokenCountString(data.totalTokens)
|
|
@@ -39,7 +39,7 @@ struct CostHistoryChartMenuView: View {
|
|
|
39
39
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
40
40
|
if model.points.isEmpty {
|
|
41
41
|
Text("No cost history data.")
|
|
42
|
-
.font(.footnote)
|
|
42
|
+
.font(RunicFont.footnote)
|
|
43
43
|
.foregroundStyle(.secondary)
|
|
44
44
|
} else {
|
|
45
45
|
Chart {
|
|
@@ -64,7 +64,7 @@ struct CostHistoryChartMenuView: View {
|
|
|
64
64
|
AxisGridLine().foregroundStyle(Color.clear)
|
|
65
65
|
AxisTick().foregroundStyle(Color.clear)
|
|
66
66
|
AxisValueLabel(format: .dateTime.month(.abbreviated).day())
|
|
67
|
-
.font(.caption2)
|
|
67
|
+
.font(RunicFont.caption2)
|
|
68
68
|
.foregroundStyle(Color(nsColor: .tertiaryLabelColor))
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -94,9 +94,9 @@ struct CostHistoryChartMenuView: View {
|
|
|
94
94
|
if self.showHoverHint {
|
|
95
95
|
HStack(spacing: RunicSpacing.xxs) {
|
|
96
96
|
Image(systemName: "cursorarrow.click.2")
|
|
97
|
-
.font(.caption2)
|
|
97
|
+
.font(RunicFont.caption2)
|
|
98
98
|
Text("Hover to explore")
|
|
99
|
-
.font(.caption2)
|
|
99
|
+
.font(RunicFont.caption2)
|
|
100
100
|
}
|
|
101
101
|
.foregroundStyle(.tertiary)
|
|
102
102
|
.transition(.opacity)
|
|
@@ -105,13 +105,13 @@ struct CostHistoryChartMenuView: View {
|
|
|
105
105
|
let detail = self.detailLines(model: model)
|
|
106
106
|
VStack(alignment: .leading, spacing: 0) {
|
|
107
107
|
Text(detail.primary)
|
|
108
|
-
.font(.
|
|
108
|
+
.font(RunicFont.caption)
|
|
109
109
|
.foregroundStyle(.secondary)
|
|
110
110
|
.lineLimit(1)
|
|
111
111
|
.truncationMode(.tail)
|
|
112
112
|
.frame(height: 16, alignment: .leading)
|
|
113
113
|
Text(detail.secondary ?? " ")
|
|
114
|
-
.font(.
|
|
114
|
+
.font(RunicFont.caption)
|
|
115
115
|
.foregroundStyle(.secondary)
|
|
116
116
|
.lineLimit(1)
|
|
117
117
|
.truncationMode(.tail)
|
|
@@ -122,7 +122,7 @@ struct CostHistoryChartMenuView: View {
|
|
|
122
122
|
|
|
123
123
|
if let total = self.totalCostUSD {
|
|
124
124
|
Text("Total (30d): \(UsageFormatter.usdString(total))")
|
|
125
|
-
.font(.
|
|
125
|
+
.font(RunicFont.caption)
|
|
126
126
|
.foregroundStyle(.secondary)
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -30,7 +30,7 @@ struct CreditsHistoryChartMenuView: View {
|
|
|
30
30
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
31
31
|
if model.points.isEmpty {
|
|
32
32
|
Text("No credits history data.")
|
|
33
|
-
.font(.footnote)
|
|
33
|
+
.font(RunicFont.footnote)
|
|
34
34
|
.foregroundStyle(.secondary)
|
|
35
35
|
} else {
|
|
36
36
|
Chart {
|
|
@@ -55,7 +55,7 @@ struct CreditsHistoryChartMenuView: View {
|
|
|
55
55
|
AxisGridLine().foregroundStyle(Color.clear)
|
|
56
56
|
AxisTick().foregroundStyle(Color.clear)
|
|
57
57
|
AxisValueLabel(format: .dateTime.month(.abbreviated).day())
|
|
58
|
-
.font(.caption2)
|
|
58
|
+
.font(RunicFont.caption2)
|
|
59
59
|
.foregroundStyle(Color(nsColor: .tertiaryLabelColor))
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -83,13 +83,13 @@ struct CreditsHistoryChartMenuView: View {
|
|
|
83
83
|
let detail = self.detailLines(model: model)
|
|
84
84
|
VStack(alignment: .leading, spacing: 0) {
|
|
85
85
|
Text(detail.primary)
|
|
86
|
-
.font(.
|
|
86
|
+
.font(RunicFont.caption)
|
|
87
87
|
.foregroundStyle(.secondary)
|
|
88
88
|
.lineLimit(1)
|
|
89
89
|
.truncationMode(.tail)
|
|
90
90
|
.frame(height: 16, alignment: .leading)
|
|
91
91
|
Text(detail.secondary ?? " ")
|
|
92
|
-
.font(.
|
|
92
|
+
.font(RunicFont.caption)
|
|
93
93
|
.foregroundStyle(.secondary)
|
|
94
94
|
.lineLimit(1)
|
|
95
95
|
.truncationMode(.tail)
|
|
@@ -99,7 +99,7 @@ struct CreditsHistoryChartMenuView: View {
|
|
|
99
99
|
|
|
100
100
|
if let total = model.totalCreditsUsed {
|
|
101
101
|
Text("Total (30d): \(total.formatted(.number.precision(.fractionLength(0...2)))) credits")
|
|
102
|
-
.font(.
|
|
102
|
+
.font(RunicFont.caption)
|
|
103
103
|
.foregroundStyle(.secondary)
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -11,10 +11,10 @@ struct EfficiencyMetricsMenuView: View {
|
|
|
11
11
|
|
|
12
12
|
var label: String {
|
|
13
13
|
switch self {
|
|
14
|
-
case .model:
|
|
15
|
-
case .tokensPerRequest:
|
|
16
|
-
case .costPerRequest:
|
|
17
|
-
case .cacheHitRate:
|
|
14
|
+
case .model: "Model"
|
|
15
|
+
case .tokensPerRequest: "Tokens/Req"
|
|
16
|
+
case .costPerRequest: "Cost/Req"
|
|
17
|
+
case .cacheHitRate: "Cache Hit %"
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -65,12 +65,12 @@ struct EfficiencyMetricsMenuView: View {
|
|
|
65
65
|
|
|
66
66
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
67
67
|
Text("Efficiency Metrics")
|
|
68
|
-
.font(.headline)
|
|
68
|
+
.font(RunicFont.headline)
|
|
69
69
|
.fontWeight(.semibold)
|
|
70
70
|
|
|
71
71
|
if model.isEmpty {
|
|
72
72
|
Text("No efficiency metrics available.")
|
|
73
|
-
.font(.footnote)
|
|
73
|
+
.font(RunicFont.footnote)
|
|
74
74
|
.foregroundStyle(.secondary)
|
|
75
75
|
} else {
|
|
76
76
|
VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
|
|
@@ -134,27 +134,27 @@ struct EfficiencyMetricsMenuView: View {
|
|
|
134
134
|
HStack(spacing: RunicSpacing.lg) {
|
|
135
135
|
VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
|
|
136
136
|
Text("Total Requests")
|
|
137
|
-
.font(.caption2)
|
|
137
|
+
.font(RunicFont.caption2)
|
|
138
138
|
.foregroundStyle(.secondary)
|
|
139
139
|
Text("\(Self.totalRequests(model))")
|
|
140
|
-
.font(.caption)
|
|
140
|
+
.font(RunicFont.caption)
|
|
141
141
|
.fontWeight(.medium)
|
|
142
142
|
}
|
|
143
143
|
VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
|
|
144
144
|
Text("Avg Tokens/Req")
|
|
145
|
-
.font(.caption2)
|
|
145
|
+
.font(RunicFont.caption2)
|
|
146
146
|
.foregroundStyle(.secondary)
|
|
147
147
|
Text(UsageFormatter.tokenCountString(Self.averageTokensPerRequest(model)))
|
|
148
|
-
.font(.caption)
|
|
148
|
+
.font(RunicFont.caption)
|
|
149
149
|
.fontWeight(.medium)
|
|
150
150
|
}
|
|
151
151
|
if Self.hasAnyCost(model) {
|
|
152
152
|
VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
|
|
153
153
|
Text("Avg Cost/Req")
|
|
154
|
-
.font(.caption2)
|
|
154
|
+
.font(RunicFont.caption2)
|
|
155
155
|
.foregroundStyle(.secondary)
|
|
156
156
|
Text(UsageFormatter.usdString(Self.averageCostPerRequest(model)))
|
|
157
|
-
.font(.caption)
|
|
157
|
+
.font(RunicFont.caption)
|
|
158
158
|
.fontWeight(.medium)
|
|
159
159
|
}
|
|
160
160
|
}
|
|
@@ -181,7 +181,7 @@ struct EfficiencyMetricsMenuView: View {
|
|
|
181
181
|
ascending: Bool) -> [ModelMetrics]
|
|
182
182
|
{
|
|
183
183
|
let metrics = summaries.map { ModelMetrics(summary: $0) }
|
|
184
|
-
|
|
184
|
+
return metrics.sorted { lhs, rhs in
|
|
185
185
|
let result: Bool
|
|
186
186
|
switch sortBy {
|
|
187
187
|
case .model:
|
|
@@ -197,7 +197,6 @@ struct EfficiencyMetricsMenuView: View {
|
|
|
197
197
|
}
|
|
198
198
|
return ascending ? result : !result
|
|
199
199
|
}
|
|
200
|
-
return sorted
|
|
201
200
|
}
|
|
202
201
|
|
|
203
202
|
private static func totalRequests(_ metrics: [ModelMetrics]) -> Int {
|
|
@@ -237,12 +236,12 @@ private struct SortableColumnHeader: View {
|
|
|
237
236
|
} label: {
|
|
238
237
|
HStack(spacing: RunicSpacing.xxs) {
|
|
239
238
|
Text(self.title)
|
|
240
|
-
.font(.caption)
|
|
239
|
+
.font(RunicFont.caption)
|
|
241
240
|
.foregroundStyle(.secondary)
|
|
242
241
|
.fontWeight(.medium)
|
|
243
242
|
if self.currentSort == self.column {
|
|
244
243
|
Image(systemName: self.ascending ? "chevron.up" : "chevron.down")
|
|
245
|
-
.font(.caption2)
|
|
244
|
+
.font(RunicFont.caption2)
|
|
246
245
|
.foregroundStyle(.secondary)
|
|
247
246
|
}
|
|
248
247
|
}
|
|
@@ -259,34 +258,34 @@ private struct MetricsRow: View {
|
|
|
259
258
|
var body: some View {
|
|
260
259
|
HStack(spacing: 0) {
|
|
261
260
|
VStack(alignment: .leading, spacing: RunicSpacing.xxxs) {
|
|
262
|
-
Text(UsageFormatter.modelDisplayName(metrics.modelName))
|
|
263
|
-
.font(.caption)
|
|
261
|
+
Text(UsageFormatter.modelDisplayName(self.metrics.modelName))
|
|
262
|
+
.font(RunicFont.caption)
|
|
264
263
|
.lineLimit(1)
|
|
265
264
|
.truncationMode(.tail)
|
|
266
265
|
if let context = UsageFormatter.modelContextLabel(for: metrics.modelName) {
|
|
267
266
|
Text(context)
|
|
268
|
-
.font(.caption2)
|
|
267
|
+
.font(RunicFont.caption2)
|
|
269
268
|
.foregroundStyle(.secondary)
|
|
270
269
|
.lineLimit(1)
|
|
271
270
|
.truncationMode(.tail)
|
|
272
271
|
}
|
|
273
272
|
}
|
|
274
273
|
.frame(width: 140, alignment: .leading)
|
|
275
|
-
Text(UsageFormatter.tokenCountString(Int(metrics.tokensPerRequest)))
|
|
276
|
-
.font(.caption)
|
|
274
|
+
Text(UsageFormatter.tokenCountString(Int(self.metrics.tokensPerRequest)))
|
|
275
|
+
.font(RunicFont.caption)
|
|
277
276
|
.frame(width: 90, alignment: .leading)
|
|
278
277
|
if let cost = metrics.costPerRequest {
|
|
279
278
|
Text(UsageFormatter.usdString(cost))
|
|
280
|
-
.font(.caption)
|
|
279
|
+
.font(RunicFont.caption)
|
|
281
280
|
.frame(width: 80, alignment: .leading)
|
|
282
281
|
} else {
|
|
283
282
|
Text("—")
|
|
284
|
-
.font(.caption)
|
|
283
|
+
.font(RunicFont.caption)
|
|
285
284
|
.foregroundStyle(.secondary)
|
|
286
285
|
.frame(width: 80, alignment: .leading)
|
|
287
286
|
}
|
|
288
|
-
Text(String(format: "%.1f%%", metrics.cacheHitRate * 100))
|
|
289
|
-
.font(.caption)
|
|
287
|
+
Text(String(format: "%.1f%%", self.metrics.cacheHitRate * 100))
|
|
288
|
+
.font(RunicFont.caption)
|
|
290
289
|
.foregroundStyle(self.cacheHitColor)
|
|
291
290
|
.frame(width: 80, alignment: .leading)
|
|
292
291
|
}
|
|
@@ -301,12 +300,12 @@ private struct MetricsRow: View {
|
|
|
301
300
|
}
|
|
302
301
|
|
|
303
302
|
private var cacheHitColor: Color {
|
|
304
|
-
if metrics.cacheHitRate > 0.5 {
|
|
305
|
-
|
|
306
|
-
} else if metrics.cacheHitRate > 0.2 {
|
|
307
|
-
|
|
303
|
+
if self.metrics.cacheHitRate > 0.5 {
|
|
304
|
+
Color(red: 0.46, green: 0.75, blue: 0.36)
|
|
305
|
+
} else if self.metrics.cacheHitRate > 0.2 {
|
|
306
|
+
Color(red: 0.94, green: 0.74, blue: 0.26)
|
|
308
307
|
} else {
|
|
309
|
-
|
|
308
|
+
.secondary
|
|
310
309
|
}
|
|
311
310
|
}
|
|
312
311
|
}
|