diff --git a/go.mod b/go.mod index 4266e719be5..a7216a4135b 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/charmbracelet/bubbletea v1.3.4 github.com/charmbracelet/glamour v0.9.1 github.com/charmbracelet/lipgloss v1.1.0 + github.com/charmbracelet/log v0.4.2 github.com/charmbracelet/x/ansi v0.8.0 github.com/fsnotify/fsnotify v1.8.0 github.com/go-logfmt/logfmt v0.6.0 @@ -34,10 +35,7 @@ require ( github.com/stretchr/testify v1.10.0 ) -require ( - github.com/charmbracelet/log v0.4.2 // indirect - golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect -) +require golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect require ( cloud.google.com/go v0.116.0 // indirect @@ -74,7 +72,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/s2a-go v0.1.8 // indirect diff --git a/go.sum b/go.sum index 83184ff7915..7af1d544a56 100644 --- a/go.sum +++ b/go.sum @@ -113,8 +113,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= -github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= diff --git a/internal/llm/models/anthropic.go b/internal/llm/models/anthropic.go index f67a748424e..d8f8c08469f 100644 --- a/internal/llm/models/anthropic.go +++ b/internal/llm/models/anthropic.go @@ -10,6 +10,7 @@ const ( Claude35Haiku ModelID = "claude-3.5-haiku" Claude3Opus ModelID = "claude-3-opus" Claude4Sonnet ModelID = "claude-4-sonnet" + Claude4Opus ModelID = "claude-4-opus" ) // https://docs.anthropic.com/en/docs/about-claude/models/all-models @@ -68,6 +69,20 @@ var AnthropicModels = map[ModelID]Model{ CanReason: true, SupportsAttachments: true, }, + Claude4Opus: { + ID: Claude4Opus, + Name: "Claude 4 Opus", + Provider: ProviderAnthropic, + APIModel: "claude-opus-4-20250514", + CostPer1MIn: 15.0, + CostPer1MInCached: 18.75, + CostPer1MOutCached: 1.50, + CostPer1MOut: 75.0, + ContextWindow: 200000, + DefaultMaxTokens: 32000, + CanReason: true, + SupportsAttachments: true, + }, Claude35Haiku: { ID: Claude35Haiku, Name: "Claude 3.5 Haiku", diff --git a/internal/llm/models/bedrock.go b/internal/llm/models/bedrock.go index 06f82565413..8386bef9008 100644 --- a/internal/llm/models/bedrock.go +++ b/internal/llm/models/bedrock.go @@ -5,6 +5,8 @@ const ( // Models BedrockClaude37Sonnet ModelID = "bedrock.claude-3.7-sonnet" + BedrockClaude4Sonnet ModelID = "bedrock.claude-4.0-sonnet" + BedrockClaude4Opus ModelID = "bedrock.claude-4.0-opus" ) var BedrockModels = map[ModelID]Model{ @@ -22,4 +24,32 @@ var BedrockModels = map[ModelID]Model{ CanReason: true, SupportsAttachments: true, }, + BedrockClaude4Sonnet: { + ID: BedrockClaude4Sonnet, + Name: "Bedrock: Claude 4 Sonnet", + Provider: ProviderBedrock, + APIModel: "anthropic.claude-sonnet-4-20250514-v1:0", + CostPer1MIn: 3.0, + CostPer1MInCached: 3.75, + CostPer1MOutCached: 0.30, + CostPer1MOut: 15.0, + ContextWindow: 200_000, + DefaultMaxTokens: 50_000, + CanReason: true, + SupportsAttachments: true, + }, + BedrockClaude4Opus: { + ID: BedrockClaude4Opus, + Name: "Bedrock: Claude 4 Opus", + Provider: ProviderBedrock, + APIModel: "anthropic.claude-opus-4-20250514-v1:0", + CostPer1MIn: 15.0, + CostPer1MInCached: 18.75, + CostPer1MOutCached: 1.50, + CostPer1MOut: 75.0, + ContextWindow: 200_000, + DefaultMaxTokens: 50_000, + CanReason: true, + SupportsAttachments: true, + }, } diff --git a/internal/tui/theme/nord.go b/internal/tui/theme/nord.go new file mode 100644 index 00000000000..e894f987d69 --- /dev/null +++ b/internal/tui/theme/nord.go @@ -0,0 +1,107 @@ +package theme + +import ( + "github.com/charmbracelet/lipgloss" +) + +// NordTheme implements the Theme interface with Nord colors. +// It provides both list and dark variants based on the Nord palette. +type NordTheme struct { + BaseTheme +} + +// NewNordTheme creates a new instance of the Nord theme. +func NewNordTheme() *NordTheme { + // Nord color palette from https://www.nordtheme.com/docs/colors-and-palettes + polarNight0 := "#2E3440" + polarNight1 := "#3B4252" + polarNight2 := "#434C5E" + polarNight3 := "#4C566A" + snowStorm0 := "#D8DEE9" + snowStorm1 := "#E5E9F0" + snowStorm2 := "#ECEFF4" + frost0 := "#8FBCBB" + frost1 := "#88C0D0" + frost2 := "#81A1C1" + frost3 := "#5E81AC" + aurora0 := "#BF616A" + // aurora1 := "#D08770" + aurora2 := "#EBCB8B" + aurora3 := "#A3BE8C" + aurora4 := "#B48EAD" + + theme := &NordTheme{} + + // Base colors + theme.PrimaryColor = lipgloss.AdaptiveColor{Dark: frost1, Light: frost1} + theme.SecondaryColor = lipgloss.AdaptiveColor{Dark: frost2, Light: frost2} + theme.AccentColor = lipgloss.AdaptiveColor{Dark: frost0, Light: frost0} + + // Status colors + theme.ErrorColor = lipgloss.AdaptiveColor{Dark: aurora0, Light: aurora0} + theme.WarningColor = lipgloss.AdaptiveColor{Dark: aurora2, Light: aurora2} + theme.SuccessColor = lipgloss.AdaptiveColor{Dark: aurora3, Light: aurora3} + theme.InfoColor = lipgloss.AdaptiveColor{Dark: frost0, Light: frost0} + + // Text colors + theme.TextColor = lipgloss.AdaptiveColor{Dark: snowStorm2, Light: polarNight0} + theme.TextMutedColor = lipgloss.AdaptiveColor{Dark: snowStorm0, Light: polarNight3} + theme.TextEmphasizedColor = lipgloss.AdaptiveColor{Dark: aurora2, Light: aurora2} + + // Background colors + theme.BackgroundColor = lipgloss.AdaptiveColor{Dark: polarNight0, Light: snowStorm2} + theme.BackgroundSecondaryColor = lipgloss.AdaptiveColor{Dark: polarNight1, Light: snowStorm1} + theme.BackgroundDarkerColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm0} + + // Border colors + theme.BorderNormalColor = lipgloss.AdaptiveColor{Dark: polarNight3, Light: snowStorm1} + theme.BorderFocusedColor = lipgloss.AdaptiveColor{Dark: frost3, Light: frost3} + theme.BorderDimColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm0} + + // Diff view colors + theme.DiffAddedColor = lipgloss.AdaptiveColor{Dark: aurora3, Light: aurora3} + theme.DiffRemovedColor = lipgloss.AdaptiveColor{Dark: aurora0, Light: aurora0} + theme.DiffContextColor = lipgloss.AdaptiveColor{Dark: polarNight3, Light: snowStorm0} + theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{Dark: frost3, Light: frost3} + theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{Dark: frost3, Light: frost3} + theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{Dark: aurora0, Light: aurora0} + theme.DiffAddedBgColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm0} + theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm0} + theme.DiffContextBgColor = lipgloss.AdaptiveColor{Dark: polarNight1, Light: snowStorm1} + theme.DiffLineNumberColor = lipgloss.AdaptiveColor{Dark: polarNight3, Light: snowStorm1} + theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm0} + theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm0} + + // Markdown colors + theme.MarkdownTextColor = lipgloss.AdaptiveColor{Dark: snowStorm2, Light: polarNight0} + theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{Dark: frost3, Light: frost3} + theme.MarkdownLinkColor = lipgloss.AdaptiveColor{Dark: frost0, Light: frost0} + theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{Dark: frost1, Light: frost1} + theme.MarkdownCodeColor = lipgloss.AdaptiveColor{Dark: aurora3, Light: aurora3} + theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{Dark: aurora2, Light: aurora2} + theme.MarkdownEmphColor = lipgloss.AdaptiveColor{Dark: aurora2, Light: aurora2} + theme.MarkdownStrongColor = lipgloss.AdaptiveColor{Dark: aurora0, Light: aurora0} + theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{Dark: polarNight3, Light: snowStorm1} + theme.MarkdownListItemColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm1} + theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm1} + theme.MarkdownImageColor = lipgloss.AdaptiveColor{Dark: frost1, Light: frost1} + theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{Dark: frost1, Light: frost1} + theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm0} + + // Syntax highsnowStorming colors + theme.SyntaxCommentColor = lipgloss.AdaptiveColor{Dark: polarNight3, Light: snowStorm2} + theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{Dark: aurora4, Light: aurora4} + theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{Dark: frost1, Light: frost1} + theme.SyntaxVariableColor = lipgloss.AdaptiveColor{Dark: frost0, Light: frost0} + theme.SyntaxStringColor = lipgloss.AdaptiveColor{Dark: aurora2, Light: aurora2} + theme.SyntaxNumberColor = lipgloss.AdaptiveColor{Dark: aurora2, Light: aurora2} + theme.SyntaxTypeColor = lipgloss.AdaptiveColor{Dark: aurora3, Light: aurora3} + theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{Dark: aurora4, Light: aurora4} + theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{Dark: polarNight2, Light: snowStorm2} + + return theme +} + +func init() { + RegisterTheme("nord", NewNordTheme()) +} diff --git a/internal/tui/theme/theme_test.go b/internal/tui/theme/theme_test.go index 790ee3aa8a3..f64ba080d99 100644 --- a/internal/tui/theme/theme_test.go +++ b/internal/tui/theme/theme_test.go @@ -47,6 +47,18 @@ func TestThemeRegistration(t *testing.T) { t.Errorf("Monokai theme is not registered") } + // Check if "nord" theme is registered + nordFound := false + for _, themeName := range availableThemes { + if themeName == "nord" { + nordFound = true + break + } + } + if !nordFound { + t.Errorf("Nord theme is not registered") + } + // Try to get the themes and make sure they're not nil catppuccin := GetTheme("catppuccin") if catppuccin == nil { diff --git a/www/src/content/docs/docs/themes.mdx b/www/src/content/docs/docs/themes.mdx index e691a22e7f0..e8e7b594bd8 100644 --- a/www/src/content/docs/docs/themes.mdx +++ b/www/src/content/docs/docs/themes.mdx @@ -14,6 +14,7 @@ The following predefined themes are available: - `flexoki` - `gruvbox` - `monokai` +- `nord` - `onedark` - `tokyonight` - `tron`