on main help sign in
chore: run `gofumpt`
0927446Andrey Nering
internal/app/lsp.go+5−5
@@ -25,7 +25,7 @@ func (app *App) initLSPClients(ctx context.Context) {
25func (app *App) createAndStartLSPClient(ctx context.Context, name string, command string, args ...string) {
25func (app *App) createAndStartLSPClient(ctx context.Context, name string, command string, args ...string) {
26 // Create a specific context for initialization with a timeout
26 // Create a specific context for initialization with a timeout
27 logging.Info("Creating LSP client", "name", name, "command", command, "args", args)
27 logging.Info("Creating LSP client", "name", name, "command", command, "args", args)
28−
28+
29 // Create the LSP client
29 // Create the LSP client
30 lspClient, err := lsp.NewClient(ctx, command, args...)
30 lspClient, err := lsp.NewClient(ctx, command, args...)
31 if err != nil {
31 if err != nil {
@@ -36,7 +36,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, comman
36 // Create a longer timeout for initialization (some servers take time to start)
36 // Create a longer timeout for initialization (some servers take time to start)
37 initCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
37 initCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
38 defer cancel()
38 defer cancel()
39−
39+
40 // Initialize with the initialization context
40 // Initialize with the initialization context
41 _, err = lspClient.InitializeLSPClient(initCtx, config.WorkingDirectory())
41 _, err = lspClient.InitializeLSPClient(initCtx, config.WorkingDirectory())
42 if err != nil {
42 if err != nil {
@@ -57,13 +57,13 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, comman
57 }
57 }
58
58
59 logging.Info("LSP client initialized", "name", name)
59 logging.Info("LSP client initialized", "name", name)
60−
60+
61 // Create a child context that can be canceled when the app is shutting down
61 // Create a child context that can be canceled when the app is shutting down
62 watchCtx, cancelFunc := context.WithCancel(ctx)
62 watchCtx, cancelFunc := context.WithCancel(ctx)
63−
63+
64 // Create a context with the server name for better identification
64 // Create a context with the server name for better identification
65 watchCtx = context.WithValue(watchCtx, "serverName", name)
65 watchCtx = context.WithValue(watchCtx, "serverName", name)
66−
66+
67 // Create the workspace watcher
67 // Create the workspace watcher
68 workspaceWatcher := watcher.NewWorkspaceWatcher(lspClient)
68 workspaceWatcher := watcher.NewWorkspaceWatcher(lspClient)
69
69
internal/config/init.go−1
@@ -58,4 +58,3 @@ func MarkProjectInitialized() error {
58
58
59 return nil
59 return nil
60}
60}
61−
internal/llm/prompt/prompt_test.go+3−3
@@ -44,13 +44,13 @@ func createTestFiles(t *testing.T, tmpDir string, testFiles []string) {
44 for _, path := range testFiles {
44 for _, path := range testFiles {
45 fullPath := filepath.Join(tmpDir, path)
45 fullPath := filepath.Join(tmpDir, path)
46 if path[len(path)-1] == '/' {
46 if path[len(path)-1] == '/' {
47− err := os.MkdirAll(fullPath, 0755)
47+ err := os.MkdirAll(fullPath, 0o755)
48 require.NoError(t, err)
48 require.NoError(t, err)
49 } else {
49 } else {
50 dir := filepath.Dir(fullPath)
50 dir := filepath.Dir(fullPath)
51− err := os.MkdirAll(dir, 0755)
51+ err := os.MkdirAll(dir, 0o755)
52 require.NoError(t, err)
52 require.NoError(t, err)
53− err = os.WriteFile(fullPath, []byte(path+": test content"), 0644)
53+ err = os.WriteFile(fullPath, []byte(path+": test content"), 0o644)
54 require.NoError(t, err)
54 require.NoError(t, err)
55 }
55 }
56 }
56 }
internal/llm/provider/azure.go−1
@@ -16,7 +16,6 @@ type azureClient struct {
16type AzureClient ProviderClient
16type AzureClient ProviderClient
17
17
18func newAzureClient(opts providerClientOptions) AzureClient {
18func newAzureClient(opts providerClientOptions) AzureClient {
19−
20 endpoint := os.Getenv("AZURE_OPENAI_ENDPOINT") // ex: https://foo.openai.azure.com
19 endpoint := os.Getenv("AZURE_OPENAI_ENDPOINT") // ex: https://foo.openai.azure.com
21 apiVersion := os.Getenv("AZURE_OPENAI_API_VERSION") // ex: 2025-04-01-preview
20 apiVersion := os.Getenv("AZURE_OPENAI_API_VERSION") // ex: 2025-04-01-preview
22
21
internal/llm/provider/bedrock.go−1
@@ -98,4 +98,3 @@ func (b *bedrockClient) stream(ctx context.Context, messages []message.Message,
98
98
99 return b.childProvider.stream(ctx, messages, tools)
99 return b.childProvider.stream(ctx, messages, tools)
100}
100}
101−
internal/llm/tools/ls_test.go+27−27
@@ -56,14 +56,14 @@ func TestLsTool_Run(t *testing.T) {
56 // Create directories
56 // Create directories
57 for _, dir := range testDirs {
57 for _, dir := range testDirs {
58 dirPath := filepath.Join(tempDir, dir)
58 dirPath := filepath.Join(tempDir, dir)
59− err := os.MkdirAll(dirPath, 0755)
59+ err := os.MkdirAll(dirPath, 0o755)
60 require.NoError(t, err)
60 require.NoError(t, err)
61 }
61 }
62
62
63 // Create files
63 // Create files
64 for _, file := range testFiles {
64 for _, file := range testFiles {
65 filePath := filepath.Join(tempDir, file)
65 filePath := filepath.Join(tempDir, file)
66− err := os.WriteFile(filePath, []byte("test content"), 0644)
66+ err := os.WriteFile(filePath, []byte("test content"), 0o644)
67 require.NoError(t, err)
67 require.NoError(t, err)
68 }
68 }
69
69
@@ -83,19 +83,19 @@ func TestLsTool_Run(t *testing.T) {
83
83
84 response, err := tool.Run(context.Background(), call)
84 response, err := tool.Run(context.Background(), call)
85 require.NoError(t, err)
85 require.NoError(t, err)
86−
86+
87 // Check that visible directories and files are included
87 // Check that visible directories and files are included
88 assert.Contains(t, response.Content, "dir1")
88 assert.Contains(t, response.Content, "dir1")
89 assert.Contains(t, response.Content, "dir2")
89 assert.Contains(t, response.Content, "dir2")
90 assert.Contains(t, response.Content, "dir3")
90 assert.Contains(t, response.Content, "dir3")
91 assert.Contains(t, response.Content, "file1.txt")
91 assert.Contains(t, response.Content, "file1.txt")
92 assert.Contains(t, response.Content, "file2.txt")
92 assert.Contains(t, response.Content, "file2.txt")
93−
93+
94 // Check that hidden files and directories are not included
94 // Check that hidden files and directories are not included
95 assert.NotContains(t, response.Content, ".hidden_dir")
95 assert.NotContains(t, response.Content, ".hidden_dir")
96 assert.NotContains(t, response.Content, ".hidden_file.txt")
96 assert.NotContains(t, response.Content, ".hidden_file.txt")
97 assert.NotContains(t, response.Content, ".hidden_root_file.txt")
97 assert.NotContains(t, response.Content, ".hidden_root_file.txt")
98−
98+
99 // Check that __pycache__ is not included
99 // Check that __pycache__ is not included
100 assert.NotContains(t, response.Content, "__pycache__")
100 assert.NotContains(t, response.Content, "__pycache__")
101 })
101 })
@@ -122,7 +122,7 @@ func TestLsTool_Run(t *testing.T) {
122 t.Run("handles empty path parameter", func(t *testing.T) {
122 t.Run("handles empty path parameter", func(t *testing.T) {
123 // For this test, we need to mock the config.WorkingDirectory function
123 // For this test, we need to mock the config.WorkingDirectory function
124 // Since we can't easily do that, we'll just check that the response doesn't contain an error message
124 // Since we can't easily do that, we'll just check that the response doesn't contain an error message
125−
125+
126 tool := NewLsTool()
126 tool := NewLsTool()
127 params := LSParams{
127 params := LSParams{
128 Path: "",
128 Path: "",
@@ -138,7 +138,7 @@ func TestLsTool_Run(t *testing.T) {
138
138
139 response, err := tool.Run(context.Background(), call)
139 response, err := tool.Run(context.Background(), call)
140 require.NoError(t, err)
140 require.NoError(t, err)
141−
141+
142 // The response should either contain a valid directory listing or an error
142 // The response should either contain a valid directory listing or an error
143 // We'll just check that it's not empty
143 // We'll just check that it's not empty
144 assert.NotEmpty(t, response.Content)
144 assert.NotEmpty(t, response.Content)
@@ -173,11 +173,11 @@ func TestLsTool_Run(t *testing.T) {
173
173
174 response, err := tool.Run(context.Background(), call)
174 response, err := tool.Run(context.Background(), call)
175 require.NoError(t, err)
175 require.NoError(t, err)
176−
176+
177 // The output format is a tree, so we need to check for specific patterns
177 // The output format is a tree, so we need to check for specific patterns
178 // Check that file1.txt is not directly mentioned
178 // Check that file1.txt is not directly mentioned
179 assert.NotContains(t, response.Content, "- file1.txt")
179 assert.NotContains(t, response.Content, "- file1.txt")
180−
180+
181 // Check that dir1/ is not directly mentioned
181 // Check that dir1/ is not directly mentioned
182 assert.NotContains(t, response.Content, "- dir1/")
182 assert.NotContains(t, response.Content, "- dir1/")
183 })
183 })
@@ -189,12 +189,12 @@ func TestLsTool_Run(t *testing.T) {
189 defer func() {
189 defer func() {
190 os.Chdir(origWd)
190 os.Chdir(origWd)
191 }()
191 }()
192−
192+
193 // Change to a directory above the temp directory
193 // Change to a directory above the temp directory
194 parentDir := filepath.Dir(tempDir)
194 parentDir := filepath.Dir(tempDir)
195 err = os.Chdir(parentDir)
195 err = os.Chdir(parentDir)
196 require.NoError(t, err)
196 require.NoError(t, err)
197−
197+
198 tool := NewLsTool()
198 tool := NewLsTool()
199 params := LSParams{
199 params := LSParams{
200 Path: filepath.Base(tempDir),
200 Path: filepath.Base(tempDir),
@@ -210,7 +210,7 @@ func TestLsTool_Run(t *testing.T) {
210
210
211 response, err := tool.Run(context.Background(), call)
211 response, err := tool.Run(context.Background(), call)
212 require.NoError(t, err)
212 require.NoError(t, err)
213−
213+
214 // Should list the temp directory contents
214 // Should list the temp directory contents
215 assert.Contains(t, response.Content, "dir1")
215 assert.Contains(t, response.Content, "dir1")
216 assert.Contains(t, response.Content, "file1.txt")
216 assert.Contains(t, response.Content, "file1.txt")
@@ -291,22 +291,22 @@ func TestCreateFileTree(t *testing.T) {
291 }
291 }
292
292
293 tree := createFileTree(paths)
293 tree := createFileTree(paths)
294−
294+
295 // Check the structure of the tree
295 // Check the structure of the tree
296 assert.Len(t, tree, 1) // Should have one root node
296 assert.Len(t, tree, 1) // Should have one root node
297−
297+
298 // Check the root node
298 // Check the root node
299 rootNode := tree[0]
299 rootNode := tree[0]
300 assert.Equal(t, "path", rootNode.Name)
300 assert.Equal(t, "path", rootNode.Name)
301 assert.Equal(t, "directory", rootNode.Type)
301 assert.Equal(t, "directory", rootNode.Type)
302 assert.Len(t, rootNode.Children, 1)
302 assert.Len(t, rootNode.Children, 1)
303−
303+
304 // Check the "to" node
304 // Check the "to" node
305 toNode := rootNode.Children[0]
305 toNode := rootNode.Children[0]
306 assert.Equal(t, "to", toNode.Name)
306 assert.Equal(t, "to", toNode.Name)
307 assert.Equal(t, "directory", toNode.Type)
307 assert.Equal(t, "directory", toNode.Type)
308 assert.Len(t, toNode.Children, 3) // file1.txt, dir1, dir2
308 assert.Len(t, toNode.Children, 3) // file1.txt, dir1, dir2
309−
309+
310 // Find the dir1 node
310 // Find the dir1 node
311 var dir1Node *TreeNode
311 var dir1Node *TreeNode
312 for _, child := range toNode.Children {
312 for _, child := range toNode.Children {
@@ -315,7 +315,7 @@ func TestCreateFileTree(t *testing.T) {
315 break
315 break
316 }
316 }
317 }
317 }
318−
318+
319 require.NotNil(t, dir1Node)
319 require.NotNil(t, dir1Node)
320 assert.Equal(t, "directory", dir1Node.Type)
320 assert.Equal(t, "directory", dir1Node.Type)
321 assert.Len(t, dir1Node.Children, 2) // file2.txt and subdir
321 assert.Len(t, dir1Node.Children, 2) // file2.txt and subdir
@@ -354,9 +354,9 @@ func TestPrintTree(t *testing.T) {
354 Type: "file",
354 Type: "file",
355 },
355 },
356 }
356 }
357−
357+
358 result := printTree(tree, "/root")
358 result := printTree(tree, "/root")
359−
359+
360 // Check the output format
360 // Check the output format
361 assert.Contains(t, result, "- /root/")
361 assert.Contains(t, result, "- /root/")
362 assert.Contains(t, result, " - dir1/")
362 assert.Contains(t, result, " - dir1/")
@@ -390,14 +390,14 @@ func TestListDirectory(t *testing.T) {
390 // Create directories
390 // Create directories
391 for _, dir := range testDirs {
391 for _, dir := range testDirs {
392 dirPath := filepath.Join(tempDir, dir)
392 dirPath := filepath.Join(tempDir, dir)
393− err := os.MkdirAll(dirPath, 0755)
393+ err := os.MkdirAll(dirPath, 0o755)
394 require.NoError(t, err)
394 require.NoError(t, err)
395 }
395 }
396
396
397 // Create files
397 // Create files
398 for _, file := range testFiles {
398 for _, file := range testFiles {
399 filePath := filepath.Join(tempDir, file)
399 filePath := filepath.Join(tempDir, file)
400− err := os.WriteFile(filePath, []byte("test content"), 0644)
400+ err := os.WriteFile(filePath, []byte("test content"), 0o644)
401 require.NoError(t, err)
401 require.NoError(t, err)
402 }
402 }
403
403
@@ -405,7 +405,7 @@ func TestListDirectory(t *testing.T) {
405 files, truncated, err := listDirectory(tempDir, []string{}, 1000)
405 files, truncated, err := listDirectory(tempDir, []string{}, 1000)
406 require.NoError(t, err)
406 require.NoError(t, err)
407 assert.False(t, truncated)
407 assert.False(t, truncated)
408−
408+
409 // Check that visible files and directories are included
409 // Check that visible files and directories are included
410 containsPath := func(paths []string, target string) bool {
410 containsPath := func(paths []string, target string) bool {
411 targetPath := filepath.Join(tempDir, target)
411 targetPath := filepath.Join(tempDir, target)
@@ -416,12 +416,12 @@ func TestListDirectory(t *testing.T) {
416 }
416 }
417 return false
417 return false
418 }
418 }
419−
419+
420 assert.True(t, containsPath(files, "dir1"))
420 assert.True(t, containsPath(files, "dir1"))
421 assert.True(t, containsPath(files, "file1.txt"))
421 assert.True(t, containsPath(files, "file1.txt"))
422 assert.True(t, containsPath(files, "file2.txt"))
422 assert.True(t, containsPath(files, "file2.txt"))
423 assert.True(t, containsPath(files, "dir1/file3.txt"))
423 assert.True(t, containsPath(files, "dir1/file3.txt"))
424−
424+
425 // Check that hidden files and directories are not included
425 // Check that hidden files and directories are not included
426 assert.False(t, containsPath(files, ".hidden_dir"))
426 assert.False(t, containsPath(files, ".hidden_dir"))
427 assert.False(t, containsPath(files, ".hidden_file.txt"))
427 assert.False(t, containsPath(files, ".hidden_file.txt"))
@@ -438,12 +438,12 @@ func TestListDirectory(t *testing.T) {
438 files, truncated, err := listDirectory(tempDir, []string{"*.txt"}, 1000)
438 files, truncated, err := listDirectory(tempDir, []string{"*.txt"}, 1000)
439 require.NoError(t, err)
439 require.NoError(t, err)
440 assert.False(t, truncated)
440 assert.False(t, truncated)
441−
441+
442 // Check that no .txt files are included
442 // Check that no .txt files are included
443 for _, file := range files {
443 for _, file := range files {
444 assert.False(t, strings.HasSuffix(file, ".txt"), "Found .txt file: %s", file)
444 assert.False(t, strings.HasSuffix(file, ".txt"), "Found .txt file: %s", file)
445 }
445 }
446−
446+
447 // But directories should still be included
447 // But directories should still be included
448 containsDir := false
448 containsDir := false
449 for _, file := range files {
449 for _, file := range files {
@@ -454,4 +454,4 @@ func TestListDirectory(t *testing.T) {
454 }
454 }
455 assert.True(t, containsDir)
455 assert.True(t, containsDir)
456 })
456 })
457−}
457+}
internal/llm/tools/shell/shell.go+4−4
@@ -61,23 +61,23 @@ func GetPersistentShell(workingDir string) *PersistentShell {
61func newPersistentShell(cwd string) *PersistentShell {
61func newPersistentShell(cwd string) *PersistentShell {
62 // Get shell configuration from config
62 // Get shell configuration from config
63 cfg := config.Get()
63 cfg := config.Get()
64−
64+
65 // Default to environment variable if config is not set or nil
65 // Default to environment variable if config is not set or nil
66 var shellPath string
66 var shellPath string
67 var shellArgs []string
67 var shellArgs []string
68−
68+
69 if cfg != nil {
69 if cfg != nil {
70 shellPath = cfg.Shell.Path
70 shellPath = cfg.Shell.Path
71 shellArgs = cfg.Shell.Args
71 shellArgs = cfg.Shell.Args
72 }
72 }
73−
73+
74 if shellPath == "" {
74 if shellPath == "" {
75 shellPath = os.Getenv("SHELL")
75 shellPath = os.Getenv("SHELL")
76 if shellPath == "" {
76 if shellPath == "" {
77 shellPath = "/bin/bash"
77 shellPath = "/bin/bash"
78 }
78 }
79 }
79 }
80−
80+
81 // Default shell args
81 // Default shell args
82 if len(shellArgs) == 0 {
82 if len(shellArgs) == 0 {
83 shellArgs = []string{"-l"}
83 shellArgs = []string{"-l"}
internal/tui/components/dialog/custom_commands.go+1−2
@@ -82,7 +82,7 @@ func loadCommandsFromDir(commandsDir string, prefix string) ([]Command, error) {
82 // Check if the commands directory exists
82 // Check if the commands directory exists
83 if _, err := os.Stat(commandsDir); os.IsNotExist(err) {
83 if _, err := os.Stat(commandsDir); os.IsNotExist(err) {
84 // Create the commands directory if it doesn't exist
84 // Create the commands directory if it doesn't exist
85− if err := os.MkdirAll(commandsDir, 0755); err != nil {
85+ if err := os.MkdirAll(commandsDir, 0o755); err != nil {
86 return nil, fmt.Errorf("failed to create commands directory %s: %w", commandsDir, err)
86 return nil, fmt.Errorf("failed to create commands directory %s: %w", commandsDir, err)
87 }
87 }
88 // Return empty list since we just created the directory
88 // Return empty list since we just created the directory
@@ -171,7 +171,6 @@ func loadCommandsFromDir(commandsDir string, prefix string) ([]Command, error) {
171 commands = append(commands, command)
171 commands = append(commands, command)
172 return nil
172 return nil
173 })
173 })
174−
175 if err != nil {
174 if err != nil {
176 return nil, fmt.Errorf("failed to load custom commands from %s: %w", commandsDir, err)
175 return nil, fmt.Errorf("failed to load custom commands from %s: %w", commandsDir, err)
177 }
176 }
internal/tui/components/dialog/custom_commands_test.go+10−10
@@ -1,8 +1,8 @@
1package dialog
1package dialog
2
2
3import (
3import (
4− "testing"
5 "regexp"
4 "regexp"
5+ "testing"
6)
6)
7
7
8func TestNamedArgPattern(t *testing.T) {
8func TestNamedArgPattern(t *testing.T) {
@@ -38,11 +38,11 @@ func TestNamedArgPattern(t *testing.T) {
38
38
39 for _, tc := range testCases {
39 for _, tc := range testCases {
40 matches := namedArgPattern.FindAllStringSubmatch(tc.input, -1)
40 matches := namedArgPattern.FindAllStringSubmatch(tc.input, -1)
41−
41+
42 // Extract unique argument names
42 // Extract unique argument names
43 argNames := make([]string, 0)
43 argNames := make([]string, 0)
44 argMap := make(map[string]bool)
44 argMap := make(map[string]bool)
45−
45+
46 for _, match := range matches {
46 for _, match := range matches {
47 argName := match[1] // Group 1 is the name without $
47 argName := match[1] // Group 1 is the name without $
48 if !argMap[argName] {
48 if !argMap[argName] {
@@ -50,13 +50,13 @@ func TestNamedArgPattern(t *testing.T) {
50 argNames = append(argNames, argName)
50 argNames = append(argNames, argName)
51 }
51 }
52 }
52 }
53−
53+
54 // Check if we got the expected number of arguments
54 // Check if we got the expected number of arguments
55 if len(argNames) != len(tc.expected) {
55 if len(argNames) != len(tc.expected) {
56 t.Errorf("Expected %d arguments, got %d for input: %s", len(tc.expected), len(argNames), tc.input)
56 t.Errorf("Expected %d arguments, got %d for input: %s", len(tc.expected), len(argNames), tc.input)
57 continue
57 continue
58 }
58 }
59−
59+
60 // Check if we got the expected argument names
60 // Check if we got the expected argument names
61 for _, expectedArg := range tc.expected {
61 for _, expectedArg := range tc.expected {
62 found := false
62 found := false
@@ -75,7 +75,7 @@ func TestNamedArgPattern(t *testing.T) {
75
75
76func TestRegexPattern(t *testing.T) {
76func TestRegexPattern(t *testing.T) {
77 pattern := regexp.MustCompile(`\$([A-Z][A-Z0-9_]*)`)
77 pattern := regexp.MustCompile(`\$([A-Z][A-Z0-9_]*)`)
78−
78+
79 validMatches := []string{
79 validMatches := []string{
80 "$FOO",
80 "$FOO",
81 "$BAR",
81 "$BAR",
@@ -83,7 +83,7 @@ func TestRegexPattern(t *testing.T) {
83 "$BAZ123",
83 "$BAZ123",
84 "$ARGUMENTS",
84 "$ARGUMENTS",
85 }
85 }
86−
86+
87 invalidMatches := []string{
87 invalidMatches := []string{
88 "$foo",
88 "$foo",
89 "$1BAR",
89 "$1BAR",
@@ -91,16 +91,16 @@ func TestRegexPattern(t *testing.T) {
91 "FOO",
91 "FOO",
92 "$",
92 "$",
93 }
93 }
94−
94+
95 for _, valid := range validMatches {
95 for _, valid := range validMatches {
96 if !pattern.MatchString(valid) {
96 if !pattern.MatchString(valid) {
97 t.Errorf("Expected %s to match, but it didn't", valid)
97 t.Errorf("Expected %s to match, but it didn't", valid)
98 }
98 }
99 }
99 }
100−
100+
101 for _, invalid := range invalidMatches {
101 for _, invalid := range invalidMatches {
102 if pattern.MatchString(invalid) {
102 if pattern.MatchString(invalid) {
103 t.Errorf("Expected %s not to match, but it did", invalid)
103 t.Errorf("Expected %s not to match, but it did", invalid)
104 }
104 }
105 }
105 }
106−}
106+}
internal/tui/theme/dracula.go+1−1
@@ -103,4 +103,4 @@ func NewDraculaTheme() *DraculaTheme {
103func init() {
103func init() {
104 // Register the Dracula theme with the theme manager
104 // Register the Dracula theme with the theme manager
105 RegisterTheme("dracula", NewDraculaTheme())
105 RegisterTheme("dracula", NewDraculaTheme())
106−}
106+}
internal/tui/theme/flexoki.go+35−35
@@ -7,20 +7,20 @@ import (
7// Flexoki color palette constants
7// Flexoki color palette constants
8const (
8const (
9 // Base colors
9 // Base colors
10− flexokiPaper = "#FFFCF0" // Paper (lightest)
10+ flexokiPaper = "#FFFCF0" // Paper (lightest)
11− flexokiBase50 = "#F2F0E5" // bg-2 (light)
11+ flexokiBase50 = "#F2F0E5" // bg-2 (light)
12− flexokiBase100 = "#E6E4D9" // ui (light)
12+ flexokiBase100 = "#E6E4D9" // ui (light)
13− flexokiBase150 = "#DAD8CE" // ui-2 (light)
13+ flexokiBase150 = "#DAD8CE" // ui-2 (light)
14− flexokiBase200 = "#CECDC3" // ui-3 (light)
14+ flexokiBase200 = "#CECDC3" // ui-3 (light)
15− flexokiBase300 = "#B7B5AC" // tx-3 (light)
15+ flexokiBase300 = "#B7B5AC" // tx-3 (light)
16− flexokiBase500 = "#878580" // tx-2 (light)
16+ flexokiBase500 = "#878580" // tx-2 (light)
17− flexokiBase600 = "#6F6E69" // tx (light)
17+ flexokiBase600 = "#6F6E69" // tx (light)
18− flexokiBase700 = "#575653" // tx-3 (dark)
18+ flexokiBase700 = "#575653" // tx-3 (dark)
19− flexokiBase800 = "#403E3C" // ui-3 (dark)
19+ flexokiBase800 = "#403E3C" // ui-3 (dark)
20− flexokiBase850 = "#343331" // ui-2 (dark)
20+ flexokiBase850 = "#343331" // ui-2 (dark)
21− flexokiBase900 = "#282726" // ui (dark)
21+ flexokiBase900 = "#282726" // ui (dark)
22− flexokiBase950 = "#1C1B1A" // bg-2 (dark)
22+ flexokiBase950 = "#1C1B1A" // bg-2 (dark)
23− flexokiBlack = "#100F0F" // bg (darkest)
23+ flexokiBlack = "#100F0F" // bg (darkest)
24
24
25 // Accent colors - Light theme (600)
25 // Accent colors - Light theme (600)
26 flexokiRed600 = "#AF3029"
26 flexokiRed600 = "#AF3029"
@@ -86,11 +86,11 @@ func NewFlexokiDarkTheme() *FlexokiTheme {
86 theme.DiffHunkHeaderColor = lipgloss.Color(flexokiBase700)
86 theme.DiffHunkHeaderColor = lipgloss.Color(flexokiBase700)
87 theme.DiffHighlightAddedColor = lipgloss.Color(flexokiGreen400)
87 theme.DiffHighlightAddedColor = lipgloss.Color(flexokiGreen400)
88 theme.DiffHighlightRemovedColor = lipgloss.Color(flexokiRed400)
88 theme.DiffHighlightRemovedColor = lipgloss.Color(flexokiRed400)
89− theme.DiffAddedBgColor = lipgloss.Color("#1D2419") // Darker green background
89+ theme.DiffAddedBgColor = lipgloss.Color("#1D2419") // Darker green background
90 theme.DiffRemovedBgColor = lipgloss.Color("#241919") // Darker red background
90 theme.DiffRemovedBgColor = lipgloss.Color("#241919") // Darker red background
91 theme.DiffContextBgColor = lipgloss.Color(flexokiBlack)
91 theme.DiffContextBgColor = lipgloss.Color(flexokiBlack)
92 theme.DiffLineNumberColor = lipgloss.Color(flexokiBase700)
92 theme.DiffLineNumberColor = lipgloss.Color(flexokiBase700)
93− theme.DiffAddedLineNumberBgColor = lipgloss.Color("#1A2017") // Slightly darker green
93+ theme.DiffAddedLineNumberBgColor = lipgloss.Color("#1A2017") // Slightly darker green
94 theme.DiffRemovedLineNumberBgColor = lipgloss.Color("#201717") // Slightly darker red
94 theme.DiffRemovedLineNumberBgColor = lipgloss.Color("#201717") // Slightly darker red
95
95
96 // Markdown colors
96 // Markdown colors
@@ -110,14 +110,14 @@ func NewFlexokiDarkTheme() *FlexokiTheme {
110 theme.MarkdownCodeBlockColor = lipgloss.Color(flexokiBase300)
110 theme.MarkdownCodeBlockColor = lipgloss.Color(flexokiBase300)
111
111
112 // Syntax highlighting colors (based on Flexoki's mappings)
112 // Syntax highlighting colors (based on Flexoki's mappings)
113− theme.SyntaxCommentColor = lipgloss.Color(flexokiBase700) // tx-3
113+ theme.SyntaxCommentColor = lipgloss.Color(flexokiBase700) // tx-3
114− theme.SyntaxKeywordColor = lipgloss.Color(flexokiGreen400) // gr
114+ theme.SyntaxKeywordColor = lipgloss.Color(flexokiGreen400) // gr
115− theme.SyntaxFunctionColor = lipgloss.Color(flexokiOrange400) // or
115+ theme.SyntaxFunctionColor = lipgloss.Color(flexokiOrange400) // or
116− theme.SyntaxVariableColor = lipgloss.Color(flexokiBlue400) // bl
116+ theme.SyntaxVariableColor = lipgloss.Color(flexokiBlue400) // bl
117− theme.SyntaxStringColor = lipgloss.Color(flexokiCyan400) // cy
117+ theme.SyntaxStringColor = lipgloss.Color(flexokiCyan400) // cy
118− theme.SyntaxNumberColor = lipgloss.Color(flexokiPurple400) // pu
118+ theme.SyntaxNumberColor = lipgloss.Color(flexokiPurple400) // pu
119− theme.SyntaxTypeColor = lipgloss.Color(flexokiYellow400) // ye
119+ theme.SyntaxTypeColor = lipgloss.Color(flexokiYellow400) // ye
120− theme.SyntaxOperatorColor = lipgloss.Color(flexokiBase500) // tx-2
120+ theme.SyntaxOperatorColor = lipgloss.Color(flexokiBase500) // tx-2
121 theme.SyntaxPunctuationColor = lipgloss.Color(flexokiBase500) // tx-2
121 theme.SyntaxPunctuationColor = lipgloss.Color(flexokiBase500) // tx-2
122
122
123 return theme
123 return theme
@@ -160,11 +160,11 @@ func NewFlexokiLightTheme() *FlexokiTheme {
160 theme.DiffHunkHeaderColor = lipgloss.Color(flexokiBase500)
160 theme.DiffHunkHeaderColor = lipgloss.Color(flexokiBase500)
161 theme.DiffHighlightAddedColor = lipgloss.Color(flexokiGreen600)
161 theme.DiffHighlightAddedColor = lipgloss.Color(flexokiGreen600)
162 theme.DiffHighlightRemovedColor = lipgloss.Color(flexokiRed600)
162 theme.DiffHighlightRemovedColor = lipgloss.Color(flexokiRed600)
163− theme.DiffAddedBgColor = lipgloss.Color("#EFF2E2") // Light green background
163+ theme.DiffAddedBgColor = lipgloss.Color("#EFF2E2") // Light green background
164 theme.DiffRemovedBgColor = lipgloss.Color("#F2E2E2") // Light red background
164 theme.DiffRemovedBgColor = lipgloss.Color("#F2E2E2") // Light red background
165 theme.DiffContextBgColor = lipgloss.Color(flexokiPaper)
165 theme.DiffContextBgColor = lipgloss.Color(flexokiPaper)
166 theme.DiffLineNumberColor = lipgloss.Color(flexokiBase500)
166 theme.DiffLineNumberColor = lipgloss.Color(flexokiBase500)
167− theme.DiffAddedLineNumberBgColor = lipgloss.Color("#E5EBD9") // Light green
167+ theme.DiffAddedLineNumberBgColor = lipgloss.Color("#E5EBD9") // Light green
168 theme.DiffRemovedLineNumberBgColor = lipgloss.Color("#EBD9D9") // Light red
168 theme.DiffRemovedLineNumberBgColor = lipgloss.Color("#EBD9D9") // Light red
169
169
170 // Markdown colors
170 // Markdown colors
@@ -184,14 +184,14 @@ func NewFlexokiLightTheme() *FlexokiTheme {
184 theme.MarkdownCodeBlockColor = lipgloss.Color(flexokiBase600)
184 theme.MarkdownCodeBlockColor = lipgloss.Color(flexokiBase600)
185
185
186 // Syntax highlighting colors (based on Flexoki's mappings)
186 // Syntax highlighting colors (based on Flexoki's mappings)
187− theme.SyntaxCommentColor = lipgloss.Color(flexokiBase300) // tx-3
187+ theme.SyntaxCommentColor = lipgloss.Color(flexokiBase300) // tx-3
188− theme.SyntaxKeywordColor = lipgloss.Color(flexokiGreen600) // gr
188+ theme.SyntaxKeywordColor = lipgloss.Color(flexokiGreen600) // gr
189− theme.SyntaxFunctionColor = lipgloss.Color(flexokiOrange600) // or
189+ theme.SyntaxFunctionColor = lipgloss.Color(flexokiOrange600) // or
190− theme.SyntaxVariableColor = lipgloss.Color(flexokiBlue600) // bl
190+ theme.SyntaxVariableColor = lipgloss.Color(flexokiBlue600) // bl
191− theme.SyntaxStringColor = lipgloss.Color(flexokiCyan600) // cy
191+ theme.SyntaxStringColor = lipgloss.Color(flexokiCyan600) // cy
192− theme.SyntaxNumberColor = lipgloss.Color(flexokiPurple600) // pu
192+ theme.SyntaxNumberColor = lipgloss.Color(flexokiPurple600) // pu
193− theme.SyntaxTypeColor = lipgloss.Color(flexokiYellow600) // ye
193+ theme.SyntaxTypeColor = lipgloss.Color(flexokiYellow600) // ye
194− theme.SyntaxOperatorColor = lipgloss.Color(flexokiBase500) // tx-2
194+ theme.SyntaxOperatorColor = lipgloss.Color(flexokiBase500) // tx-2
195 theme.SyntaxPunctuationColor = lipgloss.Color(flexokiBase500) // tx-2
195 theme.SyntaxPunctuationColor = lipgloss.Color(flexokiBase500) // tx-2
196
196
197 return theme
197 return theme
@@ -201,4 +201,4 @@ func init() {
201 // Register the Flexoki themes with the theme manager
201 // Register the Flexoki themes with the theme manager
202 RegisterTheme("flexoki-dark", NewFlexokiDarkTheme())
202 RegisterTheme("flexoki-dark", NewFlexokiDarkTheme())
203 RegisterTheme("flexoki-light", NewFlexokiLightTheme())
203 RegisterTheme("flexoki-light", NewFlexokiLightTheme())
204−}
204+}
internal/tui/theme/gruvbox.go+6−6
@@ -106,12 +106,12 @@ func NewGruvboxTheme() *GruvboxTheme {
106 theme.DiffHunkHeaderColor = lipgloss.Color(gruvboxDarkFg3)
106 theme.DiffHunkHeaderColor = lipgloss.Color(gruvboxDarkFg3)
107 theme.DiffHighlightAddedColor = lipgloss.Color(gruvboxDarkGreenBright)
107 theme.DiffHighlightAddedColor = lipgloss.Color(gruvboxDarkGreenBright)
108 theme.DiffHighlightRemovedColor = lipgloss.Color(gruvboxDarkRedBright)
108 theme.DiffHighlightRemovedColor = lipgloss.Color(gruvboxDarkRedBright)
109− theme.DiffAddedBgColor = lipgloss.Color("#3C4C3C") // Darker green background
109+ theme.DiffAddedBgColor = lipgloss.Color("#3C4C3C") // Darker green background
110− theme.DiffRemovedBgColor = lipgloss.Color("#4C3C3C") // Darker red background
110+ theme.DiffRemovedBgColor = lipgloss.Color("#4C3C3C") // Darker red background
111 theme.DiffContextBgColor = lipgloss.Color(gruvboxDarkBg0)
111 theme.DiffContextBgColor = lipgloss.Color(gruvboxDarkBg0)
112 theme.DiffLineNumberColor = lipgloss.Color(gruvboxDarkFg4)
112 theme.DiffLineNumberColor = lipgloss.Color(gruvboxDarkFg4)
113 theme.DiffAddedLineNumberBgColor = lipgloss.Color("#32432F") // Slightly darker green
113 theme.DiffAddedLineNumberBgColor = lipgloss.Color("#32432F") // Slightly darker green
114− theme.DiffRemovedLineNumberBgColor = lipgloss.Color("#43322F") // Slightly darker red
114+ theme.DiffRemovedLineNumberBgColor = lipgloss.Color("#43322F") // Slightly darker red
115
115
116 // Markdown colors
116 // Markdown colors
117 theme.MarkdownTextColor = lipgloss.Color(gruvboxDarkFg1)
117 theme.MarkdownTextColor = lipgloss.Color(gruvboxDarkFg1)
@@ -180,11 +180,11 @@ func NewGruvboxLightTheme() *GruvboxTheme {
180 theme.DiffHunkHeaderColor = lipgloss.Color(gruvboxLightFg3)
180 theme.DiffHunkHeaderColor = lipgloss.Color(gruvboxLightFg3)
181 theme.DiffHighlightAddedColor = lipgloss.Color(gruvboxLightGreenBright)
181 theme.DiffHighlightAddedColor = lipgloss.Color(gruvboxLightGreenBright)
182 theme.DiffHighlightRemovedColor = lipgloss.Color(gruvboxLightRedBright)
182 theme.DiffHighlightRemovedColor = lipgloss.Color(gruvboxLightRedBright)
183− theme.DiffAddedBgColor = lipgloss.Color("#E8F5E9") // Light green background
183+ theme.DiffAddedBgColor = lipgloss.Color("#E8F5E9") // Light green background
184 theme.DiffRemovedBgColor = lipgloss.Color("#FFEBEE") // Light red background
184 theme.DiffRemovedBgColor = lipgloss.Color("#FFEBEE") // Light red background
185 theme.DiffContextBgColor = lipgloss.Color(gruvboxLightBg0)
185 theme.DiffContextBgColor = lipgloss.Color(gruvboxLightBg0)
186 theme.DiffLineNumberColor = lipgloss.Color(gruvboxLightFg4)
186 theme.DiffLineNumberColor = lipgloss.Color(gruvboxLightFg4)
187− theme.DiffAddedLineNumberBgColor = lipgloss.Color("#C8E6C9") // Light green
187+ theme.DiffAddedLineNumberBgColor = lipgloss.Color("#C8E6C9") // Light green
188 theme.DiffRemovedLineNumberBgColor = lipgloss.Color("#FFCDD2") // Light red
188 theme.DiffRemovedLineNumberBgColor = lipgloss.Color("#FFCDD2") // Light red
189
189
190 // Markdown colors
190 // Markdown colors
@@ -221,4 +221,4 @@ func init() {
221 // Register the Gruvbox themes with the theme manager
221 // Register the Gruvbox themes with the theme manager
222 RegisterTheme("gruvbox", NewGruvboxTheme())
222 RegisterTheme("gruvbox", NewGruvboxTheme())
223 RegisterTheme("gruvbox-light", NewGruvboxLightTheme())
223 RegisterTheme("gruvbox-light", NewGruvboxLightTheme())
224−}
224+}
internal/tui/theme/monokai.go+1−1
@@ -192,4 +192,4 @@ func init() {
192 // Register the Monokai Pro themes with the theme manager
192 // Register the Monokai Pro themes with the theme manager
193 RegisterTheme("monokai", NewMonokaiProTheme())
193 RegisterTheme("monokai", NewMonokaiProTheme())
194 RegisterTheme("monokai-light", NewMonokaiProLightTheme())
194 RegisterTheme("monokai-light", NewMonokaiProLightTheme())
195−}
195+}
internal/tui/theme/onedark.go+1−1
@@ -193,4 +193,4 @@ func init() {
193 // Register the One Dark and One Light themes with the theme manager
193 // Register the One Dark and One Light themes with the theme manager
194 RegisterTheme("onedark", NewOneDarkTheme())
194 RegisterTheme("onedark", NewOneDarkTheme())
195 RegisterTheme("onelight", NewOneLightTheme())
195 RegisterTheme("onelight", NewOneLightTheme())
196−}
196+}
internal/tui/theme/theme_test.go+16−16
@@ -7,7 +7,7 @@ import (
7func TestThemeRegistration(t *testing.T) {
7func TestThemeRegistration(t *testing.T) {
8 // Get list of available themes
8 // Get list of available themes
9 availableThemes := AvailableThemes()
9 availableThemes := AvailableThemes()
10−
10+
11 // Check if "catppuccin" theme is registered
11 // Check if "catppuccin" theme is registered
12 catppuccinFound := false
12 catppuccinFound := false
13 for _, themeName := range availableThemes {
13 for _, themeName := range availableThemes {
@@ -16,11 +16,11 @@ func TestThemeRegistration(t *testing.T) {
16 break
16 break
17 }
17 }
18 }
18 }
19−
19+
20 if !catppuccinFound {
20 if !catppuccinFound {
21 t.Errorf("Catppuccin theme is not registered")
21 t.Errorf("Catppuccin theme is not registered")
22 }
22 }
23−
23+
24 // Check if "gruvbox" theme is registered
24 // Check if "gruvbox" theme is registered
25 gruvboxFound := false
25 gruvboxFound := false
26 for _, themeName := range availableThemes {
26 for _, themeName := range availableThemes {
@@ -29,11 +29,11 @@ func TestThemeRegistration(t *testing.T) {
29 break
29 break
30 }
30 }
31 }
31 }
32−
32+
33 if !gruvboxFound {
33 if !gruvboxFound {
34 t.Errorf("Gruvbox theme is not registered")
34 t.Errorf("Gruvbox theme is not registered")
35 }
35 }
36−
36+
37 // Check if "monokai" theme is registered
37 // Check if "monokai" theme is registered
38 monokaiFound := false
38 monokaiFound := false
39 for _, themeName := range availableThemes {
39 for _, themeName := range availableThemes {
@@ -42,48 +42,48 @@ func TestThemeRegistration(t *testing.T) {
42 break
42 break
43 }
43 }
44 }
44 }
45−
45+
46 if !monokaiFound {
46 if !monokaiFound {
47 t.Errorf("Monokai theme is not registered")
47 t.Errorf("Monokai theme is not registered")
48 }
48 }
49−
49+
50 // Try to get the themes and make sure they're not nil
50 // Try to get the themes and make sure they're not nil
51 catppuccin := GetTheme("catppuccin")
51 catppuccin := GetTheme("catppuccin")
52 if catppuccin == nil {
52 if catppuccin == nil {
53 t.Errorf("Catppuccin theme is nil")
53 t.Errorf("Catppuccin theme is nil")
54 }
54 }
55−
55+
56 gruvbox := GetTheme("gruvbox")
56 gruvbox := GetTheme("gruvbox")
57 if gruvbox == nil {
57 if gruvbox == nil {
58 t.Errorf("Gruvbox theme is nil")
58 t.Errorf("Gruvbox theme is nil")
59 }
59 }
60−
60+
61 monokai := GetTheme("monokai")
61 monokai := GetTheme("monokai")
62 if monokai == nil {
62 if monokai == nil {
63 t.Errorf("Monokai theme is nil")
63 t.Errorf("Monokai theme is nil")
64 }
64 }
65−
65+
66 // Test switching theme
66 // Test switching theme
67 originalTheme := CurrentThemeName()
67 originalTheme := CurrentThemeName()
68−
68+
69 err := SetTheme("gruvbox")
69 err := SetTheme("gruvbox")
70 if err != nil {
70 if err != nil {
71 t.Errorf("Failed to set theme to gruvbox: %v", err)
71 t.Errorf("Failed to set theme to gruvbox: %v", err)
72 }
72 }
73−
73+
74 if CurrentThemeName() != "gruvbox" {
74 if CurrentThemeName() != "gruvbox" {
75 t.Errorf("Theme not properly switched to gruvbox")
75 t.Errorf("Theme not properly switched to gruvbox")
76 }
76 }
77−
77+
78 err = SetTheme("monokai")
78 err = SetTheme("monokai")
79 if err != nil {
79 if err != nil {
80 t.Errorf("Failed to set theme to monokai: %v", err)
80 t.Errorf("Failed to set theme to monokai: %v", err)
81 }
81 }
82−
82+
83 if CurrentThemeName() != "monokai" {
83 if CurrentThemeName() != "monokai" {
84 t.Errorf("Theme not properly switched to monokai")
84 t.Errorf("Theme not properly switched to monokai")
85 }
85 }
86−
86+
87 // Switch back to original theme
87 // Switch back to original theme
88 _ = SetTheme(originalTheme)
88 _ = SetTheme(originalTheme)
89−}
89+}
internal/tui/theme/tokyonight.go+6−6
@@ -57,11 +57,11 @@ func NewTokyoNightTheme() *TokyoNightTheme {
57 theme.BorderDimColor = lipgloss.Color(darkSelection)
57 theme.BorderDimColor = lipgloss.Color(darkSelection)
58
58
59 // Diff view colors
59 // Diff view colors
60− theme.DiffAddedColor = lipgloss.Color("#4fd6be") // teal from palette
60+ theme.DiffAddedColor = lipgloss.Color("#4fd6be") // teal from palette
61− theme.DiffRemovedColor = lipgloss.Color("#c53b53") // red1 from palette
61+ theme.DiffRemovedColor = lipgloss.Color("#c53b53") // red1 from palette
62− theme.DiffContextColor = lipgloss.Color("#828bb8") // fg_dark from palette
62+ theme.DiffContextColor = lipgloss.Color("#828bb8") // fg_dark from palette
63− theme.DiffHunkHeaderColor = lipgloss.Color("#828bb8") // fg_dark from palette
63+ theme.DiffHunkHeaderColor = lipgloss.Color("#828bb8") // fg_dark from palette
64− theme.DiffHighlightAddedColor = lipgloss.Color("#b8db87") // git.add from palette
64+ theme.DiffHighlightAddedColor = lipgloss.Color("#b8db87") // git.add from palette
65 theme.DiffHighlightRemovedColor = lipgloss.Color("#e26a75") // git.delete from palette
65 theme.DiffHighlightRemovedColor = lipgloss.Color("#e26a75") // git.delete from palette
66 theme.DiffAddedBgColor = lipgloss.Color("#20303b")
66 theme.DiffAddedBgColor = lipgloss.Color("#20303b")
67 theme.DiffRemovedBgColor = lipgloss.Color("#37222c")
67 theme.DiffRemovedBgColor = lipgloss.Color("#37222c")
@@ -193,4 +193,4 @@ func init() {
193 // Register the Tokyo Night themes with the theme manager
193 // Register the Tokyo Night themes with the theme manager
194 RegisterTheme("tokyonight", NewTokyoNightTheme())
194 RegisterTheme("tokyonight", NewTokyoNightTheme())
195 RegisterTheme("tokyonight-day", NewTokyoNightDayTheme())
195 RegisterTheme("tokyonight-day", NewTokyoNightDayTheme())
196−}
196+}
internal/tui/theme/tron.go+1−1
@@ -195,4 +195,4 @@ func init() {
195 // Register the Tron themes with the theme manager
195 // Register the Tron themes with the theme manager
196 RegisterTheme("tron", NewTronTheme())
196 RegisterTheme("tron", NewTronTheme())
197 RegisterTheme("tron-light", NewTronLightTheme())
197 RegisterTheme("tron-light", NewTronLightTheme())
198−}
198+}