wip: refactoring tui

This commit is contained in:
adamdottv
2025-06-12 05:35:40 -05:00
parent a1ce35c208
commit cce2e4ad75
47 changed files with 1018 additions and 3406 deletions

View File

@@ -9,7 +9,7 @@ import (
"sync" "sync"
"time" "time"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
zone "github.com/lrstanley/bubblezone" zone "github.com/lrstanley/bubblezone"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/pubsub" "github.com/sst/opencode/internal/pubsub"
@@ -65,6 +65,8 @@ func main() {
zone.NewGlobal() zone.NewGlobal()
program := tea.NewProgram( program := tea.NewProgram(
tui.NewModel(app_), tui.NewModel(app_),
// tea.WithMouseCellMotion(),
tea.WithKeyboardEnhancements(),
tea.WithAltScreen(), tea.WithAltScreen(),
) )

View File

@@ -4,13 +4,12 @@ go 1.24.0
require ( require (
github.com/BurntSushi/toml v1.5.0 github.com/BurntSushi/toml v1.5.0
github.com/alecthomas/chroma/v2 v2.15.0 github.com/alecthomas/chroma/v2 v2.18.0
github.com/bmatcuk/doublestar/v4 v4.8.1 github.com/bmatcuk/doublestar/v4 v4.8.1
github.com/catppuccin/go v0.3.0 github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1
github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3
github.com/charmbracelet/bubbletea v1.3.4 github.com/charmbracelet/glamour v0.10.0
github.com/charmbracelet/glamour v0.9.1 github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/x/ansi v0.8.0 github.com/charmbracelet/x/ansi v0.8.0
github.com/lithammer/fuzzysearch v1.1.8 github.com/lithammer/fuzzysearch v1.1.8
github.com/lrstanley/bubblezone v0.0.0-20250315020633-c249a3fe1231 github.com/lrstanley/bubblezone v0.0.0-20250315020633-c249a3fe1231
@@ -28,6 +27,11 @@ require (
dario.cat/mergo v1.0.2 // indirect dario.cat/mergo v1.0.2 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/atombender/go-jsonschema v0.20.0 // indirect github.com/atombender/go-jsonschema v0.20.0 // indirect
github.com/charmbracelet/bubbletea v1.3.4 // indirect
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
github.com/charmbracelet/x/input v0.3.5-0.20250424101541-abb4d9a9b197 // indirect
github.com/charmbracelet/x/windows v0.2.1 // indirect
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/getkin/kin-openapi v0.127.0 // indirect github.com/getkin/kin-openapi v0.127.0 // indirect
@@ -57,11 +61,11 @@ require (
github.com/atotto/clipboard v0.1.4 github.com/atotto/clipboard v0.1.4
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/colorprofile v0.3.1 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/cellbuf v0.0.14-0.20250501183327-ad3bc78c6a81 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
github.com/dlclark/regexp2 v1.11.4 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect
github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/css v1.0.1 // indirect

View File

@@ -7,8 +7,8 @@ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc= github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4=
github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
@@ -26,26 +26,34 @@ github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd3
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38= github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1 h1:swACzss0FjnyPz1enfX56GKkLiuKg5FlyVmOLIlU2kE=
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3 h1:5A2e3myxXMpCES+kjEWgGsaf9VgZXjZbLi5iMTH7j40=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3/go.mod h1:ZFDg5oPjyRYrPAa3iFrtP1DO8xy+LUQxd9JFHEcuwJY=
github.com/charmbracelet/glamour v0.9.1 h1:11dEfiGP8q1BEqvGoIjivuc2rBk+5qEXdPtaQ2WoiCM= github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
github.com/charmbracelet/glamour v0.9.1/go.mod h1:+SHvIS8qnwhgTpVMiXwn7OfGomSqff1cHBCI8jLOetk= github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk=
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1 h1:SOylT6+BQzPHEjn15TIzawBPVD0QmhKXbcb3jY0ZIKU=
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1/go.mod h1:tRlx/Hu0lo/j9viunCN2H+Ze6JrmdjQlXUQvvArgaOc=
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= github.com/charmbracelet/x/cellbuf v0.0.14-0.20250501183327-ad3bc78c6a81 h1:iGrflaL5jQW6crML+pZx/ulWAVZQR3CQoRGvFsr2Tyg=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/cellbuf v0.0.14-0.20250501183327-ad3bc78c6a81/go.mod h1:poPFOXFTsJsnLbkV3H2KxAAXT7pdjxxLujLocWjkyzM=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a h1:FsHEJ52OC4VuTzU8t+n5frMjLvpYWEznSr/u8tnkCYw=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
github.com/charmbracelet/x/input v0.3.5-0.20250424101541-abb4d9a9b197 h1:fsWj8NF5njyMVzELc7++HsvRDvgz3VcgGAUgWBDWWWM=
github.com/charmbracelet/x/input v0.3.5-0.20250424101541-abb4d9a9b197/go.mod h1:xseGeVftoP9rVI+/8WKYrJFH6ior6iERGvklwwHz5+s=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/charmbracelet/x/windows v0.2.1 h1:3x7vnbpQrjpuq/4L+I4gNsG5htYoCiA5oe9hLjAij5I=
github.com/charmbracelet/x/windows v0.2.1/go.mod h1:ptZp16h40gDYqs5TSawSVW+yiLB13j4kSMA0lSCHL0M=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -56,8 +64,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58=
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w= github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w=
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q=

View File

@@ -8,7 +8,7 @@ import (
"log/slog" "log/slog"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/sst/opencode/internal/config" "github.com/sst/opencode/internal/config"
"github.com/sst/opencode/internal/fileutil" "github.com/sst/opencode/internal/fileutil"
"github.com/sst/opencode/internal/state" "github.com/sst/opencode/internal/state"

View File

@@ -5,15 +5,13 @@ import (
"log/slog" "log/slog"
"os" "os"
"os/exec" "os/exec"
"slices"
"strings" "strings"
"unicode"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
"github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/v2/spinner"
"github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/v2/textarea"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/components/dialog" "github.com/sst/opencode/internal/components/dialog"
"github.com/sst/opencode/internal/image" "github.com/sst/opencode/internal/image"
@@ -129,18 +127,18 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.attachments = nil m.attachments = nil
return m, nil return m, nil
} }
if m.deleteMode && len(msg.Runes) > 0 && unicode.IsDigit(msg.Runes[0]) { // if m.deleteMode && len(msg.Runes) > 0 && unicode.IsDigit(msg.Runes[0]) {
num := int(msg.Runes[0] - '0') // num := int(msg.Runes[0] - '0')
m.deleteMode = false // m.deleteMode = false
if num < 10 && len(m.attachments) > num { // if num < 10 && len(m.attachments) > num {
if num == 0 { // if num == 0 {
m.attachments = m.attachments[num+1:] // m.attachments = m.attachments[num+1:]
} else { // } else {
m.attachments = slices.Delete(m.attachments, num, num+1) // m.attachments = slices.Delete(m.attachments, num, num+1)
} // }
return m, nil // return m, nil
} // }
} // }
if key.Matches(msg, messageKeys.PageUp) || key.Matches(msg, messageKeys.PageDown) || if key.Matches(msg, messageKeys.PageUp) || key.Matches(msg, messageKeys.PageDown) ||
key.Matches(msg, messageKeys.HalfPageUp) || key.Matches(msg, messageKeys.HalfPageDown) { key.Matches(msg, messageKeys.HalfPageUp) || key.Matches(msg, messageKeys.HalfPageDown) {
return m, nil return m, nil
@@ -258,7 +256,7 @@ func (m *editorComponent) View() string {
m.textarea.View(), m.textarea.View(),
) )
textarea = styles.BaseStyle(). textarea = styles.BaseStyle().
Width(m.width-2). Width(m.width). // -2).
Border(lipgloss.NormalBorder(), true, true). Border(lipgloss.NormalBorder(), true, true).
BorderForeground(t.Border()). BorderForeground(t.Border()).
Render(textarea) Render(textarea)
@@ -286,10 +284,7 @@ func (m *editorComponent) View() string {
info, info,
) )
return styles.ForceReplaceBackgroundWithLipgloss( return content
content,
t.Background(),
)
} }
func (m *editorComponent) SetSize(width, height int) tea.Cmd { func (m *editorComponent) SetSize(width, height int) tea.Cmd {
@@ -414,14 +409,14 @@ func createTextArea(existing *textarea.Model) textarea.Model {
ta := textarea.New() ta := textarea.New()
ta.Placeholder = "It's prompting time..." ta.Placeholder = "It's prompting time..."
ta.BlurredStyle.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor) ta.Styles.Blurred.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor)
ta.BlurredStyle.CursorLine = styles.BaseStyle().Background(bgColor) ta.Styles.Blurred.CursorLine = styles.BaseStyle().Background(bgColor)
ta.BlurredStyle.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor) ta.Styles.Blurred.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor)
ta.BlurredStyle.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor) ta.Styles.Blurred.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor)
ta.FocusedStyle.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor) ta.Styles.Focused.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor)
ta.FocusedStyle.CursorLine = styles.BaseStyle().Background(bgColor) ta.Styles.Focused.CursorLine = styles.BaseStyle().Background(bgColor)
ta.FocusedStyle.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor) ta.Styles.Focused.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor)
ta.FocusedStyle.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor) ta.Styles.Focused.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor)
ta.Prompt = " " ta.Prompt = " "
ta.ShowLineNumbers = false ta.ShowLineNumbers = false
@@ -437,7 +432,7 @@ func createTextArea(existing *textarea.Model) textarea.Model {
return ta return ta
} }
func NewEditorComponent(app *app.App) tea.Model { func NewEditorComponent(app *app.App) layout.ModelWithView {
s := spinner.New(spinner.WithSpinner(spinner.Ellipsis), spinner.WithStyle(styles.Muted().Width(3))) s := spinner.New(spinner.WithSpinner(spinner.Ellipsis), spinner.WithStyle(styles.Muted().Width(3)))
ta := createTextArea(nil) ta := createTextArea(nil)

View File

@@ -9,7 +9,8 @@ import (
"time" "time"
"unicode" "unicode"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/charmbracelet/lipgloss/v2/compat"
"github.com/charmbracelet/x/ansi" "github.com/charmbracelet/x/ansi"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/components/diff" "github.com/sst/opencode/internal/components/diff"
@@ -21,8 +22,8 @@ import (
"golang.org/x/text/language" "golang.org/x/text/language"
) )
func toMarkdown(content string, width int) string { func toMarkdown(content string, width int, backgroundColor compat.AdaptiveColor) string {
r := styles.GetMarkdownRenderer(width) r := styles.GetMarkdownRenderer(width, backgroundColor)
content = strings.ReplaceAll(content, app.Info.Path.Root+"/", "") content = strings.ReplaceAll(content, app.Info.Path.Root+"/", "")
rendered, _ := r.Render(content) rendered, _ := r.Render(content)
lines := strings.Split(rendered, "\n") lines := strings.Split(rendered, "\n")
@@ -50,7 +51,7 @@ func toMarkdown(content string, width int) string {
type blockRenderer struct { type blockRenderer struct {
align *lipgloss.Position align *lipgloss.Position
borderColor *lipgloss.AdaptiveColor borderColor *compat.AdaptiveColor
fullWidth bool fullWidth bool
paddingTop int paddingTop int
paddingBottom int paddingBottom int
@@ -70,7 +71,7 @@ func WithAlign(align lipgloss.Position) renderingOption {
} }
} }
func WithBorderColor(color lipgloss.AdaptiveColor) renderingOption { func WithBorderColor(color compat.AdaptiveColor) renderingOption {
return func(c *blockRenderer) { return func(c *blockRenderer) {
c.borderColor = &color c.borderColor = &color
} }
@@ -137,9 +138,8 @@ func renderContentBlock(content string, options ...renderingOption) string {
BorderLeftBackground(t.Background()) BorderLeftBackground(t.Background())
} }
content = styles.ForceReplaceBackgroundWithLipgloss(content, t.BackgroundSubtle())
if renderer.fullWidth { if renderer.fullWidth {
style = style.Width(layout.Current.Container.Width - 2) style = style.Width(layout.Current.Container.Width)
} }
content = style.Render(content) content = style.Render(content)
if renderer.paddingTop > 0 { if renderer.paddingTop > 0 {
@@ -152,13 +152,11 @@ func renderContentBlock(content string, options ...renderingOption) string {
layout.Current.Container.Width, layout.Current.Container.Width,
align, align,
content, content,
lipgloss.WithWhitespaceBackground(t.Background()),
) )
content = lipgloss.PlaceHorizontal( content = lipgloss.PlaceHorizontal(
layout.Current.Viewport.Width, layout.Current.Viewport.Width,
lipgloss.Center, lipgloss.Center,
content, content,
lipgloss.WithWhitespaceBackground(t.Background()),
) )
return content return content
} }
@@ -181,9 +179,7 @@ func renderText(message client.MessageInfo, text string, author string) string {
// don't show the date if it's today // don't show the date if it's today
timestamp = timestamp[12:] timestamp = timestamp[12:]
} }
info := styles.BaseStyle(). info := fmt.Sprintf("%s (%s)", author, timestamp)
Foreground(t.TextMuted()).
Render(fmt.Sprintf("%s (%s)", author, timestamp))
align := lipgloss.Left align := lipgloss.Left
switch message.Role { switch message.Role {
@@ -195,7 +191,7 @@ func renderText(message client.MessageInfo, text string, author string) string {
textWidth := lipgloss.Width(text) textWidth := lipgloss.Width(text)
markdownWidth := min(textWidth, width-padding-4) // -4 for the border and padding markdownWidth := min(textWidth, width-padding-4) // -4 for the border and padding
content := toMarkdown(text, markdownWidth) content := toMarkdown(text, markdownWidth, t.BackgroundSubtle())
content = lipgloss.JoinVertical(align, content, info) content = lipgloss.JoinVertical(align, content, info)
switch message.Role { switch message.Role {
@@ -313,7 +309,7 @@ func renderToolInvocation(
lipgloss.Center, lipgloss.Center,
lipgloss.Center, lipgloss.Center,
body, body,
lipgloss.WithWhitespaceBackground(t.Background()), lipgloss.WithWhitespaceStyle(lipgloss.NewStyle().Background(t.Background())),
) )
} }
case "opencode_write": case "opencode_write":
@@ -328,7 +324,7 @@ func renderToolInvocation(
command := toolArgsMap["command"].(string) command := toolArgsMap["command"].(string)
stdout := metadata["stdout"].(string) stdout := metadata["stdout"].(string)
body = fmt.Sprintf("```console\n> %s\n%s```", command, stdout) body = fmt.Sprintf("```console\n> %s\n%s```", command, stdout)
body = toMarkdown(body, innerWidth) body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1)) body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
} }
case "opencode_webfetch": case "opencode_webfetch":
@@ -336,7 +332,7 @@ func renderToolInvocation(
format := toolArgsMap["format"].(string) format := toolArgsMap["format"].(string)
body = truncateHeight(body, 10) body = truncateHeight(body, 10)
if format == "html" || format == "markdown" { if format == "html" || format == "markdown" {
body = toMarkdown(body, innerWidth) body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
} }
body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1)) body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
case "opencode_todowrite": case "opencode_todowrite":
@@ -356,7 +352,7 @@ func renderToolInvocation(
body += fmt.Sprintf("- [ ] %s\n", content) body += fmt.Sprintf("- [ ] %s\n", content)
} }
} }
body = toMarkdown(body, innerWidth) body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1)) body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
} }
default: default:
@@ -368,7 +364,7 @@ func renderToolInvocation(
content := style.Render(title) content := style.Render(title)
content = lipgloss.PlaceHorizontal(layout.Current.Viewport.Width, lipgloss.Center, content) content = lipgloss.PlaceHorizontal(layout.Current.Viewport.Width, lipgloss.Center, content)
content = styles.ForceReplaceBackgroundWithLipgloss(content, t.Background()) // content = styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
if showResult && body != "" { if showResult && body != "" {
content += "\n" + body content += "\n" + body
} }
@@ -411,6 +407,7 @@ func WithTruncate(height int) fileRenderingOption {
} }
func renderFile(filename string, content string, options ...fileRenderingOption) string { func renderFile(filename string, content string, options ...fileRenderingOption) string {
t := theme.CurrentTheme()
renderer := &fileRenderer{ renderer := &fileRenderer{
filename: filename, filename: filename,
content: content, content: content,
@@ -419,7 +416,6 @@ func renderFile(filename string, content string, options ...fileRenderingOption)
option(renderer) option(renderer)
} }
// TODO: is this even needed?
lines := []string{} lines := []string{}
for line := range strings.SplitSeq(content, "\n") { for line := range strings.SplitSeq(content, "\n") {
line = strings.TrimRightFunc(line, unicode.IsSpace) line = strings.TrimRightFunc(line, unicode.IsSpace)
@@ -428,23 +424,12 @@ func renderFile(filename string, content string, options ...fileRenderingOption)
} }
content = strings.Join(lines, "\n") content = strings.Join(lines, "\n")
width := layout.Current.Container.Width - 6 width := layout.Current.Container.Width - 8
if renderer.height > 0 { if renderer.height > 0 {
content = truncateHeight(content, renderer.height) content = truncateHeight(content, renderer.height)
} }
content = fmt.Sprintf("```%s\n%s\n```", extension(renderer.filename), content) content = fmt.Sprintf("```%s\n%s\n```", extension(renderer.filename), content)
content = toMarkdown(content, width) content = toMarkdown(content, width, t.BackgroundSubtle())
// ensure no line is wider than the width
// truncated := []string{}
// for line := range strings.SplitSeq(content, "\n") {
// line = strings.TrimRightFunc(line, unicode.IsSpace)
// // if lipgloss.Width(line) > width-3 {
// line = ansi.Truncate(line, width-3, "")
// // }
// truncated = append(truncated, line)
// }
// content = strings.Join(truncated, "\n")
return renderContentBlock(content, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1)) return renderContentBlock(content, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
} }

View File

@@ -4,11 +4,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
"github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/v2/spinner"
"github.com/charmbracelet/bubbles/viewport" "github.com/charmbracelet/bubbles/v2/viewport"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/components/dialog" "github.com/sst/opencode/internal/components/dialog"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
@@ -220,11 +220,11 @@ func (m *messagesComponent) renderView() {
m.width, m.width,
lipgloss.Center, lipgloss.Center,
block, block,
lipgloss.WithWhitespaceBackground(t.Background()), lipgloss.WithWhitespaceStyle(lipgloss.NewStyle().Background(t.Background())),
)) ))
} }
m.viewport.Height = m.height - lipgloss.Height(m.header()) m.viewport.SetHeight(m.height - lipgloss.Height(m.header()))
m.viewport.SetContent(strings.Join(centered, "\n")) m.viewport.SetContent(strings.Join(centered, "\n"))
} }
@@ -238,7 +238,7 @@ func (m *messagesComponent) header() string {
base := styles.BaseStyle().Render base := styles.BaseStyle().Render
muted := styles.Muted().Render muted := styles.Muted().Render
headerLines := []string{} headerLines := []string{}
headerLines = append(headerLines, toMarkdown("# "+m.app.Session.Title, width)) headerLines = append(headerLines, toMarkdown("# "+m.app.Session.Title, width, t.Background()))
if m.app.Session.Share != nil && m.app.Session.Share.Url != "" { if m.app.Session.Share != nil && m.app.Session.Share.Url != "" {
headerLines = append(headerLines, muted(m.app.Session.Share.Url)) headerLines = append(headerLines, muted(m.app.Session.Share.Url))
} else { } else {
@@ -255,7 +255,7 @@ func (m *messagesComponent) header() string {
Background(t.Background()). Background(t.Background()).
Render(header) Render(header)
return styles.ForceReplaceBackgroundWithLipgloss(header, t.Background()) return header
} }
func (m *messagesComponent) View() string { func (m *messagesComponent) View() string {
@@ -269,73 +269,8 @@ func (m *messagesComponent) View() string {
) )
} }
// func hasToolsWithoutResponse(messages []message.Message) bool {
// toolCalls := make([]message.ToolCall, 0)
// toolResults := make([]message.ToolResult, 0)
// for _, m := range messages {
// toolCalls = append(toolCalls, m.ToolCalls()...)
// toolResults = append(toolResults, m.ToolResults()...)
// }
//
// for _, v := range toolCalls {
// found := false
// for _, r := range toolResults {
// if v.ID == r.ToolCallID {
// found = true
// break
// }
// }
// if !found && v.Finished {
// return true
// }
// }
// return false
// }
// func hasUnfinishedToolCalls(messages []message.Message) bool {
// toolCalls := make([]message.ToolCall, 0)
// for _, m := range messages {
// toolCalls = append(toolCalls, m.ToolCalls()...)
// }
// for _, v := range toolCalls {
// if !v.Finished {
// return true
// }
// }
// return false
// }
func (m *messagesComponent) help() string {
t := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
text := ""
if m.app.IsBusy() {
text += lipgloss.JoinHorizontal(
lipgloss.Left,
baseStyle.Foreground(t.TextMuted()).Bold(true).Render("press "),
baseStyle.Foreground(t.Text()).Bold(true).Render("esc"),
baseStyle.Foreground(t.TextMuted()).Bold(true).Render(" to interrupt"),
)
} else {
text += lipgloss.JoinHorizontal(
lipgloss.Left,
baseStyle.Foreground(t.Text()).Bold(true).Render("enter"),
baseStyle.Foreground(t.TextMuted()).Bold(true).Render(" to send,"),
baseStyle.Foreground(t.Text()).Bold(true).Render(" \\"),
baseStyle.Foreground(t.TextMuted()).Bold(true).Render("+"),
baseStyle.Foreground(t.Text()).Bold(true).Render("enter"),
baseStyle.Foreground(t.TextMuted()).Bold(true).Render(" for newline"),
)
}
return baseStyle.
Width(m.width).
Render(text)
}
func (m *messagesComponent) home() string { func (m *messagesComponent) home() string {
t := theme.CurrentTheme() // t := theme.CurrentTheme()
baseStyle := styles.BaseStyle() baseStyle := styles.BaseStyle()
base := baseStyle.Render base := baseStyle.Render
muted := styles.Muted().Render muted := styles.Muted().Render
@@ -398,16 +333,13 @@ func (m *messagesComponent) home() string {
lines = append(lines, "") lines = append(lines, "")
} }
return styles.ForceReplaceBackgroundWithLipgloss( return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center,
lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center,
baseStyle.Width(lipgloss.Width(logoAndVersion)).Render( baseStyle.Width(lipgloss.Width(logoAndVersion)).Render(
lipgloss.JoinVertical( lipgloss.JoinVertical(
lipgloss.Top, lipgloss.Top,
lines..., lines...,
), ),
)), ))
t.Background(),
)
} }
func (m *messagesComponent) SetSize(width, height int) tea.Cmd { func (m *messagesComponent) SetSize(width, height int) tea.Cmd {
@@ -420,10 +352,10 @@ func (m *messagesComponent) SetSize(width, height int) tea.Cmd {
} }
m.width = width m.width = width
m.height = height m.height = height
m.viewport.Width = width m.viewport.SetWidth(width)
m.viewport.Height = height - lipgloss.Height(m.header()) m.viewport.SetHeight(height - lipgloss.Height(m.header()))
m.attachments.Width = width + 40 m.attachments.SetWidth(width + 40)
m.attachments.Height = 3 m.attachments.SetHeight(3)
m.renderView() m.renderView()
return nil return nil
} }
@@ -449,15 +381,15 @@ func (m *messagesComponent) BindingKeys() []key.Binding {
} }
} }
func NewMessagesComponent(app *app.App) tea.Model { func NewMessagesComponent(app *app.App) layout.ModelWithView {
customSpinner := spinner.Spinner{ customSpinner := spinner.Spinner{
Frames: []string{" ", "┃", "┃"}, Frames: []string{" ", "┃", "┃"},
FPS: time.Second / 3, FPS: time.Second / 3,
} }
s := spinner.New(spinner.WithSpinner(customSpinner)) s := spinner.New(spinner.WithSpinner(customSpinner))
vp := viewport.New(0, 0) vp := viewport.New() //(0, 0)
attachments := viewport.New(0, 0) attachments := viewport.New() //(0, 0)
vp.KeyMap.PageUp = messageKeys.PageUp vp.KeyMap.PageUp = messageKeys.PageUp
vp.KeyMap.PageDown = messageKeys.PageDown vp.KeyMap.PageDown = messageKeys.PageDown
vp.KeyMap.HalfPageUp = messageKeys.HalfPageUp vp.KeyMap.HalfPageUp = messageKeys.HalfPageUp

View File

@@ -5,20 +5,21 @@ import (
"strings" "strings"
"time" "time"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/pubsub" "github.com/sst/opencode/internal/pubsub"
"github.com/sst/opencode/internal/status" "github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
) )
type StatusCmp interface { type StatusComponent interface {
tea.Model layout.ModelWithView
} }
type statusCmp struct { type statusComponent struct {
app *app.App app *app.App
queue []status.StatusMessage queue []status.StatusMessage
width int width int
@@ -27,7 +28,7 @@ type statusCmp struct {
} }
// clearMessageCmd is a command that clears status messages after a timeout // clearMessageCmd is a command that clears status messages after a timeout
func (m statusCmp) clearMessageCmd() tea.Cmd { func (m statusComponent) clearMessageCmd() tea.Cmd {
return tea.Tick(time.Second, func(t time.Time) tea.Msg { return tea.Tick(time.Second, func(t time.Time) tea.Msg {
return statusCleanupMsg{time: t} return statusCleanupMsg{time: t}
}) })
@@ -38,11 +39,11 @@ type statusCleanupMsg struct {
time time.Time time time.Time
} }
func (m statusCmp) Init() tea.Cmd { func (m statusComponent) Init() tea.Cmd {
return m.clearMessageCmd() return m.clearMessageCmd()
} }
func (m statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m statusComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
m.width = msg.Width m.width = msg.Width
@@ -104,10 +105,9 @@ func logo() string {
open := styles.Muted().Render("open") open := styles.Muted().Render("open")
code := styles.BaseStyle().Bold(true).Render("code") code := styles.BaseStyle().Bold(true).Render("code")
version := styles.Muted().Render(app.Info.Version) version := styles.Muted().Render(app.Info.Version)
return styles.ForceReplaceBackgroundWithLipgloss( return styles.Padded().
styles.Padded().Render(mark+open+code+" "+version), Background(t.BackgroundElement()).
t.BackgroundElement(), Render(mark + open + code + " " + version)
)
} }
func formatTokensAndCost(tokens float32, contextWindow float32, cost float32) string { func formatTokensAndCost(tokens float32, contextWindow float32, cost float32) string {
@@ -137,7 +137,7 @@ func formatTokensAndCost(tokens float32, contextWindow float32, cost float32) st
return fmt.Sprintf("Tokens: %s (%d%%), Cost: %s", formattedTokens, int(percentage), formattedCost) return fmt.Sprintf("Tokens: %s (%d%%), Cost: %s", formattedTokens, int(percentage), formattedCost)
} }
func (m statusCmp) View() string { func (m statusComponent) View() string {
if m.app.Session.Id == "" { if m.app.Session.Id == "" {
return styles.BaseStyle(). return styles.BaseStyle().
Width(m.width). Width(m.width).
@@ -250,107 +250,8 @@ func (m statusCmp) View() string {
// } // }
} }
func (m *statusCmp) projectDiagnostics() string { func NewStatusCmp(app *app.App) StatusComponent {
t := theme.CurrentTheme() statusComponent := &statusComponent{
// Check if any LSP server is still initializing
initializing := false
// for _, client := range m.app.LSPClients {
// if client.GetServerState() == lsp.StateStarting {
// initializing = true
// break
// }
// }
// If any server is initializing, show that status
if initializing {
return lipgloss.NewStyle().
Foreground(t.Warning()).
Render(fmt.Sprintf("%s Initializing LSP...", styles.SpinnerIcon))
}
// errorDiagnostics := []protocol.Diagnostic{}
// warnDiagnostics := []protocol.Diagnostic{}
// hintDiagnostics := []protocol.Diagnostic{}
// infoDiagnostics := []protocol.Diagnostic{}
// for _, client := range m.app.LSPClients {
// for _, d := range client.GetDiagnostics() {
// for _, diag := range d {
// switch diag.Severity {
// case protocol.SeverityError:
// errorDiagnostics = append(errorDiagnostics, diag)
// case protocol.SeverityWarning:
// warnDiagnostics = append(warnDiagnostics, diag)
// case protocol.SeverityHint:
// hintDiagnostics = append(hintDiagnostics, diag)
// case protocol.SeverityInformation:
// infoDiagnostics = append(infoDiagnostics, diag)
// }
// }
// }
// }
return styles.ForceReplaceBackgroundWithLipgloss(
styles.Padded().Render("No diagnostics"),
t.BackgroundElement(),
)
// if len(errorDiagnostics) == 0 &&
// len(warnDiagnostics) == 0 &&
// len(infoDiagnostics) == 0 &&
// len(hintDiagnostics) == 0 {
// return styles.ForceReplaceBackgroundWithLipgloss(
// styles.Padded().Render("No diagnostics"),
// t.BackgroundDarker(),
// )
// }
// diagnostics := []string{}
//
// errStr := lipgloss.NewStyle().
// Background(t.BackgroundDarker()).
// Foreground(t.Error()).
// Render(fmt.Sprintf("%s %d", styles.ErrorIcon, len(errorDiagnostics)))
// diagnostics = append(diagnostics, errStr)
//
// warnStr := lipgloss.NewStyle().
// Background(t.BackgroundDarker()).
// Foreground(t.Warning()).
// Render(fmt.Sprintf("%s %d", styles.WarningIcon, len(warnDiagnostics)))
// diagnostics = append(diagnostics, warnStr)
//
// infoStr := lipgloss.NewStyle().
// Background(t.BackgroundDarker()).
// Foreground(t.Info()).
// Render(fmt.Sprintf("%s %d", styles.InfoIcon, len(infoDiagnostics)))
// diagnostics = append(diagnostics, infoStr)
//
// hintStr := lipgloss.NewStyle().
// Background(t.BackgroundDarker()).
// Foreground(t.Text()).
// Render(fmt.Sprintf("%s %d", styles.HintIcon, len(hintDiagnostics)))
// diagnostics = append(diagnostics, hintStr)
//
// return styles.ForceReplaceBackgroundWithLipgloss(
// styles.Padded().Render(strings.Join(diagnostics, " ")),
// t.BackgroundDarker(),
// )
}
func (m statusCmp) model() string {
t := theme.CurrentTheme()
model := "None"
if m.app.Model != nil {
model = *m.app.Model.Name
}
return styles.Padded().
Background(t.Secondary()).
Foreground(t.Background()).
Render(model)
}
func NewStatusCmp(app *app.App) StatusCmp {
statusComponent := &statusCmp{
app: app, app: app,
queue: []status.StatusMessage{}, queue: []status.StatusMessage{},
messageTTL: 4 * time.Second, messageTTL: 4 * time.Second,

View File

@@ -2,10 +2,10 @@ package dialog
import ( import (
"fmt" "fmt"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
"github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/v2/textinput"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
@@ -70,17 +70,24 @@ func NewMultiArgumentsDialogCmp(commandID, content string, argNames []string) Mu
for i, name := range argNames { for i, name := range argNames {
ti := textinput.New() ti := textinput.New()
ti.Placeholder = fmt.Sprintf("Enter value for %s...", name) ti.Placeholder = fmt.Sprintf("Enter value for %s...", name)
ti.Width = 40 ti.SetWidth(40)
ti.Prompt = "" ti.Prompt = ""
ti.PlaceholderStyle = ti.PlaceholderStyle.Background(t.Background()) ti.Styles.Blurred.Placeholder = ti.Styles.Blurred.Placeholder.Background(t.Background())
ti.PromptStyle = ti.PromptStyle.Background(t.Background()) ti.Styles.Blurred.Text = ti.Styles.Blurred.Text.Background(t.Background())
ti.TextStyle = ti.TextStyle.Background(t.Background()) ti.Styles.Blurred.Prompt = ti.Styles.Blurred.Prompt.Foreground(t.Primary())
ti.Styles.Focused.Placeholder = ti.Styles.Focused.Placeholder.Background(t.Background())
ti.Styles.Focused.Text = ti.Styles.Focused.Text.Background(t.Background())
ti.Styles.Focused.Prompt = ti.Styles.Focused.Prompt.Foreground(t.Primary())
// ti.PromptStyle = ti.PromptStyle.Background(t.Background())
// ti.TextStyle = ti.TextStyle.Background(t.Background())
// Only focus the first input initially // Only focus the first input initially
if i == 0 { if i == 0 {
ti.Focus() ti.Focus()
ti.PromptStyle = ti.PromptStyle.Foreground(t.Primary()) // ti.PromptStyle = ti.PromptStyle.Foreground(t.Primary())
ti.TextStyle = ti.TextStyle.Foreground(t.Primary()) // ti.TextStyle = ti.TextStyle.Foreground(t.Primary())
} else { } else {
ti.Blur() ti.Blur()
} }
@@ -115,7 +122,7 @@ func (m MultiArgumentsDialogCmp) Init() tea.Cmd {
// Update implements tea.Model. // Update implements tea.Model.
func (m MultiArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m MultiArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd var cmds []tea.Cmd
t := theme.CurrentTheme() // t := theme.CurrentTheme()
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
@@ -145,22 +152,22 @@ func (m MultiArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.inputs[m.focusIndex].Blur() m.inputs[m.focusIndex].Blur()
m.focusIndex++ m.focusIndex++
m.inputs[m.focusIndex].Focus() m.inputs[m.focusIndex].Focus()
m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary()) // m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary()) // m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
case key.Matches(msg, key.NewBinding(key.WithKeys("tab"))): case key.Matches(msg, key.NewBinding(key.WithKeys("tab"))):
// Move to the next input // Move to the next input
m.inputs[m.focusIndex].Blur() m.inputs[m.focusIndex].Blur()
m.focusIndex = (m.focusIndex + 1) % len(m.inputs) m.focusIndex = (m.focusIndex + 1) % len(m.inputs)
m.inputs[m.focusIndex].Focus() m.inputs[m.focusIndex].Focus()
m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary()) // m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary()) // m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
case key.Matches(msg, key.NewBinding(key.WithKeys("shift+tab"))): case key.Matches(msg, key.NewBinding(key.WithKeys("shift+tab"))):
// Move to the previous input // Move to the previous input
m.inputs[m.focusIndex].Blur() m.inputs[m.focusIndex].Blur()
m.focusIndex = (m.focusIndex - 1 + len(m.inputs)) % len(m.inputs) m.focusIndex = (m.focusIndex - 1 + len(m.inputs)) % len(m.inputs)
m.inputs[m.focusIndex].Focus() m.inputs[m.focusIndex].Focus()
m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary()) // m.inputs[m.focusIndex].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary()) // m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
} }
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
m.width = msg.Width m.width = msg.Width

View File

@@ -1,9 +1,9 @@
package dialog package dialog
import ( import (
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
utilComponents "github.com/sst/opencode/internal/components/util" utilComponents "github.com/sst/opencode/internal/components/util"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
@@ -56,12 +56,12 @@ type CloseCommandDialogMsg struct{}
// CommandDialog interface for the command selection dialog // CommandDialog interface for the command selection dialog
type CommandDialog interface { type CommandDialog interface {
tea.Model layout.ModelWithView
layout.Bindings layout.Bindings
SetCommands(commands []Command) SetCommands(commands []Command)
} }
type commandDialogCmp struct { type commandDialogComponent struct {
listView utilComponents.SimpleList[Command] listView utilComponents.SimpleList[Command]
width int width int
height int height int
@@ -83,11 +83,11 @@ var commandKeys = commandKeyMap{
), ),
} }
func (c *commandDialogCmp) Init() tea.Cmd { func (c *commandDialogComponent) Init() tea.Cmd {
return c.listView.Init() return c.listView.Init()
} }
func (c *commandDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (c *commandDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd var cmds []tea.Cmd
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
@@ -114,7 +114,7 @@ func (c *commandDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return c, tea.Batch(cmds...) return c, tea.Batch(cmds...)
} }
func (c *commandDialogCmp) View() string { func (c *commandDialogComponent) View() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
baseStyle := styles.BaseStyle() baseStyle := styles.BaseStyle()
@@ -158,11 +158,11 @@ func (c *commandDialogCmp) View() string {
Render(content) Render(content)
} }
func (c *commandDialogCmp) BindingKeys() []key.Binding { func (c *commandDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(commandKeys) return layout.KeyMapToSlice(commandKeys)
} }
func (c *commandDialogCmp) SetCommands(commands []Command) { func (c *commandDialogComponent) SetCommands(commands []Command) {
c.listView.SetItems(commands) c.listView.SetItems(commands)
} }
@@ -174,7 +174,7 @@ func NewCommandDialogCmp() CommandDialog {
"No commands available", "No commands available",
true, true,
) )
return &commandDialogCmp{ return &commandDialogComponent{
listView: listView, listView: listView,
} }
} }

View File

@@ -1,13 +1,13 @@
package dialog package dialog
import ( import (
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
"github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/v2/textarea"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/status"
utilComponents "github.com/sst/opencode/internal/components/util" utilComponents "github.com/sst/opencode/internal/components/util"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
"github.com/sst/opencode/internal/util" "github.com/sst/opencode/internal/util"
@@ -77,7 +77,7 @@ type CompletionDialogCompleteItemMsg struct {
type CompletionDialogCloseMsg struct{} type CompletionDialogCloseMsg struct{}
type CompletionDialog interface { type CompletionDialog interface {
tea.Model layout.ModelWithView
layout.Bindings layout.Bindings
SetWidth(width int) SetWidth(width int)
} }

View File

@@ -7,7 +7,7 @@ import (
"regexp" "regexp"
"strings" "strings"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/util" "github.com/sst/opencode/internal/util"
) )

View File

@@ -12,13 +12,14 @@ import (
"log/slog" "log/slog"
"github.com/atotto/clipboard" "github.com/atotto/clipboard"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
"github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/v2/textinput"
"github.com/charmbracelet/bubbles/viewport" "github.com/charmbracelet/bubbles/v2/viewport"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/image" "github.com/sst/opencode/internal/image"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/status" "github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
@@ -82,7 +83,7 @@ var filePickerKeyMap = FilePrickerKeyMap{
), ),
} }
type filepickerCmp struct { type filepickerComponent struct {
basePath string basePath string
width int width int
height int height int
@@ -118,18 +119,18 @@ type AttachmentAddedMsg struct {
Attachment app.Attachment Attachment app.Attachment
} }
func (f *filepickerCmp) Init() tea.Cmd { func (f *filepickerComponent) Init() tea.Cmd {
return nil return nil
} }
func (f *filepickerCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (f *filepickerComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd var cmd tea.Cmd
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
f.width = 60 f.width = 60
f.height = 20 f.height = 20
f.viewport.Width = 80 f.viewport.SetWidth(80)
f.viewport.Height = 22 f.viewport.SetHeight(22)
f.cursor = 0 f.cursor = 0
f.getCurrentFileBelowCursor() f.getCurrentFileBelowCursor()
case tea.KeyMsg: case tea.KeyMsg:
@@ -236,7 +237,7 @@ func (f *filepickerCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return f, cmd return f, cmd
} }
func (f *filepickerCmp) addAttachmentToMessage() (tea.Model, tea.Cmd) { func (f *filepickerComponent) addAttachmentToMessage() (tea.Model, tea.Cmd) {
// modeInfo := GetSelectedModel(config.Get()) // modeInfo := GetSelectedModel(config.Get())
// if !modeInfo.SupportsAttachments { // if !modeInfo.SupportsAttachments {
// status.Error(fmt.Sprintf("Model %s doesn't support attachments", modeInfo.Name)) // status.Error(fmt.Sprintf("Model %s doesn't support attachments", modeInfo.Name))
@@ -273,7 +274,7 @@ func (f *filepickerCmp) addAttachmentToMessage() (tea.Model, tea.Cmd) {
return f, util.CmdHandler(AttachmentAddedMsg{attachment}) return f, util.CmdHandler(AttachmentAddedMsg{attachment})
} }
func (f *filepickerCmp) View() string { func (f *filepickerComponent) View() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
const maxVisibleDirs = 20 const maxVisibleDirs = 20
const maxWidth = 80 const maxWidth = 80
@@ -333,7 +334,7 @@ func (f *filepickerCmp) View() string {
Render(f.cwd.View()) Render(f.cwd.View())
viewportstyle := lipgloss.NewStyle(). viewportstyle := lipgloss.NewStyle().
Width(f.viewport.Width). Width(f.viewport.Width()).
Background(t.Background()). Background(t.Background()).
Border(lipgloss.RoundedBorder()). Border(lipgloss.RoundedBorder()).
BorderForeground(t.TextMuted()). BorderForeground(t.TextMuted()).
@@ -366,21 +367,21 @@ func (f *filepickerCmp) View() string {
return lipgloss.JoinHorizontal(lipgloss.Center, contentStyle.Render(content), viewportstyle) return lipgloss.JoinHorizontal(lipgloss.Center, contentStyle.Render(content), viewportstyle)
} }
type FilepickerCmp interface { type FilepickerComponent interface {
tea.Model layout.ModelWithView
ToggleFilepicker(showFilepicker bool) ToggleFilepicker(showFilepicker bool)
IsCWDFocused() bool IsCWDFocused() bool
} }
func (f *filepickerCmp) ToggleFilepicker(showFilepicker bool) { func (f *filepickerComponent) ToggleFilepicker(showFilepicker bool) {
f.ShowFilePicker = showFilepicker f.ShowFilePicker = showFilepicker
} }
func (f *filepickerCmp) IsCWDFocused() bool { func (f *filepickerComponent) IsCWDFocused() bool {
return f.cwd.Focused() return f.cwd.Focused()
} }
func NewFilepickerCmp(app *app.App) FilepickerCmp { func NewFilepickerCmp(app *app.App) FilepickerComponent {
homepath, err := os.UserHomeDir() homepath, err := os.UserHomeDir()
if err != nil { if err != nil {
slog.Error("error loading user files") slog.Error("error loading user files")
@@ -388,16 +389,16 @@ func NewFilepickerCmp(app *app.App) FilepickerCmp {
} }
baseDir := DirNode{parent: nil, directory: homepath} baseDir := DirNode{parent: nil, directory: homepath}
dirs := readDir(homepath, false) dirs := readDir(homepath, false)
viewport := viewport.New(0, 0) viewport := viewport.New() // viewport.New(0, 0)
currentDirectory := textinput.New() currentDirectory := textinput.New()
currentDirectory.CharLimit = 200 currentDirectory.CharLimit = 200
currentDirectory.Width = 44 currentDirectory.SetWidth(44)
currentDirectory.Cursor.Blink = true // currentDirectory.Cursor.Blink = true
currentDirectory.SetValue(baseDir.directory) currentDirectory.SetValue(baseDir.directory)
return &filepickerCmp{cwdDetails: &baseDir, dirs: dirs, cursorChain: make(stack, 0), viewport: viewport, cwd: currentDirectory, app: app} return &filepickerComponent{cwdDetails: &baseDir, dirs: dirs, cursorChain: make(stack, 0), viewport: viewport, cwd: currentDirectory, app: app}
} }
func (f *filepickerCmp) getCurrentFileBelowCursor() { func (f *filepickerComponent) getCurrentFileBelowCursor() {
if len(f.dirs) == 0 || f.cursor < 0 || f.cursor >= len(f.dirs) { if len(f.dirs) == 0 || f.cursor < 0 || f.cursor >= len(f.dirs) {
slog.Error(fmt.Sprintf("Invalid cursor position. Dirs length: %d, Cursor: %d", len(f.dirs), f.cursor)) slog.Error(fmt.Sprintf("Invalid cursor position. Dirs length: %d, Cursor: %d", len(f.dirs), f.cursor))
f.viewport.SetContent("Preview unavailable") f.viewport.SetContent("Preview unavailable")
@@ -410,7 +411,7 @@ func (f *filepickerCmp) getCurrentFileBelowCursor() {
fullPath := f.cwdDetails.directory + "/" + dir.Name() fullPath := f.cwdDetails.directory + "/" + dir.Name()
go func() { go func() {
imageString, err := image.ImagePreview(f.viewport.Width-4, fullPath) imageString, err := image.ImagePreview(f.viewport.Width()-4, fullPath)
if err != nil { if err != nil {
slog.Error(err.Error()) slog.Error(err.Error())
f.viewport.SetContent("Preview unavailable") f.viewport.SetContent("Preview unavailable")

View File

@@ -3,28 +3,29 @@ package dialog
import ( import (
"strings" "strings"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
) )
type helpCmp struct { type helpComponent struct {
width int width int
height int height int
keys []key.Binding keys []key.Binding
} }
func (h *helpCmp) Init() tea.Cmd { func (h *helpComponent) Init() tea.Cmd {
return nil return nil
} }
func (h *helpCmp) SetBindings(k []key.Binding) { func (h *helpComponent) SetBindings(k []key.Binding) {
h.keys = k h.keys = k
} }
func (h *helpCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (h *helpComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
h.width = 90 h.width = 90
@@ -53,7 +54,7 @@ func removeDuplicateBindings(bindings []key.Binding) []key.Binding {
return result return result
} }
func (h *helpCmp) render() string { func (h *helpComponent) render() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
baseStyle := styles.BaseStyle() baseStyle := styles.BaseStyle()
@@ -134,7 +135,7 @@ func (h *helpCmp) render() string {
pairs = append(pairs, pair) pairs = append(pairs, pair)
} }
// https://github.com/charmbracelet/lipgloss/issues/209 // https://github.com/charmbracelet/lipgloss/v2/issues/209
if len(pairs) > 1 { if len(pairs) > 1 {
prefix := pairs[:len(pairs)-1] prefix := pairs[:len(pairs)-1]
lastPair := pairs[len(pairs)-1] lastPair := pairs[len(pairs)-1]
@@ -144,7 +145,7 @@ func (h *helpCmp) render() string {
lipgloss.Left, // x lipgloss.Left, // x
lipgloss.Top, // y lipgloss.Top, // y
lastPair, // content lastPair, // content
lipgloss.WithWhitespaceBackground(t.Background()), // lipgloss.WithWhitespaceBackground(t.Background()),
)) ))
content := baseStyle.Width(h.width).Render( content := baseStyle.Width(h.width).Render(
lipgloss.JoinHorizontal( lipgloss.JoinHorizontal(
@@ -165,7 +166,7 @@ func (h *helpCmp) render() string {
return content return content
} }
func (h *helpCmp) View() string { func (h *helpComponent) View() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
baseStyle := styles.BaseStyle() baseStyle := styles.BaseStyle()
@@ -190,11 +191,11 @@ func (h *helpCmp) View() string {
) )
} }
type HelpCmp interface { type HelpComponent interface {
tea.Model layout.ModelWithView
SetBindings([]key.Binding) SetBindings([]key.Binding)
} }
func NewHelpCmp() HelpCmp { func NewHelpCmp() HelpComponent {
return &helpCmp{} return &helpComponent{}
} }

View File

@@ -1,9 +1,9 @@
package dialog package dialog
import ( import (
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"

View File

@@ -7,9 +7,9 @@ import (
"slices" "slices"
"strings" "strings"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
@@ -31,13 +31,13 @@ type CloseModelDialogMsg struct {
// ModelDialog interface for the model selection dialog // ModelDialog interface for the model selection dialog
type ModelDialog interface { type ModelDialog interface {
tea.Model layout.ModelWithView
layout.Bindings layout.Bindings
SetProviders(providers []client.ProviderInfo) SetProviders(providers []client.ProviderInfo)
} }
type modelDialogCmp struct { type modelDialogComponent struct {
app *app.App app *app.App
availableProviders []client.ProviderInfo availableProviders []client.ProviderInfo
provider client.ProviderInfo provider client.ProviderInfo
@@ -106,7 +106,7 @@ var modelKeys = modelKeyMap{
), ),
} }
func (m *modelDialogCmp) Init() tea.Cmd { func (m *modelDialogComponent) Init() tea.Cmd {
// cfg := config.Get() // cfg := config.Get()
// modelInfo := GetSelectedModel(cfg) // modelInfo := GetSelectedModel(cfg)
// m.availableProviders = getEnabledProviders(cfg) // m.availableProviders = getEnabledProviders(cfg)
@@ -125,11 +125,11 @@ func (m *modelDialogCmp) Init() tea.Cmd {
return nil return nil
} }
func (m *modelDialogCmp) SetProviders(providers []client.ProviderInfo) { func (m *modelDialogComponent) SetProviders(providers []client.ProviderInfo) {
m.availableProviders = providers m.availableProviders = providers
} }
func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m *modelDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
switch { switch {
@@ -159,7 +159,7 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil return m, nil
} }
func (m *modelDialogCmp) models() []client.ProviderModel { func (m *modelDialogComponent) models() []client.ProviderModel {
models := slices.SortedFunc(maps.Values(m.provider.Models), func(a, b client.ProviderModel) int { models := slices.SortedFunc(maps.Values(m.provider.Models), func(a, b client.ProviderModel) int {
return strings.Compare(*a.Name, *b.Name) return strings.Compare(*a.Name, *b.Name)
}) })
@@ -167,7 +167,7 @@ func (m *modelDialogCmp) models() []client.ProviderModel {
} }
// moveSelectionUp moves the selection up or wraps to bottom // moveSelectionUp moves the selection up or wraps to bottom
func (m *modelDialogCmp) moveSelectionUp() { func (m *modelDialogComponent) moveSelectionUp() {
if m.selectedIdx > 0 { if m.selectedIdx > 0 {
m.selectedIdx-- m.selectedIdx--
} else { } else {
@@ -182,7 +182,7 @@ func (m *modelDialogCmp) moveSelectionUp() {
} }
// moveSelectionDown moves the selection down or wraps to top // moveSelectionDown moves the selection down or wraps to top
func (m *modelDialogCmp) moveSelectionDown() { func (m *modelDialogComponent) moveSelectionDown() {
if m.selectedIdx < len(m.provider.Models)-1 { if m.selectedIdx < len(m.provider.Models)-1 {
m.selectedIdx++ m.selectedIdx++
} else { } else {
@@ -196,7 +196,7 @@ func (m *modelDialogCmp) moveSelectionDown() {
} }
} }
func (m *modelDialogCmp) switchProvider(offset int) { func (m *modelDialogComponent) switchProvider(offset int) {
newOffset := m.hScrollOffset + offset newOffset := m.hScrollOffset + offset
// Ensure we stay within bounds // Ensure we stay within bounds
@@ -212,7 +212,7 @@ func (m *modelDialogCmp) switchProvider(offset int) {
m.setupModelsForProvider(m.provider.Id) m.setupModelsForProvider(m.provider.Id)
} }
func (m *modelDialogCmp) View() string { func (m *modelDialogComponent) View() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
baseStyle := styles.BaseStyle() baseStyle := styles.BaseStyle()
@@ -255,7 +255,7 @@ func (m *modelDialogCmp) View() string {
Render(content) Render(content)
} }
func (m *modelDialogCmp) getScrollIndicators(maxWidth int) string { func (m *modelDialogComponent) getScrollIndicators(maxWidth int) string {
var indicator string var indicator string
if len(m.provider.Models) > numVisibleModels { if len(m.provider.Models) > numVisibleModels {
@@ -291,7 +291,7 @@ func (m *modelDialogCmp) getScrollIndicators(maxWidth int) string {
Render(indicator) Render(indicator)
} }
func (m *modelDialogCmp) BindingKeys() []key.Binding { func (m *modelDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(modelKeys) return layout.KeyMapToSlice(modelKeys)
} }
@@ -305,7 +305,7 @@ func (m *modelDialogCmp) BindingKeys() []key.Binding {
// return -1 // return -1
// } // }
func (m *modelDialogCmp) setupModelsForProvider(_ string) { func (m *modelDialogComponent) setupModelsForProvider(_ string) {
m.selectedIdx = 0 m.selectedIdx = 0
m.scrollOffset = 0 m.scrollOffset = 0
@@ -332,7 +332,7 @@ func (m *modelDialogCmp) setupModelsForProvider(_ string) {
} }
func NewModelDialogCmp(app *app.App) ModelDialog { func NewModelDialogCmp(app *app.App) ModelDialog {
return &modelDialogCmp{ return &modelDialogComponent{
app: app, app: app,
} }
} }

View File

@@ -2,10 +2,10 @@ package dialog
import ( import (
"fmt" "fmt"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
"github.com/charmbracelet/bubbles/viewport" "github.com/charmbracelet/bubbles/v2/viewport"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
@@ -28,9 +28,9 @@ type PermissionResponseMsg struct {
Action PermissionAction Action PermissionAction
} }
// PermissionDialogCmp interface for permission dialog component // PermissionDialogComponent interface for permission dialog component
type PermissionDialogCmp interface { type PermissionDialogComponent interface {
tea.Model layout.ModelWithView
layout.Bindings layout.Bindings
// SetPermissions(permission permission.PermissionRequest) tea.Cmd // SetPermissions(permission permission.PermissionRequest) tea.Cmd
} }
@@ -76,8 +76,8 @@ var permissionsKeys = permissionsMapping{
), ),
} }
// permissionDialogCmp is the implementation of PermissionDialog // permissionDialogComponent is the implementation of PermissionDialog
type permissionDialogCmp struct { type permissionDialogComponent struct {
width int width int
height int height int
// permission permission.PermissionRequest // permission permission.PermissionRequest
@@ -89,11 +89,11 @@ type permissionDialogCmp struct {
markdownCache map[string]string markdownCache map[string]string
} }
func (p *permissionDialogCmp) Init() tea.Cmd { func (p *permissionDialogComponent) Init() tea.Cmd {
return p.contentViewPort.Init() return p.contentViewPort.Init()
} }
func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (p *permissionDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd var cmds []tea.Cmd
switch msg := msg.(type) { switch msg := msg.(type) {
@@ -129,7 +129,7 @@ func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return p, tea.Batch(cmds...) return p, tea.Batch(cmds...)
} }
func (p *permissionDialogCmp) selectCurrentOption() tea.Cmd { func (p *permissionDialogComponent) selectCurrentOption() tea.Cmd {
var action PermissionAction var action PermissionAction
switch p.selectedOption { switch p.selectedOption {
@@ -144,7 +144,7 @@ func (p *permissionDialogCmp) selectCurrentOption() tea.Cmd {
return util.CmdHandler(PermissionResponseMsg{Action: action}) // , Permission: p.permission}) return util.CmdHandler(PermissionResponseMsg{Action: action}) // , Permission: p.permission})
} }
func (p *permissionDialogCmp) renderButtons() string { func (p *permissionDialogComponent) renderButtons() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
baseStyle := styles.BaseStyle() baseStyle := styles.BaseStyle()
@@ -190,7 +190,7 @@ func (p *permissionDialogCmp) renderButtons() string {
return content return content
} }
func (p *permissionDialogCmp) renderHeader() string { func (p *permissionDialogComponent) renderHeader() string {
return "NOT IMPLEMENTED" return "NOT IMPLEMENTED"
// t := theme.CurrentTheme() // t := theme.CurrentTheme()
// baseStyle := styles.BaseStyle() // baseStyle := styles.BaseStyle()
@@ -246,7 +246,7 @@ func (p *permissionDialogCmp) renderHeader() string {
// return lipgloss.NewStyle().Background(t.Background()).Render(lipgloss.JoinVertical(lipgloss.Left, headerParts...)) // return lipgloss.NewStyle().Background(t.Background()).Render(lipgloss.JoinVertical(lipgloss.Left, headerParts...))
} }
func (p *permissionDialogCmp) renderBashContent() string { func (p *permissionDialogComponent) renderBashContent() string {
// t := theme.CurrentTheme() // t := theme.CurrentTheme()
// baseStyle := styles.BaseStyle() // baseStyle := styles.BaseStyle()
// //
@@ -269,7 +269,7 @@ func (p *permissionDialogCmp) renderBashContent() string {
return "" return ""
} }
func (p *permissionDialogCmp) renderEditContent() string { func (p *permissionDialogComponent) renderEditContent() string {
// if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok { // if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok {
// diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) { // diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
// return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width)) // return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width))
@@ -281,7 +281,7 @@ func (p *permissionDialogCmp) renderEditContent() string {
return "" return ""
} }
func (p *permissionDialogCmp) renderPatchContent() string { func (p *permissionDialogComponent) renderPatchContent() string {
// if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok { // if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok {
// diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) { // diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
// return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width)) // return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width))
@@ -293,7 +293,7 @@ func (p *permissionDialogCmp) renderPatchContent() string {
return "" return ""
} }
func (p *permissionDialogCmp) renderWriteContent() string { func (p *permissionDialogComponent) renderWriteContent() string {
// if pr, ok := p.permission.Params.(tools.WritePermissionsParams); ok { // if pr, ok := p.permission.Params.(tools.WritePermissionsParams); ok {
// // Use the cache for diff rendering // // Use the cache for diff rendering
// diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) { // diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
@@ -306,7 +306,7 @@ func (p *permissionDialogCmp) renderWriteContent() string {
return "" return ""
} }
func (p *permissionDialogCmp) renderFetchContent() string { func (p *permissionDialogComponent) renderFetchContent() string {
// t := theme.CurrentTheme() // t := theme.CurrentTheme()
// baseStyle := styles.BaseStyle() // baseStyle := styles.BaseStyle()
// //
@@ -329,7 +329,7 @@ func (p *permissionDialogCmp) renderFetchContent() string {
return "" return ""
} }
func (p *permissionDialogCmp) renderDefaultContent() string { func (p *permissionDialogComponent) renderDefaultContent() string {
// t := theme.CurrentTheme() // t := theme.CurrentTheme()
// baseStyle := styles.BaseStyle() // baseStyle := styles.BaseStyle()
// //
@@ -354,7 +354,7 @@ func (p *permissionDialogCmp) renderDefaultContent() string {
return p.styleViewport() return p.styleViewport()
} }
func (p *permissionDialogCmp) styleViewport() string { func (p *permissionDialogComponent) styleViewport() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
contentStyle := lipgloss.NewStyle(). contentStyle := lipgloss.NewStyle().
Background(t.Background()) Background(t.Background())
@@ -362,7 +362,7 @@ func (p *permissionDialogCmp) styleViewport() string {
return contentStyle.Render(p.contentViewPort.View()) return contentStyle.Render(p.contentViewPort.View())
} }
func (p *permissionDialogCmp) render() string { func (p *permissionDialogComponent) render() string {
return "NOT IMPLEMENTED" return "NOT IMPLEMENTED"
// t := theme.CurrentTheme() // t := theme.CurrentTheme()
// baseStyle := styles.BaseStyle() // baseStyle := styles.BaseStyle()
@@ -420,15 +420,15 @@ func (p *permissionDialogCmp) render() string {
// ) // )
} }
func (p *permissionDialogCmp) View() string { func (p *permissionDialogComponent) View() string {
return p.render() return p.render()
} }
func (p *permissionDialogCmp) BindingKeys() []key.Binding { func (p *permissionDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(permissionsKeys) return layout.KeyMapToSlice(permissionsKeys)
} }
func (p *permissionDialogCmp) SetSize() tea.Cmd { func (p *permissionDialogComponent) SetSize() tea.Cmd {
// if p.permission.ID == "" { // if p.permission.ID == "" {
// return nil // return nil
// } // }
@@ -458,7 +458,7 @@ func (p *permissionDialogCmp) SetSize() tea.Cmd {
// } // }
// Helper to get or set cached diff content // Helper to get or set cached diff content
func (c *permissionDialogCmp) GetOrSetDiff(key string, generator func() (string, error)) string { func (c *permissionDialogComponent) GetOrSetDiff(key string, generator func() (string, error)) string {
if cached, ok := c.diffCache[key]; ok { if cached, ok := c.diffCache[key]; ok {
return cached return cached
} }
@@ -474,7 +474,7 @@ func (c *permissionDialogCmp) GetOrSetDiff(key string, generator func() (string,
} }
// Helper to get or set cached markdown content // Helper to get or set cached markdown content
func (c *permissionDialogCmp) GetOrSetMarkdown(key string, generator func() (string, error)) string { func (c *permissionDialogComponent) GetOrSetMarkdown(key string, generator func() (string, error)) string {
if cached, ok := c.markdownCache[key]; ok { if cached, ok := c.markdownCache[key]; ok {
return cached return cached
} }
@@ -489,11 +489,11 @@ func (c *permissionDialogCmp) GetOrSetMarkdown(key string, generator func() (str
return content return content
} }
func NewPermissionDialogCmp() PermissionDialogCmp { func NewPermissionDialogCmp() PermissionDialogComponent {
// Create viewport for content // Create viewport for content
contentViewport := viewport.New(0, 0) contentViewport := viewport.New() // (0, 0)
return &permissionDialogCmp{ return &permissionDialogComponent{
contentViewPort: contentViewport, contentViewPort: contentViewport,
selectedOption: 0, // Default to "Allow" selectedOption: 0, // Default to "Allow"
diffCache: make(map[string]string), diffCache: make(map[string]string),

View File

@@ -3,9 +3,9 @@ package dialog
import ( import (
"strings" "strings"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
@@ -17,11 +17,11 @@ const question = "Are you sure you want to quit?"
type CloseQuitMsg struct{} type CloseQuitMsg struct{}
type QuitDialog interface { type QuitDialog interface {
tea.Model layout.ModelWithView
layout.Bindings layout.Bindings
} }
type quitDialogCmp struct { type quitDialogComponent struct {
selectedNo bool selectedNo bool
} }
@@ -56,11 +56,11 @@ var helpKeys = helpMapping{
), ),
} }
func (q *quitDialogCmp) Init() tea.Cmd { func (q *quitDialogComponent) Init() tea.Cmd {
return nil return nil
} }
func (q *quitDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (q *quitDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
switch { switch {
@@ -81,7 +81,7 @@ func (q *quitDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return q, nil return q, nil
} }
func (q *quitDialogCmp) View() string { func (q *quitDialogComponent) View() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
baseStyle := styles.BaseStyle() baseStyle := styles.BaseStyle()
@@ -125,12 +125,12 @@ func (q *quitDialogCmp) View() string {
Render(content) Render(content)
} }
func (q *quitDialogCmp) BindingKeys() []key.Binding { func (q *quitDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(helpKeys) return layout.KeyMapToSlice(helpKeys)
} }
func NewQuitCmp() QuitDialog { func NewQuitCmp() QuitDialog {
return &quitDialogCmp{ return &quitDialogComponent{
selectedNo: true, selectedNo: true,
} }
} }

View File

@@ -1,9 +1,9 @@
package dialog package dialog
import ( import (
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
@@ -18,13 +18,13 @@ type CloseSessionDialogMsg struct {
// SessionDialog interface for the session switching dialog // SessionDialog interface for the session switching dialog
type SessionDialog interface { type SessionDialog interface {
tea.Model layout.ModelWithView
layout.Bindings layout.Bindings
SetSessions(sessions []client.SessionInfo) SetSessions(sessions []client.SessionInfo)
SetSelectedSession(sessionID string) SetSelectedSession(sessionID string)
} }
type sessionDialogCmp struct { type sessionDialogComponent struct {
sessions []client.SessionInfo sessions []client.SessionInfo
selectedIdx int selectedIdx int
width int width int
@@ -68,11 +68,11 @@ var sessionKeys = sessionKeyMap{
), ),
} }
func (s *sessionDialogCmp) Init() tea.Cmd { func (s *sessionDialogComponent) Init() tea.Cmd {
return nil return nil
} }
func (s *sessionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (s *sessionDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
s.width = msg.Width s.width = msg.Width
@@ -105,7 +105,7 @@ func (s *sessionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return s, nil return s, nil
} }
func (s *sessionDialogCmp) View() string { func (s *sessionDialogComponent) View() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
baseStyle := styles.BaseStyle() baseStyle := styles.BaseStyle()
@@ -185,11 +185,11 @@ func (s *sessionDialogCmp) View() string {
Render(content) Render(content)
} }
func (s *sessionDialogCmp) BindingKeys() []key.Binding { func (s *sessionDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(sessionKeys) return layout.KeyMapToSlice(sessionKeys)
} }
func (s *sessionDialogCmp) SetSessions(sessions []client.SessionInfo) { func (s *sessionDialogComponent) SetSessions(sessions []client.SessionInfo) {
s.sessions = sessions s.sessions = sessions
// If we have a selected session ID, find its index // If we have a selected session ID, find its index
@@ -206,7 +206,7 @@ func (s *sessionDialogCmp) SetSessions(sessions []client.SessionInfo) {
s.selectedIdx = 0 s.selectedIdx = 0
} }
func (s *sessionDialogCmp) SetSelectedSession(sessionID string) { func (s *sessionDialogComponent) SetSelectedSession(sessionID string) {
s.selectedSessionID = sessionID s.selectedSessionID = sessionID
// Update the selected index if sessions are already loaded // Update the selected index if sessions are already loaded
@@ -222,7 +222,7 @@ func (s *sessionDialogCmp) SetSelectedSession(sessionID string) {
// NewSessionDialogCmp creates a new session switching dialog // NewSessionDialogCmp creates a new session switching dialog
func NewSessionDialogCmp() SessionDialog { func NewSessionDialogCmp() SessionDialog {
return &sessionDialogCmp{ return &sessionDialogComponent{
sessions: []client.SessionInfo{}, sessions: []client.SessionInfo{},
selectedIdx: 0, selectedIdx: 0,
selectedSessionID: "", selectedSessionID: "",

View File

@@ -1,9 +1,9 @@
package dialog package dialog
import ( import (
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/status" "github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
@@ -21,11 +21,11 @@ type CloseThemeDialogMsg struct{}
// ThemeDialog interface for the theme switching dialog // ThemeDialog interface for the theme switching dialog
type ThemeDialog interface { type ThemeDialog interface {
tea.Model layout.ModelWithView
layout.Bindings layout.Bindings
} }
type themeDialogCmp struct { type themeDialogComponent struct {
themes []string themes []string
selectedIdx int selectedIdx int
width int width int
@@ -69,7 +69,7 @@ var themeKeys = themeKeyMap{
), ),
} }
func (t *themeDialogCmp) Init() tea.Cmd { func (t *themeDialogComponent) Init() tea.Cmd {
// Load available themes and update selectedIdx based on current theme // Load available themes and update selectedIdx based on current theme
t.themes = theme.AvailableThemes() t.themes = theme.AvailableThemes()
t.currentTheme = theme.CurrentThemeName() t.currentTheme = theme.CurrentThemeName()
@@ -85,7 +85,7 @@ func (t *themeDialogCmp) Init() tea.Cmd {
return nil return nil
} }
func (t *themeDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (t *themeDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
switch { switch {
@@ -124,7 +124,7 @@ func (t *themeDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return t, nil return t, nil
} }
func (t *themeDialogCmp) View() string { func (t *themeDialogComponent) View() string {
currentTheme := theme.CurrentTheme() currentTheme := theme.CurrentTheme()
baseStyle := styles.BaseStyle() baseStyle := styles.BaseStyle()
@@ -185,13 +185,13 @@ func (t *themeDialogCmp) View() string {
Render(content) Render(content)
} }
func (t *themeDialogCmp) BindingKeys() []key.Binding { func (t *themeDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(themeKeys) return layout.KeyMapToSlice(themeKeys)
} }
// NewThemeDialogCmp creates a new theme switching dialog // NewThemeDialogCmp creates a new theme switching dialog
func NewThemeDialogCmp() ThemeDialog { func NewThemeDialogCmp() ThemeDialog {
return &themeDialogCmp{ return &themeDialogComponent{
themes: []string{}, themes: []string{},
selectedIdx: 0, selectedIdx: 0,
currentTheme: "", currentTheme: "",

View File

@@ -1,9 +1,9 @@
package dialog package dialog
import ( import (
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
utilComponents "github.com/sst/opencode/internal/components/util" utilComponents "github.com/sst/opencode/internal/components/util"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
@@ -17,7 +17,7 @@ const (
// ToolsDialog interface for the tools list dialog // ToolsDialog interface for the tools list dialog
type ToolsDialog interface { type ToolsDialog interface {
tea.Model layout.ModelWithView
layout.Bindings layout.Bindings
SetTools(tools []string) SetTools(tools []string)
} }
@@ -53,7 +53,7 @@ func (t toolItem) Render(selected bool, width int) string {
return baseStyle.Render(t.name) return baseStyle.Render(t.name)
} }
type toolsDialogCmp struct { type toolsDialogComponent struct {
tools []toolItem tools []toolItem
width int width int
height int height int
@@ -91,11 +91,11 @@ var toolsKeys = toolsKeyMap{
), ),
} }
func (m *toolsDialogCmp) Init() tea.Cmd { func (m *toolsDialogComponent) Init() tea.Cmd {
return nil return nil
} }
func (m *toolsDialogCmp) SetTools(tools []string) { func (m *toolsDialogComponent) SetTools(tools []string) {
var toolItems []toolItem var toolItems []toolItem
for _, name := range tools { for _, name := range tools {
toolItems = append(toolItems, toolItem{name: name}) toolItems = append(toolItems, toolItem{name: name})
@@ -105,7 +105,7 @@ func (m *toolsDialogCmp) SetTools(tools []string) {
m.list.SetItems(toolItems) m.list.SetItems(toolItems)
} }
func (m *toolsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m *toolsDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
switch { switch {
@@ -130,7 +130,7 @@ func (m *toolsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, cmd return m, cmd
} }
func (m *toolsDialogCmp) View() string { func (m *toolsDialogComponent) View() string {
t := theme.CurrentTheme() t := theme.CurrentTheme()
baseStyle := styles.BaseStyle().Background(t.Background()) baseStyle := styles.BaseStyle().Background(t.Background())
@@ -160,7 +160,7 @@ func (m *toolsDialogCmp) View() string {
Render(content) Render(content)
} }
func (m *toolsDialogCmp) BindingKeys() []key.Binding { func (m *toolsDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(toolsKeys) return layout.KeyMapToSlice(toolsKeys)
} }
@@ -172,7 +172,7 @@ func NewToolsDialogCmp() ToolsDialog {
true, true,
) )
return &toolsDialogCmp{ return &toolsDialogComponent{
list: list, list: list,
} }
} }

View File

@@ -3,6 +3,7 @@ package diff
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"image/color"
"io" "io"
"regexp" "regexp"
"strconv" "strconv"
@@ -12,9 +13,11 @@ import (
"github.com/alecthomas/chroma/v2/formatters" "github.com/alecthomas/chroma/v2/formatters"
"github.com/alecthomas/chroma/v2/lexers" "github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles" "github.com/alecthomas/chroma/v2/styles"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/charmbracelet/lipgloss/v2/compat"
"github.com/charmbracelet/x/ansi" "github.com/charmbracelet/x/ansi"
"github.com/sergi/go-diff/diffmatchpatch" "github.com/sergi/go-diff/diffmatchpatch"
stylesi "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
) )
@@ -300,7 +303,7 @@ func pairLines(lines []DiffLine) []linePair {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// SyntaxHighlight applies syntax highlighting to text based on file extension // SyntaxHighlight applies syntax highlighting to text based on file extension
func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg lipgloss.TerminalColor) error { func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg color.Color) error {
t := theme.CurrentTheme() t := theme.CurrentTheme()
// Determine the language lexer to use // Determine the language lexer to use
@@ -509,15 +512,12 @@ func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg lipglos
} }
// getColor returns the appropriate hex color string based on terminal background // getColor returns the appropriate hex color string based on terminal background
func getColor(adaptiveColor lipgloss.AdaptiveColor) string { func getColor(adaptiveColor compat.AdaptiveColor) string {
if lipgloss.HasDarkBackground() { return stylesi.AdaptiveColorToString(adaptiveColor)
return adaptiveColor.Dark
}
return adaptiveColor.Light
} }
// highlightLine applies syntax highlighting to a single line // highlightLine applies syntax highlighting to a single line
func highlightLine(fileName string, line string, bg lipgloss.TerminalColor) string { func highlightLine(fileName string, line string, bg color.Color) string {
var buf bytes.Buffer var buf bytes.Buffer
err := SyntaxHighlight(&buf, line, fileName, "terminal16m", bg) err := SyntaxHighlight(&buf, line, fileName, "terminal16m", bg)
if err != nil { if err != nil {
@@ -540,7 +540,7 @@ func createStyles(t theme.Theme) (removedLineStyle, addedLineStyle, contextLineS
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// applyHighlighting applies intra-line highlighting to a piece of text // applyHighlighting applies intra-line highlighting to a piece of text
func applyHighlighting(content string, segments []Segment, segmentType LineType, highlightBg lipgloss.AdaptiveColor) string { func applyHighlighting(content string, segments []Segment, segmentType LineType, highlightBg compat.AdaptiveColor) string {
// Find all ANSI sequences in the content // Find all ANSI sequences in the content
ansiRegex := regexp.MustCompile(`\x1b(?:[@-Z\\-_]|\[[0-9?]*(?:;[0-9?]*)*[@-~])`) ansiRegex := regexp.MustCompile(`\x1b(?:[@-Z\\-_]|\[[0-9?]*(?:;[0-9?]*)*[@-~])`)
ansiMatches := ansiRegex.FindAllStringIndex(content, -1) ansiMatches := ansiRegex.FindAllStringIndex(content, -1)
@@ -662,7 +662,7 @@ func renderDiffColumnLine(
var bgStyle lipgloss.Style var bgStyle lipgloss.Style
var lineNum string var lineNum string
var highlightType LineType var highlightType LineType
var highlightColor lipgloss.AdaptiveColor var highlightColor compat.AdaptiveColor
if isLeftColumn { if isLeftColumn {
// Left column logic // Left column logic

View File

@@ -3,7 +3,7 @@ package qr
import ( import (
"strings" "strings"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
"rsc.io/qr" "rsc.io/qr"
) )

View File

@@ -1,9 +1,9 @@
package utilComponents package utilComponents
import ( import (
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout" "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles" "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
@@ -14,7 +14,7 @@ type SimpleListItem interface {
} }
type SimpleList[T SimpleListItem] interface { type SimpleList[T SimpleListItem] interface {
tea.Model layout.ModelWithView
layout.Bindings layout.Bindings
SetMaxWidth(maxWidth int) SetMaxWidth(maxWidth int)
GetSelectedItem() (item T, idx int) GetSelectedItem() (item T, idx int)

View File

@@ -4,11 +4,12 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"image" "image"
"image/color"
"image/png" "image/png"
"os" "os"
"strings" "strings"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"github.com/lucasb-eyer/go-colorful" "github.com/lucasb-eyer/go-colorful"
_ "golang.org/x/image/webp" _ "golang.org/x/image/webp"
@@ -39,7 +40,7 @@ func ToString(width int, img image.Image) string {
c1, _ := colorful.MakeColor(img.At(x, heightCounter)) c1, _ := colorful.MakeColor(img.At(x, heightCounter))
color1 := lipgloss.Color(c1.Hex()) color1 := lipgloss.Color(c1.Hex())
var color2 lipgloss.Color var color2 color.Color
if heightCounter+1 < h { if heightCounter+1 < h {
c2, _ := colorful.MakeColor(img.At(x, heightCounter+1)) c2, _ := colorful.MakeColor(img.At(x, heightCounter+1))
color2 = lipgloss.Color(c2.Hex()) color2 = lipgloss.Color(c2.Hex())

View File

@@ -1,14 +1,19 @@
package layout package layout
import ( import (
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
) )
type Container interface { type ModelWithView interface {
tea.Model tea.Model
tea.ViewModel
}
type Container interface {
ModelWithView
Sizeable Sizeable
Bindings Bindings
Focus() Focus()
@@ -21,7 +26,7 @@ type container struct {
width int width int
height int height int
content tea.Model content ModelWithView
paddingTop int paddingTop int
paddingRight int paddingRight int
@@ -46,7 +51,7 @@ func (c *container) Init() tea.Cmd {
func (c *container) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (c *container) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
u, cmd := c.content.Update(msg) u, cmd := c.content.Update(msg)
c.content = u c.content = u.(ModelWithView)
return c, cmd return c, cmd
} }
@@ -175,7 +180,7 @@ func (c *container) Blur() {
type ContainerOption func(*container) type ContainerOption func(*container)
func NewContainer(content tea.Model, options ...ContainerOption) Container { func NewContainer(content ModelWithView, options ...ContainerOption) Container {
c := &container{ c := &container{
content: content, content: content,
borderStyle: lipgloss.NormalBorder(), borderStyle: lipgloss.NormalBorder(),

View File

@@ -1,10 +1,9 @@
package layout package layout
import ( import (
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/theme"
) )
type FlexDirection int type FlexDirection int
@@ -26,7 +25,7 @@ func FlexPaneSizeFixed(size int) FlexPaneSize {
} }
type FlexLayout interface { type FlexLayout interface {
tea.Model ModelWithView
Sizeable Sizeable
Bindings Bindings
SetPanes(panes []Container) tea.Cmd SetPanes(panes []Container) tea.Cmd
@@ -75,8 +74,6 @@ func (f *flexLayout) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
func (f *flexLayout) View() string { func (f *flexLayout) View() string {
t := theme.CurrentTheme()
if len(f.panes) == 0 { if len(f.panes) == 0 {
return "" return ""
} }
@@ -94,7 +91,6 @@ func (f *flexLayout) View() string {
paneWidth, paneWidth,
pane.Alignment(), pane.Alignment(),
pane.View(), pane.View(),
lipgloss.WithWhitespaceBackground(t.Background()),
) )
views = append(views, view) views = append(views, view)
} else { } else {
@@ -105,7 +101,6 @@ func (f *flexLayout) View() string {
lipgloss.Center, lipgloss.Center,
pane.Alignment(), pane.Alignment(),
pane.View(), pane.View(),
lipgloss.WithWhitespaceBackground(t.Background()),
) )
views = append(views, view) views = append(views, view)
} }
@@ -245,4 +240,3 @@ func WithPaneSizes(sizes ...FlexPaneSize) FlexLayoutOption {
f.sizes = sizes f.sizes = sizes
} }
} }

View File

@@ -3,8 +3,8 @@ package layout
import ( import (
"reflect" "reflect"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
) )
var Current *LayoutInfo var Current *LayoutInfo

View File

@@ -3,7 +3,7 @@ package layout
import ( import (
"strings" "strings"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
chAnsi "github.com/charmbracelet/x/ansi" chAnsi "github.com/charmbracelet/x/ansi"
"github.com/muesli/ansi" "github.com/muesli/ansi"
"github.com/muesli/reflow/truncate" "github.com/muesli/reflow/truncate"
@@ -14,7 +14,7 @@ import (
) )
// Most of this code is borrowed from // Most of this code is borrowed from
// https://github.com/charmbracelet/lipgloss/pull/102 // https://github.com/charmbracelet/lipgloss/v2/pull/102
// as well as the lipgloss library, with some modification for what I needed. // as well as the lipgloss library, with some modification for what I needed.
// Split a string into lines, additionally returning the size of the widest line. // Split a string into lines, additionally returning the size of the widest line.

View File

@@ -4,9 +4,9 @@ import (
"context" "context"
"strings" "strings"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/completions" "github.com/sst/opencode/internal/completions"
"github.com/sst/opencode/internal/components/chat" "github.com/sst/opencode/internal/components/chat"
@@ -187,7 +187,7 @@ func (p *chatPage) BindingKeys() []key.Binding {
return bindings return bindings
} }
func NewChatPage(app *app.App) tea.Model { func NewChatPage(app *app.App) layout.ModelWithView {
cg := completions.NewFileAndFolderContextGroup() cg := completions.NewFileAndFolderContextGroup()
completionDialog := dialog.NewCompletionDialogCmp(cg) completionDialog := dialog.NewCompletionDialogCmp(cg)
messagesContainer := layout.NewContainer( messagesContainer := layout.NewContainer(

View File

@@ -1,123 +1,13 @@
package styles package styles
import ( type TerminalInfo struct {
"fmt" BackgroundIsDark bool
"regexp"
"strings"
"github.com/charmbracelet/lipgloss"
)
var ansiEscape = regexp.MustCompile("\x1b\\[[0-9;]*m")
func getColorRGB(c lipgloss.TerminalColor) (uint8, uint8, uint8) {
r, g, b, a := c.RGBA()
// Un-premultiply alpha if needed
if a > 0 && a < 0xffff {
r = (r * 0xffff) / a
g = (g * 0xffff) / a
b = (b * 0xffff) / a
}
// Convert from 16-bit to 8-bit color
return uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)
} }
// ForceReplaceBackgroundWithLipgloss replaces any ANSI background color codes var Terminal *TerminalInfo
// in `input` with a single 24bit background (48;2;R;G;B).
func ForceReplaceBackgroundWithLipgloss(input string, newBgColor lipgloss.TerminalColor) string {
// Precompute our new-bg sequence once
r, g, b := getColorRGB(newBgColor)
newBg := fmt.Sprintf("48;2;%d;%d;%d", r, g, b)
return ansiEscape.ReplaceAllStringFunc(input, func(seq string) string { func init() {
const ( Terminal = &TerminalInfo{
escPrefixLen = 2 // "\x1b[" BackgroundIsDark: false,
escSuffixLen = 1 // "m"
)
raw := seq
start := escPrefixLen
end := len(raw) - escSuffixLen
var sb strings.Builder
// reserve enough space: original content minus bg codes + our newBg
sb.Grow((end - start) + len(newBg) + 2)
// scan from start..end, token by token
for i := start; i < end; {
// find the next ';' or end
j := i
for j < end && raw[j] != ';' {
j++
} }
token := raw[i:j]
// fastpath: skip "48;5;N" or "48;2;R;G;B"
if len(token) == 2 && token[0] == '4' && token[1] == '8' {
k := j + 1
if k < end {
// find next token
l := k
for l < end && raw[l] != ';' {
l++
}
next := raw[k:l]
if next == "5" {
// skip "48;5;N"
m := l + 1
for m < end && raw[m] != ';' {
m++
}
i = m + 1
continue
} else if next == "2" {
// skip "48;2;R;G;B"
m := l + 1
for count := 0; count < 3 && m < end; count++ {
for m < end && raw[m] != ';' {
m++
}
m++
}
i = m
continue
}
}
}
// decide whether to keep this token
// manually parse ASCII digits to int
isNum := true
val := 0
for p := i; p < j; p++ {
c := raw[p]
if c < '0' || c > '9' {
isNum = false
break
}
val = val*10 + int(c-'0')
}
keep := !isNum ||
((val < 40 || val > 47) && (val < 100 || val > 107) && val != 49)
if keep {
if sb.Len() > 0 {
sb.WriteByte(';')
}
sb.WriteString(token)
}
// advance past this token (and the semicolon)
i = j + 1
}
// append our new background
if sb.Len() > 0 {
sb.WriteByte(';')
}
sb.WriteString(newBg)
return "\x1b[" + sb.String() + "m"
})
} }

View File

@@ -3,7 +3,8 @@ package styles
import ( import (
"github.com/charmbracelet/glamour" "github.com/charmbracelet/glamour"
"github.com/charmbracelet/glamour/ansi" "github.com/charmbracelet/glamour/ansi"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2/compat"
"github.com/lucasb-eyer/go-colorful"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
) )
@@ -15,117 +16,120 @@ func stringPtr(s string) *string { return &s }
func uintPtr(u uint) *uint { return &u } func uintPtr(u uint) *uint { return &u }
// returns a glamour TermRenderer configured with the current theme // returns a glamour TermRenderer configured with the current theme
func GetMarkdownRenderer(width int) *glamour.TermRenderer { func GetMarkdownRenderer(width int, backgroundColor compat.AdaptiveColor) *glamour.TermRenderer {
r, _ := glamour.NewTermRenderer( r, _ := glamour.NewTermRenderer(
glamour.WithStyles(generateMarkdownStyleConfig()), glamour.WithStyles(generateMarkdownStyleConfig(backgroundColor)),
glamour.WithWordWrap(width), glamour.WithWordWrap(width),
glamour.WithChromaFormatter("terminal16m"),
) )
return r return r
} }
// creates an ansi.StyleConfig for markdown rendering // creates an ansi.StyleConfig for markdown rendering
// using adaptive colors from the provided theme. // using adaptive colors from the provided theme.
func generateMarkdownStyleConfig() ansi.StyleConfig { func generateMarkdownStyleConfig(backgroundColor compat.AdaptiveColor) ansi.StyleConfig {
t := theme.CurrentTheme() t := theme.CurrentTheme()
background := stringPtr(AdaptiveColorToString(backgroundColor))
return ansi.StyleConfig{ return ansi.StyleConfig{
Document: ansi.StyleBlock{ Document: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
BlockPrefix: "", BlockPrefix: "",
BlockSuffix: "", BlockSuffix: "",
Color: stringPtr(adaptiveColorToString(t.MarkdownText())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
}, },
}, },
BlockQuote: ansi.StyleBlock{ BlockQuote: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownBlockQuote())), Color: stringPtr(AdaptiveColorToString(t.MarkdownBlockQuote())),
Italic: boolPtr(true), Italic: boolPtr(true),
Prefix: "┃ ", Prefix: "┃ ",
}, },
Indent: uintPtr(1), Indent: uintPtr(1),
IndentToken: stringPtr(BaseStyle().Render(" ")), IndentToken: stringPtr(" "),
}, },
List: ansi.StyleList{ List: ansi.StyleList{
LevelIndent: defaultMargin, LevelIndent: defaultMargin,
StyleBlock: ansi.StyleBlock{ StyleBlock: ansi.StyleBlock{
IndentToken: stringPtr(BaseStyle().Render(" ")), IndentToken: stringPtr(" "),
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownText())), Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
}, },
}, },
}, },
Heading: ansi.StyleBlock{ Heading: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
BlockSuffix: "\n", BlockSuffix: "\n",
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
Bold: boolPtr(true), Bold: boolPtr(true),
}, },
}, },
H1: ansi.StyleBlock{ H1: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Prefix: "# ", Prefix: "# ",
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
Bold: boolPtr(true), Bold: boolPtr(true),
}, },
}, },
H2: ansi.StyleBlock{ H2: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Prefix: "## ", Prefix: "## ",
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
Bold: boolPtr(true), Bold: boolPtr(true),
}, },
}, },
H3: ansi.StyleBlock{ H3: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Prefix: "### ", Prefix: "### ",
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
Bold: boolPtr(true), Bold: boolPtr(true),
}, },
}, },
H4: ansi.StyleBlock{ H4: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Prefix: "#### ", Prefix: "#### ",
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
Bold: boolPtr(true), Bold: boolPtr(true),
}, },
}, },
H5: ansi.StyleBlock{ H5: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Prefix: "##### ", Prefix: "##### ",
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
Bold: boolPtr(true), Bold: boolPtr(true),
}, },
}, },
H6: ansi.StyleBlock{ H6: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Prefix: "###### ", Prefix: "###### ",
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
Bold: boolPtr(true), Bold: boolPtr(true),
}, },
}, },
Strikethrough: ansi.StylePrimitive{ Strikethrough: ansi.StylePrimitive{
CrossedOut: boolPtr(true), CrossedOut: boolPtr(true),
Color: stringPtr(adaptiveColorToString(t.TextMuted())), Color: stringPtr(AdaptiveColorToString(t.TextMuted())),
}, },
Emph: ansi.StylePrimitive{ Emph: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownEmph())), Color: stringPtr(AdaptiveColorToString(t.MarkdownEmph())),
Italic: boolPtr(true), Italic: boolPtr(true),
}, },
Strong: ansi.StylePrimitive{ Strong: ansi.StylePrimitive{
Bold: boolPtr(true), Bold: boolPtr(true),
Color: stringPtr(adaptiveColorToString(t.MarkdownStrong())), Color: stringPtr(AdaptiveColorToString(t.MarkdownStrong())),
}, },
HorizontalRule: ansi.StylePrimitive{ HorizontalRule: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownHorizontalRule())), Color: stringPtr(AdaptiveColorToString(t.MarkdownHorizontalRule())),
Format: "\n─────────────────────────────────────────\n", Format: "\n─────────────────────────────────────────\n",
}, },
Item: ansi.StylePrimitive{ Item: ansi.StylePrimitive{
BlockPrefix: "• ", BlockPrefix: "• ",
Color: stringPtr(adaptiveColorToString(t.MarkdownListItem())), Color: stringPtr(AdaptiveColorToString(t.MarkdownListItem())),
}, },
Enumeration: ansi.StylePrimitive{ Enumeration: ansi.StylePrimitive{
BlockPrefix: ". ", BlockPrefix: ". ",
Color: stringPtr(adaptiveColorToString(t.MarkdownListEnumeration())), Color: stringPtr(AdaptiveColorToString(t.MarkdownListEnumeration())),
}, },
Task: ansi.StyleTask{ Task: ansi.StyleTask{
StylePrimitive: ansi.StylePrimitive{}, StylePrimitive: ansi.StylePrimitive{},
@@ -133,25 +137,26 @@ func generateMarkdownStyleConfig() ansi.StyleConfig {
Unticked: "[ ] ", Unticked: "[ ] ",
}, },
Link: ansi.StylePrimitive{ Link: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownLink())), Color: stringPtr(AdaptiveColorToString(t.MarkdownLink())),
Underline: boolPtr(true), Underline: boolPtr(true),
}, },
LinkText: ansi.StylePrimitive{ LinkText: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownLinkText())), Color: stringPtr(AdaptiveColorToString(t.MarkdownLinkText())),
Bold: boolPtr(true), Bold: boolPtr(true),
}, },
Image: ansi.StylePrimitive{ Image: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownImage())), Color: stringPtr(AdaptiveColorToString(t.MarkdownImage())),
Underline: boolPtr(true), Underline: boolPtr(true),
Format: "🖼 {{.text}}", Format: "🖼 {{.text}}",
}, },
ImageText: ansi.StylePrimitive{ ImageText: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownImageText())), Color: stringPtr(AdaptiveColorToString(t.MarkdownImageText())),
Format: "{{.text}}", Format: "{{.text}}",
}, },
Code: ansi.StyleBlock{ Code: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownCode())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.MarkdownCode())),
Prefix: "", Prefix: "",
Suffix: "", Suffix: "",
}, },
@@ -159,90 +164,120 @@ func generateMarkdownStyleConfig() ansi.StyleConfig {
CodeBlock: ansi.StyleCodeBlock{ CodeBlock: ansi.StyleCodeBlock{
StyleBlock: ansi.StyleBlock{ StyleBlock: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
BackgroundColor: background,
Prefix: " ", Prefix: " ",
Color: stringPtr(adaptiveColorToString(t.MarkdownCodeBlock())), Color: stringPtr(AdaptiveColorToString(t.MarkdownCodeBlock())),
}, },
}, },
Chroma: &ansi.Chroma{ Chroma: &ansi.Chroma{
Background: ansi.StylePrimitive{
BackgroundColor: background,
},
Text: ansi.StylePrimitive{ Text: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownText())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
}, },
Error: ansi.StylePrimitive{ Error: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.Error())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.Error())),
}, },
Comment: ansi.StylePrimitive{ Comment: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxComment())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxComment())),
}, },
CommentPreproc: ansi.StylePrimitive{ CommentPreproc: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
}, },
Keyword: ansi.StylePrimitive{ Keyword: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
}, },
KeywordReserved: ansi.StylePrimitive{ KeywordReserved: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
}, },
KeywordNamespace: ansi.StylePrimitive{ KeywordNamespace: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
}, },
KeywordType: ansi.StylePrimitive{ KeywordType: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxType())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxType())),
}, },
Operator: ansi.StylePrimitive{ Operator: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxOperator())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxOperator())),
}, },
Punctuation: ansi.StylePrimitive{ Punctuation: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxPunctuation())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxPunctuation())),
}, },
Name: ansi.StylePrimitive{ Name: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxVariable())),
}, },
NameBuiltin: ansi.StylePrimitive{ NameBuiltin: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxVariable())),
}, },
NameTag: ansi.StylePrimitive{ NameTag: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
}, },
NameAttribute: ansi.StylePrimitive{ NameAttribute: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxFunction())),
}, },
NameClass: ansi.StylePrimitive{ NameClass: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxType())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxType())),
}, },
NameConstant: ansi.StylePrimitive{ NameConstant: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxVariable())),
}, },
NameDecorator: ansi.StylePrimitive{ NameDecorator: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxFunction())),
}, },
NameFunction: ansi.StylePrimitive{ NameFunction: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxFunction())),
}, },
LiteralNumber: ansi.StylePrimitive{ LiteralNumber: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxNumber())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxNumber())),
}, },
LiteralString: ansi.StylePrimitive{ LiteralString: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxString())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxString())),
}, },
LiteralStringEscape: ansi.StylePrimitive{ LiteralStringEscape: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.SyntaxKeyword())),
}, },
GenericDeleted: ansi.StylePrimitive{ GenericDeleted: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.DiffRemoved())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.DiffRemoved())),
}, },
GenericEmph: ansi.StylePrimitive{ GenericEmph: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownEmph())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.MarkdownEmph())),
Italic: boolPtr(true), Italic: boolPtr(true),
}, },
GenericInserted: ansi.StylePrimitive{ GenericInserted: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.DiffAdded())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.DiffAdded())),
}, },
GenericStrong: ansi.StylePrimitive{ GenericStrong: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownStrong())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.MarkdownStrong())),
Bold: boolPtr(true), Bold: boolPtr(true),
}, },
GenericSubheading: ansi.StylePrimitive{ GenericSubheading: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())), BackgroundColor: background,
Color: stringPtr(AdaptiveColorToString(t.MarkdownHeading())),
}, },
}, },
}, },
@@ -259,24 +294,26 @@ func generateMarkdownStyleConfig() ansi.StyleConfig {
}, },
DefinitionDescription: ansi.StylePrimitive{ DefinitionDescription: ansi.StylePrimitive{
BlockPrefix: "\n ", BlockPrefix: "\n ",
Color: stringPtr(adaptiveColorToString(t.MarkdownLinkText())), Color: stringPtr(AdaptiveColorToString(t.MarkdownLinkText())),
}, },
Text: ansi.StylePrimitive{ Text: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownText())), Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
}, },
Paragraph: ansi.StyleBlock{ Paragraph: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{ StylePrimitive: ansi.StylePrimitive{
Color: stringPtr(adaptiveColorToString(t.MarkdownText())), Color: stringPtr(AdaptiveColorToString(t.MarkdownText())),
}, },
}, },
} }
} }
// adaptiveColorToString converts a lipgloss.AdaptiveColor to the appropriate // AdaptiveColorToString converts a compat.AdaptiveColor to the appropriate
// hex color string based on the current terminal background // hex color string based on the current terminal background
func adaptiveColorToString(color lipgloss.AdaptiveColor) string { func AdaptiveColorToString(color compat.AdaptiveColor) string {
if lipgloss.HasDarkBackground() { if Terminal.BackgroundIsDark {
return color.Dark c1, _ := colorful.MakeColor(color.Dark)
return c1.Hex()
} }
return color.Light c1, _ := colorful.MakeColor(color.Light)
return c1.Hex()
} }

View File

@@ -1,7 +1,8 @@
package styles package styles
import ( import (
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/charmbracelet/lipgloss/v2/compat"
"github.com/sst/opencode/internal/theme" "github.com/sst/opencode/internal/theme"
) )
@@ -83,76 +84,76 @@ func DimBorder() lipgloss.Style {
} }
// PrimaryColor returns the primary color from the current theme // PrimaryColor returns the primary color from the current theme
func PrimaryColor() lipgloss.AdaptiveColor { func PrimaryColor() compat.AdaptiveColor {
return theme.CurrentTheme().Primary() return theme.CurrentTheme().Primary()
} }
// SecondaryColor returns the secondary color from the current theme // SecondaryColor returns the secondary color from the current theme
func SecondaryColor() lipgloss.AdaptiveColor { func SecondaryColor() compat.AdaptiveColor {
return theme.CurrentTheme().Secondary() return theme.CurrentTheme().Secondary()
} }
// AccentColor returns the accent color from the current theme // AccentColor returns the accent color from the current theme
func AccentColor() lipgloss.AdaptiveColor { func AccentColor() compat.AdaptiveColor {
return theme.CurrentTheme().Accent() return theme.CurrentTheme().Accent()
} }
// ErrorColor returns the error color from the current theme // ErrorColor returns the error color from the current theme
func ErrorColor() lipgloss.AdaptiveColor { func ErrorColor() compat.AdaptiveColor {
return theme.CurrentTheme().Error() return theme.CurrentTheme().Error()
} }
// WarningColor returns the warning color from the current theme // WarningColor returns the warning color from the current theme
func WarningColor() lipgloss.AdaptiveColor { func WarningColor() compat.AdaptiveColor {
return theme.CurrentTheme().Warning() return theme.CurrentTheme().Warning()
} }
// SuccessColor returns the success color from the current theme // SuccessColor returns the success color from the current theme
func SuccessColor() lipgloss.AdaptiveColor { func SuccessColor() compat.AdaptiveColor {
return theme.CurrentTheme().Success() return theme.CurrentTheme().Success()
} }
// InfoColor returns the info color from the current theme // InfoColor returns the info color from the current theme
func InfoColor() lipgloss.AdaptiveColor { func InfoColor() compat.AdaptiveColor {
return theme.CurrentTheme().Info() return theme.CurrentTheme().Info()
} }
// TextColor returns the text color from the current theme // TextColor returns the text color from the current theme
func TextColor() lipgloss.AdaptiveColor { func TextColor() compat.AdaptiveColor {
return theme.CurrentTheme().Text() return theme.CurrentTheme().Text()
} }
// TextMutedColor returns the muted text color from the current theme // TextMutedColor returns the muted text color from the current theme
func TextMutedColor() lipgloss.AdaptiveColor { func TextMutedColor() compat.AdaptiveColor {
return theme.CurrentTheme().TextMuted() return theme.CurrentTheme().TextMuted()
} }
// BackgroundColor returns the background color from the current theme // BackgroundColor returns the background color from the current theme
func BackgroundColor() lipgloss.AdaptiveColor { func BackgroundColor() compat.AdaptiveColor {
return theme.CurrentTheme().Background() return theme.CurrentTheme().Background()
} }
// BackgroundSubtleColor returns the subtle background color from the current theme // BackgroundSubtleColor returns the subtle background color from the current theme
func BackgroundSubtleColor() lipgloss.AdaptiveColor { func BackgroundSubtleColor() compat.AdaptiveColor {
return theme.CurrentTheme().BackgroundSubtle() return theme.CurrentTheme().BackgroundSubtle()
} }
// BackgroundElementColor returns the darker background color from the current theme // BackgroundElementColor returns the darker background color from the current theme
func BackgroundElementColor() lipgloss.AdaptiveColor { func BackgroundElementColor() compat.AdaptiveColor {
return theme.CurrentTheme().BackgroundElement() return theme.CurrentTheme().BackgroundElement()
} }
// BorderColor returns the border color from the current theme // BorderColor returns the border color from the current theme
func BorderColor() lipgloss.AdaptiveColor { func BorderColor() compat.AdaptiveColor {
return theme.CurrentTheme().Border() return theme.CurrentTheme().Border()
} }
// BorderActiveColor returns the active border color from the current theme // BorderActiveColor returns the active border color from the current theme
func BorderActiveColor() lipgloss.AdaptiveColor { func BorderActiveColor() compat.AdaptiveColor {
return theme.CurrentTheme().BorderActive() return theme.CurrentTheme().BorderActive()
} }
// BorderSubtleColor returns the subtle border color from the current theme // BorderSubtleColor returns the subtle border color from the current theme
func BorderSubtleColor() lipgloss.AdaptiveColor { func BorderSubtleColor() compat.AdaptiveColor {
return theme.CurrentTheme().BorderSubtle() return theme.CurrentTheme().BorderSubtle()
} }

View File

@@ -1,276 +0,0 @@
package theme
import (
"github.com/charmbracelet/lipgloss"
)
// AyuDarkTheme implements the Theme interface with Ayu Dark colors.
type AyuDarkTheme struct {
BaseTheme
}
// AyuLightTheme implements the Theme interface with Ayu Light colors.
type AyuLightTheme struct {
BaseTheme
}
// AyuMirageTheme implements the Theme interface with Ayu Mirage colors.
type AyuMirageTheme struct {
BaseTheme
}
// NewAyuDarkTheme creates a new instance of the Ayu Dark theme.
func NewAyuDarkTheme() *AyuDarkTheme {
// Ayu Dark color palette
darkBackground := "#0f1419"
darkCurrentLine := "#191f26"
darkSelection := "#253340"
darkForeground := "#b3b1ad"
darkComment := "#5c6773"
darkBlue := "#53bdfa"
darkCyan := "#90e1c6"
darkGreen := "#91b362"
darkOrange := "#f9af4f"
darkPurple := "#fae994"
darkRed := "#ea6c73"
darkBorder := "#253340"
// Light mode approximation for terminal compatibility
lightBackground := "#fafafa"
lightCurrentLine := "#f0f0f0"
lightSelection := "#d1d1d1"
lightForeground := "#5c6773"
lightComment := "#828c99"
lightBlue := "#3199e1"
lightCyan := "#46ba94"
lightGreen := "#7c9f32"
lightOrange := "#f29718"
lightPurple := "#9e75c7"
lightRed := "#f07171"
lightBorder := "#d1d1d1"
theme := &AyuDarkTheme{}
// Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.SecondaryColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.AccentColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
// Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.WarningColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.SuccessColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.InfoColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
// Text colors
theme.TextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.TextMutedColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
// Background colors
theme.BackgroundColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
Dark: darkCurrentLine,
Light: lightCurrentLine,
}
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
Dark: "#0b0e14", // Darker than background
Light: "#ffffff", // Lighter than background
}
// Border colors
theme.BorderColor = lipgloss.AdaptiveColor{
Dark: darkBorder,
Light: lightBorder,
}
theme.BorderActiveColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
Dark: darkSelection,
Light: lightSelection,
}
// Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.DiffContextColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
Dark: "#91b362",
Light: "#a5d6a7",
}
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
Dark: "#ea6c73",
Light: "#ef9a9a",
}
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
Dark: "#1f2c1f",
Light: "#e8f5e9",
}
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
Dark: "#2c1f1f",
Light: "#ffebee",
}
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#1a261a",
Light: "#c8e6c9",
}
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#261a1a",
Light: "#ffcdd2",
}
// Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
// Syntax highlighting colors
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
return theme
}
func init() {
// Register all three Ayu theme variants with the theme manager
RegisterTheme("ayu", NewAyuDarkTheme())
}

View File

@@ -1,244 +0,0 @@
package theme
import (
catppuccin "github.com/catppuccin/go"
"github.com/charmbracelet/lipgloss"
)
// CatppuccinTheme implements the Theme interface with Catppuccin colors.
// It provides both dark (Mocha) and light (Latte) variants.
type CatppuccinTheme struct {
BaseTheme
}
// NewCatppuccinTheme creates a new instance of the Catppuccin theme.
func NewCatppuccinTheme() *CatppuccinTheme {
// Get the Catppuccin palettes
mocha := catppuccin.Mocha
latte := catppuccin.Latte
theme := &CatppuccinTheme{}
// Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{
Dark: mocha.Blue().Hex,
Light: latte.Blue().Hex,
}
theme.SecondaryColor = lipgloss.AdaptiveColor{
Dark: mocha.Mauve().Hex,
Light: latte.Mauve().Hex,
}
theme.AccentColor = lipgloss.AdaptiveColor{
Dark: mocha.Peach().Hex,
Light: latte.Peach().Hex,
}
// Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{
Dark: mocha.Red().Hex,
Light: latte.Red().Hex,
}
theme.WarningColor = lipgloss.AdaptiveColor{
Dark: mocha.Peach().Hex,
Light: latte.Peach().Hex,
}
theme.SuccessColor = lipgloss.AdaptiveColor{
Dark: mocha.Green().Hex,
Light: latte.Green().Hex,
}
theme.InfoColor = lipgloss.AdaptiveColor{
Dark: mocha.Blue().Hex,
Light: latte.Blue().Hex,
}
// Text colors
theme.TextColor = lipgloss.AdaptiveColor{
Dark: mocha.Text().Hex,
Light: latte.Text().Hex,
}
theme.TextMutedColor = lipgloss.AdaptiveColor{
Dark: mocha.Subtext0().Hex,
Light: latte.Subtext0().Hex,
}
// Background colors
theme.BackgroundColor = lipgloss.AdaptiveColor{
Dark: "#212121", // From existing styles
Light: "#EEEEEE", // Light equivalent
}
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
Dark: "#2c2c2c", // From existing styles
Light: "#E0E0E0", // Light equivalent
}
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
Dark: "#181818", // From existing styles
Light: "#F5F5F5", // Light equivalent
}
// Border colors
theme.BorderColor = lipgloss.AdaptiveColor{
Dark: "#4b4c5c", // From existing styles
Light: "#BDBDBD", // Light equivalent
}
theme.BorderActiveColor = lipgloss.AdaptiveColor{
Dark: mocha.Blue().Hex,
Light: latte.Blue().Hex,
}
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
Dark: mocha.Surface0().Hex,
Light: latte.Surface0().Hex,
}
// Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{
Dark: "#478247", // From existing diff.go
Light: "#2E7D32", // Light equivalent
}
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
Dark: "#7C4444", // From existing diff.go
Light: "#C62828", // Light equivalent
}
theme.DiffContextColor = lipgloss.AdaptiveColor{
Dark: "#a0a0a0", // From existing diff.go
Light: "#757575", // Light equivalent
}
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
Dark: "#a0a0a0", // From existing diff.go
Light: "#757575", // Light equivalent
}
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
Dark: "#DAFADA", // From existing diff.go
Light: "#A5D6A7", // Light equivalent
}
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
Dark: "#FADADD", // From existing diff.go
Light: "#EF9A9A", // Light equivalent
}
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
Dark: "#303A30", // From existing diff.go
Light: "#E8F5E9", // Light equivalent
}
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
Dark: "#3A3030", // From existing diff.go
Light: "#FFEBEE", // Light equivalent
}
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
Dark: "#212121", // From existing diff.go
Light: "#F5F5F5", // Light equivalent
}
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
Dark: "#888888", // From existing diff.go
Light: "#9E9E9E", // Light equivalent
}
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#293229", // From existing diff.go
Light: "#C8E6C9", // Light equivalent
}
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#332929", // From existing diff.go
Light: "#FFCDD2", // Light equivalent
}
// Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
Dark: mocha.Text().Hex,
Light: latte.Text().Hex,
}
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
Dark: mocha.Mauve().Hex,
Light: latte.Mauve().Hex,
}
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
Dark: mocha.Sky().Hex,
Light: latte.Sky().Hex,
}
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
Dark: mocha.Pink().Hex,
Light: latte.Pink().Hex,
}
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
Dark: mocha.Green().Hex,
Light: latte.Green().Hex,
}
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
Dark: mocha.Yellow().Hex,
Light: latte.Yellow().Hex,
}
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
Dark: mocha.Yellow().Hex,
Light: latte.Yellow().Hex,
}
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
Dark: mocha.Peach().Hex,
Light: latte.Peach().Hex,
}
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
Dark: mocha.Overlay0().Hex,
Light: latte.Overlay0().Hex,
}
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
Dark: mocha.Blue().Hex,
Light: latte.Blue().Hex,
}
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
Dark: mocha.Sky().Hex,
Light: latte.Sky().Hex,
}
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
Dark: mocha.Sapphire().Hex,
Light: latte.Sapphire().Hex,
}
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
Dark: mocha.Pink().Hex,
Light: latte.Pink().Hex,
}
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
Dark: mocha.Text().Hex,
Light: latte.Text().Hex,
}
// Syntax highlighting colors
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
Dark: mocha.Overlay1().Hex,
Light: latte.Overlay1().Hex,
}
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
Dark: mocha.Pink().Hex,
Light: latte.Pink().Hex,
}
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
Dark: mocha.Green().Hex,
Light: latte.Green().Hex,
}
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
Dark: mocha.Sky().Hex,
Light: latte.Sky().Hex,
}
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
Dark: mocha.Yellow().Hex,
Light: latte.Yellow().Hex,
}
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
Dark: mocha.Teal().Hex,
Light: latte.Teal().Hex,
}
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
Dark: mocha.Sky().Hex,
Light: latte.Sky().Hex,
}
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
Dark: mocha.Pink().Hex,
Light: latte.Pink().Hex,
}
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
Dark: mocha.Text().Hex,
Light: latte.Text().Hex,
}
return theme
}
func init() {
// Register the Catppuccin theme with the theme manager
RegisterTheme("catppuccin", NewCatppuccinTheme())
}

View File

@@ -1,270 +0,0 @@
package theme
import (
"github.com/charmbracelet/lipgloss"
)
// DraculaTheme implements the Theme interface with Dracula colors.
// It provides both dark and light variants, though Dracula is primarily a dark theme.
type DraculaTheme struct {
BaseTheme
}
// NewDraculaTheme creates a new instance of the Dracula theme.
func NewDraculaTheme() *DraculaTheme {
// Dracula color palette
// Official colors from https://draculatheme.com/
darkBackground := "#282a36"
darkCurrentLine := "#44475a"
darkSelection := "#44475a"
darkForeground := "#f8f8f2"
darkComment := "#6272a4"
darkCyan := "#8be9fd"
darkGreen := "#50fa7b"
darkOrange := "#ffb86c"
darkPink := "#ff79c6"
darkPurple := "#bd93f9"
darkRed := "#ff5555"
darkYellow := "#f1fa8c"
darkBorder := "#44475a"
// Light mode approximation (Dracula is primarily a dark theme)
lightBackground := "#f8f8f2"
lightCurrentLine := "#e6e6e6"
lightSelection := "#d8d8d8"
lightForeground := "#282a36"
lightComment := "#6272a4"
lightCyan := "#0097a7"
lightGreen := "#388e3c"
lightOrange := "#f57c00"
lightPink := "#d81b60"
lightPurple := "#7e57c2"
lightRed := "#e53935"
lightYellow := "#fbc02d"
lightBorder := "#d8d8d8"
theme := &DraculaTheme{}
// Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.SecondaryColor = lipgloss.AdaptiveColor{
Dark: darkPink,
Light: lightPink,
}
theme.AccentColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
// Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.WarningColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.SuccessColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.InfoColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
// Text colors
theme.TextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.TextMutedColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
// Background colors
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
Dark: darkCurrentLine,
Light: lightCurrentLine,
}
theme.BackgroundColor = lipgloss.AdaptiveColor{
Dark: "#21222c", // Slightly darker than background
Light: "#ffffff", // Slightly lighter than background
}
// Border colors
theme.BorderColor = lipgloss.AdaptiveColor{
Dark: darkBorder,
Light: lightBorder,
}
theme.BorderActiveColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
Dark: darkSelection,
Light: lightSelection,
}
// Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.DiffContextColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
Dark: darkCurrentLine,
Light: lightCurrentLine,
}
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
Dark: "#50fa7b",
Light: "#a5d6a7",
}
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
Dark: "#ff5555",
Light: "#ef9a9a",
}
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
Dark: "#2c3b2c",
Light: "#e8f5e9",
}
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
Dark: "#3b2c2c",
Light: "#ffebee",
}
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#253025",
Light: "#c8e6c9",
}
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#302525",
Light: "#ffcdd2",
}
// Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
Dark: darkPink,
Light: lightPink,
}
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
// Syntax highlighting colors
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
Dark: darkPink,
Light: lightPink,
}
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
Dark: darkPink,
Light: lightPink,
}
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
return theme
}
func init() {
// Register the Dracula theme with the theme manager
RegisterTheme("dracula", NewDraculaTheme())
}

View File

@@ -1,278 +0,0 @@
package theme
import (
"github.com/charmbracelet/lipgloss"
)
// Flexoki color palette constants
const (
// Base colors
flexokiPaper = "#FFFCF0" // Paper (lightest)
flexokiBase50 = "#F2F0E5" // bg-2 (light)
flexokiBase100 = "#E6E4D9" // ui (light)
flexokiBase150 = "#DAD8CE" // ui-2 (light)
flexokiBase200 = "#CECDC3" // ui-3 (light)
flexokiBase300 = "#B7B5AC" // tx-3 (light)
flexokiBase500 = "#878580" // tx-2 (light)
flexokiBase600 = "#6F6E69" // tx (light)
flexokiBase700 = "#575653" // tx-3 (dark)
flexokiBase800 = "#403E3C" // ui-3 (dark)
flexokiBase850 = "#343331" // ui-2 (dark)
flexokiBase900 = "#282726" // ui (dark)
flexokiBase950 = "#1C1B1A" // bg-2 (dark)
flexokiBlack = "#100F0F" // bg (darkest)
// Accent colors - Light theme (600)
flexokiRed600 = "#AF3029"
flexokiOrange600 = "#BC5215"
flexokiYellow600 = "#AD8301"
flexokiGreen600 = "#66800B"
flexokiCyan600 = "#24837B"
flexokiBlue600 = "#205EA6"
flexokiPurple600 = "#5E409D"
flexokiMagenta600 = "#A02F6F"
// Accent colors - Dark theme (400)
flexokiRed400 = "#D14D41"
flexokiOrange400 = "#DA702C"
flexokiYellow400 = "#D0A215"
flexokiGreen400 = "#879A39"
flexokiCyan400 = "#3AA99F"
flexokiBlue400 = "#4385BE"
flexokiPurple400 = "#8B7EC8"
flexokiMagenta400 = "#CE5D97"
)
// FlexokiTheme implements the Theme interface with Flexoki colors.
// It provides both dark and light variants.
type FlexokiTheme struct {
BaseTheme
}
// NewFlexokiTheme creates a new instance of the Flexoki theme.
func NewFlexokiTheme() *FlexokiTheme {
theme := &FlexokiTheme{}
// Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{
Dark: flexokiBlue400,
Light: flexokiBlue600,
}
theme.SecondaryColor = lipgloss.AdaptiveColor{
Dark: flexokiPurple400,
Light: flexokiPurple600,
}
theme.AccentColor = lipgloss.AdaptiveColor{
Dark: flexokiOrange400,
Light: flexokiOrange600,
}
// Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{
Dark: flexokiRed400,
Light: flexokiRed600,
}
theme.WarningColor = lipgloss.AdaptiveColor{
Dark: flexokiYellow400,
Light: flexokiYellow600,
}
theme.SuccessColor = lipgloss.AdaptiveColor{
Dark: flexokiGreen400,
Light: flexokiGreen600,
}
theme.InfoColor = lipgloss.AdaptiveColor{
Dark: flexokiCyan400,
Light: flexokiCyan600,
}
// Text colors
theme.TextColor = lipgloss.AdaptiveColor{
Dark: flexokiBase300,
Light: flexokiBase600,
}
theme.TextMutedColor = lipgloss.AdaptiveColor{
Dark: flexokiBase700,
Light: flexokiBase500,
}
// Background colors
theme.BackgroundColor = lipgloss.AdaptiveColor{
Dark: flexokiBlack,
Light: flexokiPaper,
}
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
Dark: flexokiBase950,
Light: flexokiBase50,
}
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
Dark: flexokiBase900,
Light: flexokiBase100,
}
// Border colors
theme.BorderColor = lipgloss.AdaptiveColor{
Dark: flexokiBase900,
Light: flexokiBase100,
}
theme.BorderActiveColor = lipgloss.AdaptiveColor{
Dark: flexokiBlue400,
Light: flexokiBlue600,
}
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
Dark: flexokiBase850,
Light: flexokiBase150,
}
// Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{
Dark: flexokiGreen400,
Light: flexokiGreen600,
}
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
Dark: flexokiRed400,
Light: flexokiRed600,
}
theme.DiffContextColor = lipgloss.AdaptiveColor{
Dark: flexokiBase700,
Light: flexokiBase500,
}
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
Dark: flexokiBase700,
Light: flexokiBase500,
}
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
Dark: flexokiGreen400,
Light: flexokiGreen600,
}
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
Dark: flexokiRed400,
Light: flexokiRed600,
}
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
Dark: "#1D2419", // Darker green background
Light: "#EFF2E2", // Light green background
}
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
Dark: "#241919", // Darker red background
Light: "#F2E2E2", // Light red background
}
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
Dark: flexokiBlack,
Light: flexokiPaper,
}
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
Dark: flexokiBase700,
Light: flexokiBase500,
}
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#1A2017", // Slightly darker green
Light: "#E5EBD9", // Light green
}
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#201717", // Slightly darker red
Light: "#EBD9D9", // Light red
}
// Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
Dark: flexokiBase300,
Light: flexokiBase600,
}
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
Dark: flexokiYellow400,
Light: flexokiYellow600,
}
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
Dark: flexokiCyan400,
Light: flexokiCyan600,
}
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
Dark: flexokiMagenta400,
Light: flexokiMagenta600,
}
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
Dark: flexokiGreen400,
Light: flexokiGreen600,
}
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
Dark: flexokiCyan400,
Light: flexokiCyan600,
}
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
Dark: flexokiYellow400,
Light: flexokiYellow600,
}
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
Dark: flexokiOrange400,
Light: flexokiOrange600,
}
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
Dark: flexokiBase800,
Light: flexokiBase200,
}
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
Dark: flexokiBlue400,
Light: flexokiBlue600,
}
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
Dark: flexokiBlue400,
Light: flexokiBlue600,
}
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
Dark: flexokiPurple400,
Light: flexokiPurple600,
}
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
Dark: flexokiMagenta400,
Light: flexokiMagenta600,
}
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
Dark: flexokiBase300,
Light: flexokiBase600,
}
// Syntax highlighting colors (based on Flexoki's mappings)
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
Dark: flexokiBase700, // tx-3
Light: flexokiBase300, // tx-3
}
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
Dark: flexokiGreen400, // gr
Light: flexokiGreen600, // gr
}
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
Dark: flexokiOrange400, // or
Light: flexokiOrange600, // or
}
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
Dark: flexokiBlue400, // bl
Light: flexokiBlue600, // bl
}
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
Dark: flexokiCyan400, // cy
Light: flexokiCyan600, // cy
}
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
Dark: flexokiPurple400, // pu
Light: flexokiPurple600, // pu
}
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
Dark: flexokiYellow400, // ye
Light: flexokiYellow600, // ye
}
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
Dark: flexokiBase500, // tx-2
Light: flexokiBase500, // tx-2
}
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
Dark: flexokiBase500, // tx-2
Light: flexokiBase500, // tx-2
}
return theme
}
func init() {
// Register the Flexoki theme with the theme manager
RegisterTheme("flexoki", NewFlexokiTheme())
}

View File

@@ -1,298 +0,0 @@
package theme
import (
"github.com/charmbracelet/lipgloss"
)
// Gruvbox color palette constants
const (
// Dark theme colors
gruvboxDarkBg0 = "#282828"
gruvboxDarkBg0Soft = "#32302f"
gruvboxDarkBg1 = "#3c3836"
gruvboxDarkBg2 = "#504945"
gruvboxDarkBg3 = "#665c54"
gruvboxDarkBg4 = "#7c6f64"
gruvboxDarkFg0 = "#fbf1c7"
gruvboxDarkFg1 = "#ebdbb2"
gruvboxDarkFg2 = "#d5c4a1"
gruvboxDarkFg3 = "#bdae93"
gruvboxDarkFg4 = "#a89984"
gruvboxDarkGray = "#928374"
gruvboxDarkRed = "#cc241d"
gruvboxDarkRedBright = "#fb4934"
gruvboxDarkGreen = "#98971a"
gruvboxDarkGreenBright = "#b8bb26"
gruvboxDarkYellow = "#d79921"
gruvboxDarkYellowBright = "#fabd2f"
gruvboxDarkBlue = "#458588"
gruvboxDarkBlueBright = "#83a598"
gruvboxDarkPurple = "#b16286"
gruvboxDarkPurpleBright = "#d3869b"
gruvboxDarkAqua = "#689d6a"
gruvboxDarkAquaBright = "#8ec07c"
gruvboxDarkOrange = "#d65d0e"
gruvboxDarkOrangeBright = "#fe8019"
// Light theme colors
gruvboxLightBg0 = "#fbf1c7"
gruvboxLightBg0Soft = "#f2e5bc"
gruvboxLightBg1 = "#ebdbb2"
gruvboxLightBg2 = "#d5c4a1"
gruvboxLightBg3 = "#bdae93"
gruvboxLightBg4 = "#a89984"
gruvboxLightFg0 = "#282828"
gruvboxLightFg1 = "#3c3836"
gruvboxLightFg2 = "#504945"
gruvboxLightFg3 = "#665c54"
gruvboxLightFg4 = "#7c6f64"
gruvboxLightGray = "#928374"
gruvboxLightRed = "#9d0006"
gruvboxLightRedBright = "#cc241d"
gruvboxLightGreen = "#79740e"
gruvboxLightGreenBright = "#98971a"
gruvboxLightYellow = "#b57614"
gruvboxLightYellowBright = "#d79921"
gruvboxLightBlue = "#076678"
gruvboxLightBlueBright = "#458588"
gruvboxLightPurple = "#8f3f71"
gruvboxLightPurpleBright = "#b16286"
gruvboxLightAqua = "#427b58"
gruvboxLightAquaBright = "#689d6a"
gruvboxLightOrange = "#af3a03"
gruvboxLightOrangeBright = "#d65d0e"
)
// GruvboxTheme implements the Theme interface with Gruvbox colors.
// It provides both dark and light variants.
type GruvboxTheme struct {
BaseTheme
}
// NewGruvboxTheme creates a new instance of the Gruvbox theme.
func NewGruvboxTheme() *GruvboxTheme {
theme := &GruvboxTheme{}
// Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBlueBright,
Light: gruvboxLightBlueBright,
}
theme.SecondaryColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkPurpleBright,
Light: gruvboxLightPurpleBright,
}
theme.AccentColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkOrangeBright,
Light: gruvboxLightOrangeBright,
}
// Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkRedBright,
Light: gruvboxLightRedBright,
}
theme.WarningColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkYellowBright,
Light: gruvboxLightYellowBright,
}
theme.SuccessColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkGreenBright,
Light: gruvboxLightGreenBright,
}
theme.InfoColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBlueBright,
Light: gruvboxLightBlueBright,
}
// Text colors
theme.TextColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkFg1,
Light: gruvboxLightFg1,
}
theme.TextMutedColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkFg4,
Light: gruvboxLightFg4,
}
// Background colors
theme.BackgroundColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBg0,
Light: gruvboxLightBg0,
}
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBg1,
Light: gruvboxLightBg1,
}
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBg0Soft,
Light: gruvboxLightBg0Soft,
}
// Border colors
theme.BorderColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBg2,
Light: gruvboxLightBg2,
}
theme.BorderActiveColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBlueBright,
Light: gruvboxLightBlueBright,
}
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBg1,
Light: gruvboxLightBg1,
}
// Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkGreenBright,
Light: gruvboxLightGreenBright,
}
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkRedBright,
Light: gruvboxLightRedBright,
}
theme.DiffContextColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkFg4,
Light: gruvboxLightFg4,
}
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkFg3,
Light: gruvboxLightFg3,
}
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkGreenBright,
Light: gruvboxLightGreenBright,
}
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkRedBright,
Light: gruvboxLightRedBright,
}
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
Dark: "#3C4C3C", // Darker green background
Light: "#E8F5E9", // Light green background
}
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
Dark: "#4C3C3C", // Darker red background
Light: "#FFEBEE", // Light red background
}
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBg0,
Light: gruvboxLightBg0,
}
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkFg4,
Light: gruvboxLightFg4,
}
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#32432F", // Slightly darker green
Light: "#C8E6C9", // Light green
}
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#43322F", // Slightly darker red
Light: "#FFCDD2", // Light red
}
// Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkFg1,
Light: gruvboxLightFg1,
}
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkYellowBright,
Light: gruvboxLightYellowBright,
}
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBlueBright,
Light: gruvboxLightBlueBright,
}
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkAquaBright,
Light: gruvboxLightAquaBright,
}
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkGreenBright,
Light: gruvboxLightGreenBright,
}
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkAquaBright,
Light: gruvboxLightAquaBright,
}
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkYellowBright,
Light: gruvboxLightYellowBright,
}
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkOrangeBright,
Light: gruvboxLightOrangeBright,
}
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBg3,
Light: gruvboxLightBg3,
}
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBlueBright,
Light: gruvboxLightBlueBright,
}
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBlueBright,
Light: gruvboxLightBlueBright,
}
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkPurpleBright,
Light: gruvboxLightPurpleBright,
}
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkAquaBright,
Light: gruvboxLightAquaBright,
}
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkFg1,
Light: gruvboxLightFg1,
}
// Syntax highlighting colors
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkGray,
Light: gruvboxLightGray,
}
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkRedBright,
Light: gruvboxLightRedBright,
}
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkGreenBright,
Light: gruvboxLightGreenBright,
}
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkBlueBright,
Light: gruvboxLightBlueBright,
}
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkYellowBright,
Light: gruvboxLightYellowBright,
}
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkPurpleBright,
Light: gruvboxLightPurpleBright,
}
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkYellow,
Light: gruvboxLightYellow,
}
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkAquaBright,
Light: gruvboxLightAquaBright,
}
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
Dark: gruvboxDarkFg1,
Light: gruvboxLightFg1,
}
return theme
}
func init() {
// Register the Gruvbox theme with the theme manager
RegisterTheme("gruvbox", NewGruvboxTheme())
}

View File

@@ -6,8 +6,7 @@ import (
"slices" "slices"
"strings" "strings"
"sync" "sync"
// "github.com/alecthomas/chroma/v2/styles"
"github.com/alecthomas/chroma/v2/styles"
) )
// Manager handles theme registration, selection, and retrieval. // Manager handles theme registration, selection, and retrieval.
@@ -46,7 +45,7 @@ func RegisterTheme(name string, theme Theme) {
func SetTheme(name string) error { func SetTheme(name string) error {
globalManager.mu.Lock() globalManager.mu.Lock()
defer globalManager.mu.Unlock() defer globalManager.mu.Unlock()
delete(styles.Registry, "charm") // delete(styles.Registry, "charm")
// Handle custom theme // Handle custom theme
// if name == "custom" { // if name == "custom" {

View File

@@ -1,269 +0,0 @@
package theme
import (
"github.com/charmbracelet/lipgloss"
)
// MonokaiProTheme implements the Theme interface with Monokai Pro colors.
// It provides both dark and light variants.
type MonokaiProTheme struct {
BaseTheme
}
// NewMonokaiProTheme creates a new instance of the Monokai Pro theme.
func NewMonokaiProTheme() *MonokaiProTheme {
// Monokai Pro color palette (dark mode)
darkBackground := "#2d2a2e"
darkCurrentLine := "#403e41"
darkSelection := "#5b595c"
darkForeground := "#fcfcfa"
darkComment := "#727072"
darkRed := "#ff6188"
darkOrange := "#fc9867"
darkYellow := "#ffd866"
darkGreen := "#a9dc76"
darkCyan := "#78dce8"
darkBlue := "#ab9df2"
darkPurple := "#ab9df2"
darkBorder := "#403e41"
// Light mode colors (adapted from dark)
lightBackground := "#fafafa"
lightCurrentLine := "#f0f0f0"
lightSelection := "#e5e5e6"
lightForeground := "#2d2a2e"
lightComment := "#939293"
lightRed := "#f92672"
lightOrange := "#fd971f"
lightYellow := "#e6db74"
lightGreen := "#9bca65"
lightCyan := "#66d9ef"
lightBlue := "#7e75db"
lightPurple := "#ae81ff"
lightBorder := "#d3d3d3"
theme := &MonokaiProTheme{}
// Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.SecondaryColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.AccentColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
// Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.WarningColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.SuccessColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.InfoColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
// Text colors
theme.TextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.TextMutedColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
// Background colors
theme.BackgroundColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
Dark: darkCurrentLine,
Light: lightCurrentLine,
}
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
Dark: "#221f22", // Slightly darker than background
Light: "#ffffff", // Slightly lighter than background
}
// Border colors
theme.BorderColor = lipgloss.AdaptiveColor{
Dark: darkBorder,
Light: lightBorder,
}
theme.BorderActiveColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
Dark: darkSelection,
Light: lightSelection,
}
// Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{
Dark: "#a9dc76",
Light: "#9bca65",
}
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
Dark: "#ff6188",
Light: "#f92672",
}
theme.DiffContextColor = lipgloss.AdaptiveColor{
Dark: "#a0a0a0",
Light: "#757575",
}
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
Dark: "#a0a0a0",
Light: "#757575",
}
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
Dark: "#c2e7a9",
Light: "#c5e0b4",
}
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
Dark: "#ff8ca6",
Light: "#ffb3c8",
}
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
Dark: "#3a4a35",
Light: "#e8f5e9",
}
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
Dark: "#4a3439",
Light: "#ffebee",
}
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
Dark: "#888888",
Light: "#9e9e9e",
}
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#2d3a28",
Light: "#c8e6c9",
}
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#3d2a2e",
Light: "#ffcdd2",
}
// Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
// Syntax highlighting colors
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
return theme
}
func init() {
// Register the Monokai Pro theme with the theme manager
RegisterTheme("monokai", NewMonokaiProTheme())
}

View File

@@ -1,270 +0,0 @@
package theme
import (
"github.com/charmbracelet/lipgloss"
)
// OneDarkTheme implements the Theme interface with Atom's One Dark colors.
// It provides both dark and light variants.
type OneDarkTheme struct {
BaseTheme
}
// NewOneDarkTheme creates a new instance of the One Dark theme.
func NewOneDarkTheme() *OneDarkTheme {
// One Dark color palette
// Dark mode colors from Atom One Dark
darkBackground := "#282c34"
darkCurrentLine := "#2c313c"
darkSelection := "#3e4451"
darkForeground := "#abb2bf"
darkComment := "#5c6370"
darkRed := "#e06c75"
darkOrange := "#d19a66"
darkYellow := "#e5c07b"
darkGreen := "#98c379"
darkCyan := "#56b6c2"
darkBlue := "#61afef"
darkPurple := "#c678dd"
darkBorder := "#3b4048"
// Light mode colors from Atom One Light
lightBackground := "#fafafa"
lightCurrentLine := "#f0f0f0"
lightSelection := "#e5e5e6"
lightForeground := "#383a42"
lightComment := "#a0a1a7"
lightRed := "#e45649"
lightOrange := "#da8548"
lightYellow := "#c18401"
lightGreen := "#50a14f"
lightCyan := "#0184bc"
lightBlue := "#4078f2"
lightPurple := "#a626a4"
lightBorder := "#d3d3d3"
theme := &OneDarkTheme{}
// Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.SecondaryColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.AccentColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
// Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.WarningColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.SuccessColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.InfoColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
// Text colors
theme.TextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.TextMutedColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
// Background colors
theme.BackgroundColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
Dark: darkCurrentLine,
Light: lightCurrentLine,
}
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
Dark: "#21252b", // Slightly darker than background
Light: "#ffffff", // Slightly lighter than background
}
// Border colors
theme.BorderColor = lipgloss.AdaptiveColor{
Dark: darkBorder,
Light: lightBorder,
}
theme.BorderActiveColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
Dark: darkSelection,
Light: lightSelection,
}
// Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{
Dark: "#478247",
Light: "#2E7D32",
}
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
Dark: "#7C4444",
Light: "#C62828",
}
theme.DiffContextColor = lipgloss.AdaptiveColor{
Dark: "#a0a0a0",
Light: "#757575",
}
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
Dark: "#a0a0a0",
Light: "#757575",
}
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
Dark: "#DAFADA",
Light: "#A5D6A7",
}
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
Dark: "#FADADD",
Light: "#EF9A9A",
}
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
Dark: "#303A30",
Light: "#E8F5E9",
}
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
Dark: "#3A3030",
Light: "#FFEBEE",
}
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
Dark: "#888888",
Light: "#9E9E9E",
}
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#293229",
Light: "#C8E6C9",
}
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#332929",
Light: "#FFCDD2",
}
// Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
// Syntax highlighting colors
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
return theme
}
func init() {
// Register the One Dark theme with the theme manager
RegisterTheme("onedark", NewOneDarkTheme())
}

View File

@@ -1,7 +1,8 @@
package theme package theme
import ( import (
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/charmbracelet/lipgloss/v2/compat"
) )
// OpenCodeTheme implements the Theme interface with OpenCode brand colors. // OpenCodeTheme implements the Theme interface with OpenCode brand colors.
@@ -72,219 +73,219 @@ func NewOpenCodeTheme() *OpenCodeTheme {
theme := &OpenCodeTheme{} theme := &OpenCodeTheme{}
// Base colors // Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{ theme.PrimaryColor = compat.AdaptiveColor{
Dark: darkPrimary, Dark: lipgloss.Color(darkPrimary),
Light: lightPrimary, Light: lipgloss.Color(lightPrimary),
} }
theme.SecondaryColor = lipgloss.AdaptiveColor{ theme.SecondaryColor = compat.AdaptiveColor{
Dark: darkSecondary, Dark: lipgloss.Color(darkSecondary),
Light: lightSecondary, Light: lipgloss.Color(lightSecondary),
} }
theme.AccentColor = lipgloss.AdaptiveColor{ theme.AccentColor = compat.AdaptiveColor{
Dark: darkAccent, Dark: lipgloss.Color(darkAccent),
Light: lightAccent, Light: lipgloss.Color(lightAccent),
} }
// Status colors // Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{ theme.ErrorColor = compat.AdaptiveColor{
Dark: darkRed, Dark: lipgloss.Color(darkRed),
Light: lightRed, Light: lipgloss.Color(lightRed),
} }
theme.WarningColor = lipgloss.AdaptiveColor{ theme.WarningColor = compat.AdaptiveColor{
Dark: darkOrange, Dark: lipgloss.Color(darkOrange),
Light: lightOrange, Light: lipgloss.Color(lightOrange),
} }
theme.SuccessColor = lipgloss.AdaptiveColor{ theme.SuccessColor = compat.AdaptiveColor{
Dark: darkGreen, Dark: lipgloss.Color(darkGreen),
Light: lightGreen, Light: lipgloss.Color(lightGreen),
} }
theme.InfoColor = lipgloss.AdaptiveColor{ theme.InfoColor = compat.AdaptiveColor{
Dark: darkCyan, Dark: lipgloss.Color(darkCyan),
Light: lightCyan, Light: lipgloss.Color(lightCyan),
} }
// Text colors // Text colors
theme.TextColor = lipgloss.AdaptiveColor{ theme.TextColor = compat.AdaptiveColor{
Dark: darkStep12, Dark: lipgloss.Color(darkStep12),
Light: lightStep12, Light: lipgloss.Color(lightStep12),
} }
theme.TextMutedColor = lipgloss.AdaptiveColor{ theme.TextMutedColor = compat.AdaptiveColor{
Dark: darkStep11, Dark: lipgloss.Color(darkStep11),
Light: lightStep11, Light: lipgloss.Color(lightStep11),
} }
// Background colors // Background colors
theme.BackgroundColor = lipgloss.AdaptiveColor{ theme.BackgroundColor = compat.AdaptiveColor{
Dark: darkStep1, Dark: lipgloss.Color(darkStep1),
Light: lightStep1, Light: lipgloss.Color(lightStep1),
} }
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{ theme.BackgroundSubtleColor = compat.AdaptiveColor{
Dark: darkStep2, Dark: lipgloss.Color(darkStep2),
Light: lightStep2, Light: lipgloss.Color(lightStep2),
} }
theme.BackgroundElementColor = lipgloss.AdaptiveColor{ theme.BackgroundElementColor = compat.AdaptiveColor{
Dark: darkStep3, Dark: lipgloss.Color(darkStep3),
Light: lightStep3, Light: lipgloss.Color(lightStep3),
} }
// Border colors // Border colors
theme.BorderColor = lipgloss.AdaptiveColor{ theme.BorderColor = compat.AdaptiveColor{
Dark: darkStep7, Dark: lipgloss.Color(darkStep7),
Light: lightStep7, Light: lipgloss.Color(lightStep7),
} }
theme.BorderActiveColor = lipgloss.AdaptiveColor{ theme.BorderActiveColor = compat.AdaptiveColor{
Dark: darkStep8, Dark: lipgloss.Color(darkStep8),
Light: lightStep8, Light: lipgloss.Color(lightStep8),
} }
theme.BorderSubtleColor = lipgloss.AdaptiveColor{ theme.BorderSubtleColor = compat.AdaptiveColor{
Dark: darkStep6, Dark: lipgloss.Color(darkStep6),
Light: lightStep6, Light: lipgloss.Color(lightStep6),
} }
// Diff view colors // Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{ theme.DiffAddedColor = compat.AdaptiveColor{
Dark: "#478247", Dark: lipgloss.Color("#478247"),
Light: "#2E7D32", Light: lipgloss.Color("#2E7D32"),
} }
theme.DiffRemovedColor = lipgloss.AdaptiveColor{ theme.DiffRemovedColor = compat.AdaptiveColor{
Dark: "#7C4444", Dark: lipgloss.Color("#7C4444"),
Light: "#C62828", Light: lipgloss.Color("#C62828"),
} }
theme.DiffContextColor = lipgloss.AdaptiveColor{ theme.DiffContextColor = compat.AdaptiveColor{
Dark: "#a0a0a0", Dark: lipgloss.Color("#a0a0a0"),
Light: "#757575", Light: lipgloss.Color("#757575"),
} }
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{ theme.DiffHunkHeaderColor = compat.AdaptiveColor{
Dark: "#a0a0a0", Dark: lipgloss.Color("#a0a0a0"),
Light: "#757575", Light: lipgloss.Color("#757575"),
} }
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{ theme.DiffHighlightAddedColor = compat.AdaptiveColor{
Dark: "#DAFADA", Dark: lipgloss.Color("#DAFADA"),
Light: "#A5D6A7", Light: lipgloss.Color("#A5D6A7"),
} }
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{ theme.DiffHighlightRemovedColor = compat.AdaptiveColor{
Dark: "#FADADD", Dark: lipgloss.Color("#FADADD"),
Light: "#EF9A9A", Light: lipgloss.Color("#EF9A9A"),
} }
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{ theme.DiffAddedBgColor = compat.AdaptiveColor{
Dark: "#303A30", Dark: lipgloss.Color("#303A30"),
Light: "#E8F5E9", Light: lipgloss.Color("#E8F5E9"),
} }
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{ theme.DiffRemovedBgColor = compat.AdaptiveColor{
Dark: "#3A3030", Dark: lipgloss.Color("#3A3030"),
Light: "#FFEBEE", Light: lipgloss.Color("#FFEBEE"),
} }
theme.DiffContextBgColor = lipgloss.AdaptiveColor{ theme.DiffContextBgColor = compat.AdaptiveColor{
Dark: darkStep2, Dark: lipgloss.Color(darkStep2),
Light: lightStep2, Light: lipgloss.Color(lightStep2),
} }
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{ theme.DiffLineNumberColor = compat.AdaptiveColor{
Dark: darkStep3, Dark: lipgloss.Color(darkStep3),
Light: lightStep3, Light: lipgloss.Color(lightStep3),
} }
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{ theme.DiffAddedLineNumberBgColor = compat.AdaptiveColor{
Dark: "#293229", Dark: lipgloss.Color("#293229"),
Light: "#C8E6C9", Light: lipgloss.Color("#C8E6C9"),
} }
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{ theme.DiffRemovedLineNumberBgColor = compat.AdaptiveColor{
Dark: "#332929", Dark: lipgloss.Color("#332929"),
Light: "#FFCDD2", Light: lipgloss.Color("#FFCDD2"),
} }
// Markdown colors // Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{ theme.MarkdownTextColor = compat.AdaptiveColor{
Dark: darkStep12, Dark: lipgloss.Color(darkStep12),
Light: lightStep12, Light: lipgloss.Color(lightStep12),
} }
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{ theme.MarkdownHeadingColor = compat.AdaptiveColor{
Dark: darkSecondary, Dark: lipgloss.Color(darkSecondary),
Light: lightSecondary, Light: lipgloss.Color(lightSecondary),
} }
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{ theme.MarkdownLinkColor = compat.AdaptiveColor{
Dark: darkPrimary, Dark: lipgloss.Color(darkPrimary),
Light: lightPrimary, Light: lipgloss.Color(lightPrimary),
} }
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{ theme.MarkdownLinkTextColor = compat.AdaptiveColor{
Dark: darkCyan, Dark: lipgloss.Color(darkCyan),
Light: lightCyan, Light: lipgloss.Color(lightCyan),
} }
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{ theme.MarkdownCodeColor = compat.AdaptiveColor{
Dark: darkGreen, Dark: lipgloss.Color(darkGreen),
Light: lightGreen, Light: lipgloss.Color(lightGreen),
} }
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{ theme.MarkdownBlockQuoteColor = compat.AdaptiveColor{
Dark: darkYellow, Dark: lipgloss.Color(darkYellow),
Light: lightYellow, Light: lipgloss.Color(lightYellow),
} }
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{ theme.MarkdownEmphColor = compat.AdaptiveColor{
Dark: darkYellow, Dark: lipgloss.Color(darkYellow),
Light: lightYellow, Light: lipgloss.Color(lightYellow),
} }
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{ theme.MarkdownStrongColor = compat.AdaptiveColor{
Dark: darkAccent, Dark: lipgloss.Color(darkAccent),
Light: lightAccent, Light: lipgloss.Color(lightAccent),
} }
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{ theme.MarkdownHorizontalRuleColor = compat.AdaptiveColor{
Dark: darkStep11, Dark: lipgloss.Color(darkStep11),
Light: lightStep11, Light: lipgloss.Color(lightStep11),
} }
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{ theme.MarkdownListItemColor = compat.AdaptiveColor{
Dark: darkPrimary, Dark: lipgloss.Color(darkPrimary),
Light: lightPrimary, Light: lipgloss.Color(lightPrimary),
} }
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{ theme.MarkdownListEnumerationColor = compat.AdaptiveColor{
Dark: darkCyan, Dark: lipgloss.Color(darkCyan),
Light: lightCyan, Light: lipgloss.Color(lightCyan),
} }
theme.MarkdownImageColor = lipgloss.AdaptiveColor{ theme.MarkdownImageColor = compat.AdaptiveColor{
Dark: darkPrimary, Dark: lipgloss.Color(darkPrimary),
Light: lightPrimary, Light: lipgloss.Color(lightPrimary),
} }
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{ theme.MarkdownImageTextColor = compat.AdaptiveColor{
Dark: darkCyan, Dark: lipgloss.Color(darkCyan),
Light: lightCyan, Light: lipgloss.Color(lightCyan),
} }
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{ theme.MarkdownCodeBlockColor = compat.AdaptiveColor{
Dark: darkStep12, Dark: lipgloss.Color(darkStep12),
Light: lightStep12, Light: lipgloss.Color(lightStep12),
} }
// Syntax highlighting colors // Syntax highlighting colors
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{ theme.SyntaxCommentColor = compat.AdaptiveColor{
Dark: darkStep11, Dark: lipgloss.Color(darkStep11),
Light: lightStep11, Light: lipgloss.Color(lightStep11),
} }
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{ theme.SyntaxKeywordColor = compat.AdaptiveColor{
Dark: darkSecondary, Dark: lipgloss.Color(darkPrimary),
Light: lightSecondary, Light: lipgloss.Color(lightPrimary),
} }
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{ theme.SyntaxFunctionColor = compat.AdaptiveColor{
Dark: darkPrimary, Dark: lipgloss.Color(darkPrimary),
Light: lightPrimary, Light: lipgloss.Color(lightPrimary),
} }
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{ theme.SyntaxVariableColor = compat.AdaptiveColor{
Dark: darkRed, Dark: lipgloss.Color(darkRed),
Light: lightRed, Light: lipgloss.Color(lightRed),
} }
theme.SyntaxStringColor = lipgloss.AdaptiveColor{ theme.SyntaxStringColor = compat.AdaptiveColor{
Dark: darkGreen, Dark: lipgloss.Color(darkGreen),
Light: lightGreen, Light: lipgloss.Color(lightGreen),
} }
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{ theme.SyntaxNumberColor = compat.AdaptiveColor{
Dark: darkAccent, Dark: lipgloss.Color(darkAccent),
Light: lightAccent, Light: lipgloss.Color(lightAccent),
} }
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{ theme.SyntaxTypeColor = compat.AdaptiveColor{
Dark: darkYellow, Dark: lipgloss.Color(darkYellow),
Light: lightYellow, Light: lipgloss.Color(lightYellow),
} }
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{ theme.SyntaxOperatorColor = compat.AdaptiveColor{
Dark: darkCyan, Dark: lipgloss.Color(darkCyan),
Light: lightCyan, Light: lipgloss.Color(lightCyan),
} }
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{ theme.SyntaxPunctuationColor = compat.AdaptiveColor{
Dark: darkStep12, Dark: lipgloss.Color(darkStep12),
Light: lightStep12, Light: lipgloss.Color(lightStep12),
} }
return theme return theme

View File

@@ -4,231 +4,232 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/charmbracelet/lipgloss/v2/compat"
) )
// Theme defines the interface for all UI themes in the application. // Theme defines the interface for all UI themes in the application.
// All colors must be defined as lipgloss.AdaptiveColor to support // All colors must be defined as compat.AdaptiveColor to support
// both light and dark terminal backgrounds. // both light and dark terminal backgrounds.
type Theme interface { type Theme interface {
// Background colors // Background colors
Background() lipgloss.AdaptiveColor // Radix 1 Background() compat.AdaptiveColor // Radix 1
BackgroundSubtle() lipgloss.AdaptiveColor // Radix 2 BackgroundSubtle() compat.AdaptiveColor // Radix 2
BackgroundElement() lipgloss.AdaptiveColor // Radix 3 BackgroundElement() compat.AdaptiveColor // Radix 3
// Border colors // Border colors
BorderSubtle() lipgloss.AdaptiveColor // Radix 6 BorderSubtle() compat.AdaptiveColor // Radix 6
Border() lipgloss.AdaptiveColor // Radix 7 Border() compat.AdaptiveColor // Radix 7
BorderActive() lipgloss.AdaptiveColor // Radix 8 BorderActive() compat.AdaptiveColor // Radix 8
// Brand colors // Brand colors
Primary() lipgloss.AdaptiveColor // Radix 9 Primary() compat.AdaptiveColor // Radix 9
Secondary() lipgloss.AdaptiveColor Secondary() compat.AdaptiveColor
Accent() lipgloss.AdaptiveColor Accent() compat.AdaptiveColor
// Text colors // Text colors
TextMuted() lipgloss.AdaptiveColor // Radix 11 TextMuted() compat.AdaptiveColor // Radix 11
Text() lipgloss.AdaptiveColor // Radix 12 Text() compat.AdaptiveColor // Radix 12
// Status colors // Status colors
Error() lipgloss.AdaptiveColor Error() compat.AdaptiveColor
Warning() lipgloss.AdaptiveColor Warning() compat.AdaptiveColor
Success() lipgloss.AdaptiveColor Success() compat.AdaptiveColor
Info() lipgloss.AdaptiveColor Info() compat.AdaptiveColor
// Diff view colors // Diff view colors
DiffAdded() lipgloss.AdaptiveColor DiffAdded() compat.AdaptiveColor
DiffRemoved() lipgloss.AdaptiveColor DiffRemoved() compat.AdaptiveColor
DiffContext() lipgloss.AdaptiveColor DiffContext() compat.AdaptiveColor
DiffHunkHeader() lipgloss.AdaptiveColor DiffHunkHeader() compat.AdaptiveColor
DiffHighlightAdded() lipgloss.AdaptiveColor DiffHighlightAdded() compat.AdaptiveColor
DiffHighlightRemoved() lipgloss.AdaptiveColor DiffHighlightRemoved() compat.AdaptiveColor
DiffAddedBg() lipgloss.AdaptiveColor DiffAddedBg() compat.AdaptiveColor
DiffRemovedBg() lipgloss.AdaptiveColor DiffRemovedBg() compat.AdaptiveColor
DiffContextBg() lipgloss.AdaptiveColor DiffContextBg() compat.AdaptiveColor
DiffLineNumber() lipgloss.AdaptiveColor DiffLineNumber() compat.AdaptiveColor
DiffAddedLineNumberBg() lipgloss.AdaptiveColor DiffAddedLineNumberBg() compat.AdaptiveColor
DiffRemovedLineNumberBg() lipgloss.AdaptiveColor DiffRemovedLineNumberBg() compat.AdaptiveColor
// Markdown colors // Markdown colors
MarkdownText() lipgloss.AdaptiveColor MarkdownText() compat.AdaptiveColor
MarkdownHeading() lipgloss.AdaptiveColor MarkdownHeading() compat.AdaptiveColor
MarkdownLink() lipgloss.AdaptiveColor MarkdownLink() compat.AdaptiveColor
MarkdownLinkText() lipgloss.AdaptiveColor MarkdownLinkText() compat.AdaptiveColor
MarkdownCode() lipgloss.AdaptiveColor MarkdownCode() compat.AdaptiveColor
MarkdownBlockQuote() lipgloss.AdaptiveColor MarkdownBlockQuote() compat.AdaptiveColor
MarkdownEmph() lipgloss.AdaptiveColor MarkdownEmph() compat.AdaptiveColor
MarkdownStrong() lipgloss.AdaptiveColor MarkdownStrong() compat.AdaptiveColor
MarkdownHorizontalRule() lipgloss.AdaptiveColor MarkdownHorizontalRule() compat.AdaptiveColor
MarkdownListItem() lipgloss.AdaptiveColor MarkdownListItem() compat.AdaptiveColor
MarkdownListEnumeration() lipgloss.AdaptiveColor MarkdownListEnumeration() compat.AdaptiveColor
MarkdownImage() lipgloss.AdaptiveColor MarkdownImage() compat.AdaptiveColor
MarkdownImageText() lipgloss.AdaptiveColor MarkdownImageText() compat.AdaptiveColor
MarkdownCodeBlock() lipgloss.AdaptiveColor MarkdownCodeBlock() compat.AdaptiveColor
// Syntax highlighting colors // Syntax highlighting colors
SyntaxComment() lipgloss.AdaptiveColor SyntaxComment() compat.AdaptiveColor
SyntaxKeyword() lipgloss.AdaptiveColor SyntaxKeyword() compat.AdaptiveColor
SyntaxFunction() lipgloss.AdaptiveColor SyntaxFunction() compat.AdaptiveColor
SyntaxVariable() lipgloss.AdaptiveColor SyntaxVariable() compat.AdaptiveColor
SyntaxString() lipgloss.AdaptiveColor SyntaxString() compat.AdaptiveColor
SyntaxNumber() lipgloss.AdaptiveColor SyntaxNumber() compat.AdaptiveColor
SyntaxType() lipgloss.AdaptiveColor SyntaxType() compat.AdaptiveColor
SyntaxOperator() lipgloss.AdaptiveColor SyntaxOperator() compat.AdaptiveColor
SyntaxPunctuation() lipgloss.AdaptiveColor SyntaxPunctuation() compat.AdaptiveColor
} }
// BaseTheme provides a default implementation of the Theme interface // BaseTheme provides a default implementation of the Theme interface
// that can be embedded in concrete theme implementations. // that can be embedded in concrete theme implementations.
type BaseTheme struct { type BaseTheme struct {
// Background colors // Background colors
BackgroundColor lipgloss.AdaptiveColor BackgroundColor compat.AdaptiveColor
BackgroundSubtleColor lipgloss.AdaptiveColor BackgroundSubtleColor compat.AdaptiveColor
BackgroundElementColor lipgloss.AdaptiveColor BackgroundElementColor compat.AdaptiveColor
// Border colors // Border colors
BorderSubtleColor lipgloss.AdaptiveColor BorderSubtleColor compat.AdaptiveColor
BorderColor lipgloss.AdaptiveColor BorderColor compat.AdaptiveColor
BorderActiveColor lipgloss.AdaptiveColor BorderActiveColor compat.AdaptiveColor
// Brand colors // Brand colors
PrimaryColor lipgloss.AdaptiveColor PrimaryColor compat.AdaptiveColor
SecondaryColor lipgloss.AdaptiveColor SecondaryColor compat.AdaptiveColor
AccentColor lipgloss.AdaptiveColor AccentColor compat.AdaptiveColor
// Text colors // Text colors
TextMutedColor lipgloss.AdaptiveColor TextMutedColor compat.AdaptiveColor
TextColor lipgloss.AdaptiveColor TextColor compat.AdaptiveColor
// Status colors // Status colors
ErrorColor lipgloss.AdaptiveColor ErrorColor compat.AdaptiveColor
WarningColor lipgloss.AdaptiveColor WarningColor compat.AdaptiveColor
SuccessColor lipgloss.AdaptiveColor SuccessColor compat.AdaptiveColor
InfoColor lipgloss.AdaptiveColor InfoColor compat.AdaptiveColor
// Diff view colors // Diff view colors
DiffAddedColor lipgloss.AdaptiveColor DiffAddedColor compat.AdaptiveColor
DiffRemovedColor lipgloss.AdaptiveColor DiffRemovedColor compat.AdaptiveColor
DiffContextColor lipgloss.AdaptiveColor DiffContextColor compat.AdaptiveColor
DiffHunkHeaderColor lipgloss.AdaptiveColor DiffHunkHeaderColor compat.AdaptiveColor
DiffHighlightAddedColor lipgloss.AdaptiveColor DiffHighlightAddedColor compat.AdaptiveColor
DiffHighlightRemovedColor lipgloss.AdaptiveColor DiffHighlightRemovedColor compat.AdaptiveColor
DiffAddedBgColor lipgloss.AdaptiveColor DiffAddedBgColor compat.AdaptiveColor
DiffRemovedBgColor lipgloss.AdaptiveColor DiffRemovedBgColor compat.AdaptiveColor
DiffContextBgColor lipgloss.AdaptiveColor DiffContextBgColor compat.AdaptiveColor
DiffLineNumberColor lipgloss.AdaptiveColor DiffLineNumberColor compat.AdaptiveColor
DiffAddedLineNumberBgColor lipgloss.AdaptiveColor DiffAddedLineNumberBgColor compat.AdaptiveColor
DiffRemovedLineNumberBgColor lipgloss.AdaptiveColor DiffRemovedLineNumberBgColor compat.AdaptiveColor
// Markdown colors // Markdown colors
MarkdownTextColor lipgloss.AdaptiveColor MarkdownTextColor compat.AdaptiveColor
MarkdownHeadingColor lipgloss.AdaptiveColor MarkdownHeadingColor compat.AdaptiveColor
MarkdownLinkColor lipgloss.AdaptiveColor MarkdownLinkColor compat.AdaptiveColor
MarkdownLinkTextColor lipgloss.AdaptiveColor MarkdownLinkTextColor compat.AdaptiveColor
MarkdownCodeColor lipgloss.AdaptiveColor MarkdownCodeColor compat.AdaptiveColor
MarkdownBlockQuoteColor lipgloss.AdaptiveColor MarkdownBlockQuoteColor compat.AdaptiveColor
MarkdownEmphColor lipgloss.AdaptiveColor MarkdownEmphColor compat.AdaptiveColor
MarkdownStrongColor lipgloss.AdaptiveColor MarkdownStrongColor compat.AdaptiveColor
MarkdownHorizontalRuleColor lipgloss.AdaptiveColor MarkdownHorizontalRuleColor compat.AdaptiveColor
MarkdownListItemColor lipgloss.AdaptiveColor MarkdownListItemColor compat.AdaptiveColor
MarkdownListEnumerationColor lipgloss.AdaptiveColor MarkdownListEnumerationColor compat.AdaptiveColor
MarkdownImageColor lipgloss.AdaptiveColor MarkdownImageColor compat.AdaptiveColor
MarkdownImageTextColor lipgloss.AdaptiveColor MarkdownImageTextColor compat.AdaptiveColor
MarkdownCodeBlockColor lipgloss.AdaptiveColor MarkdownCodeBlockColor compat.AdaptiveColor
// Syntax highlighting colors // Syntax highlighting colors
SyntaxCommentColor lipgloss.AdaptiveColor SyntaxCommentColor compat.AdaptiveColor
SyntaxKeywordColor lipgloss.AdaptiveColor SyntaxKeywordColor compat.AdaptiveColor
SyntaxFunctionColor lipgloss.AdaptiveColor SyntaxFunctionColor compat.AdaptiveColor
SyntaxVariableColor lipgloss.AdaptiveColor SyntaxVariableColor compat.AdaptiveColor
SyntaxStringColor lipgloss.AdaptiveColor SyntaxStringColor compat.AdaptiveColor
SyntaxNumberColor lipgloss.AdaptiveColor SyntaxNumberColor compat.AdaptiveColor
SyntaxTypeColor lipgloss.AdaptiveColor SyntaxTypeColor compat.AdaptiveColor
SyntaxOperatorColor lipgloss.AdaptiveColor SyntaxOperatorColor compat.AdaptiveColor
SyntaxPunctuationColor lipgloss.AdaptiveColor SyntaxPunctuationColor compat.AdaptiveColor
} }
// Implement the Theme interface for BaseTheme // Implement the Theme interface for BaseTheme
func (t *BaseTheme) Primary() lipgloss.AdaptiveColor { return t.PrimaryColor } func (t *BaseTheme) Primary() compat.AdaptiveColor { return t.PrimaryColor }
func (t *BaseTheme) Secondary() lipgloss.AdaptiveColor { return t.SecondaryColor } func (t *BaseTheme) Secondary() compat.AdaptiveColor { return t.SecondaryColor }
func (t *BaseTheme) Accent() lipgloss.AdaptiveColor { return t.AccentColor } func (t *BaseTheme) Accent() compat.AdaptiveColor { return t.AccentColor }
func (t *BaseTheme) Error() lipgloss.AdaptiveColor { return t.ErrorColor } func (t *BaseTheme) Error() compat.AdaptiveColor { return t.ErrorColor }
func (t *BaseTheme) Warning() lipgloss.AdaptiveColor { return t.WarningColor } func (t *BaseTheme) Warning() compat.AdaptiveColor { return t.WarningColor }
func (t *BaseTheme) Success() lipgloss.AdaptiveColor { return t.SuccessColor } func (t *BaseTheme) Success() compat.AdaptiveColor { return t.SuccessColor }
func (t *BaseTheme) Info() lipgloss.AdaptiveColor { return t.InfoColor } func (t *BaseTheme) Info() compat.AdaptiveColor { return t.InfoColor }
func (t *BaseTheme) Text() lipgloss.AdaptiveColor { return t.TextColor } func (t *BaseTheme) Text() compat.AdaptiveColor { return t.TextColor }
func (t *BaseTheme) TextMuted() lipgloss.AdaptiveColor { return t.TextMutedColor } func (t *BaseTheme) TextMuted() compat.AdaptiveColor { return t.TextMutedColor }
func (t *BaseTheme) Background() lipgloss.AdaptiveColor { return t.BackgroundColor } func (t *BaseTheme) Background() compat.AdaptiveColor { return t.BackgroundColor }
func (t *BaseTheme) BackgroundSubtle() lipgloss.AdaptiveColor { return t.BackgroundSubtleColor } func (t *BaseTheme) BackgroundSubtle() compat.AdaptiveColor { return t.BackgroundSubtleColor }
func (t *BaseTheme) BackgroundElement() lipgloss.AdaptiveColor { return t.BackgroundElementColor } func (t *BaseTheme) BackgroundElement() compat.AdaptiveColor { return t.BackgroundElementColor }
func (t *BaseTheme) Border() lipgloss.AdaptiveColor { return t.BorderColor } func (t *BaseTheme) Border() compat.AdaptiveColor { return t.BorderColor }
func (t *BaseTheme) BorderActive() lipgloss.AdaptiveColor { return t.BorderActiveColor } func (t *BaseTheme) BorderActive() compat.AdaptiveColor { return t.BorderActiveColor }
func (t *BaseTheme) BorderSubtle() lipgloss.AdaptiveColor { return t.BorderSubtleColor } func (t *BaseTheme) BorderSubtle() compat.AdaptiveColor { return t.BorderSubtleColor }
func (t *BaseTheme) DiffAdded() lipgloss.AdaptiveColor { return t.DiffAddedColor } func (t *BaseTheme) DiffAdded() compat.AdaptiveColor { return t.DiffAddedColor }
func (t *BaseTheme) DiffRemoved() lipgloss.AdaptiveColor { return t.DiffRemovedColor } func (t *BaseTheme) DiffRemoved() compat.AdaptiveColor { return t.DiffRemovedColor }
func (t *BaseTheme) DiffContext() lipgloss.AdaptiveColor { return t.DiffContextColor } func (t *BaseTheme) DiffContext() compat.AdaptiveColor { return t.DiffContextColor }
func (t *BaseTheme) DiffHunkHeader() lipgloss.AdaptiveColor { return t.DiffHunkHeaderColor } func (t *BaseTheme) DiffHunkHeader() compat.AdaptiveColor { return t.DiffHunkHeaderColor }
func (t *BaseTheme) DiffHighlightAdded() lipgloss.AdaptiveColor { return t.DiffHighlightAddedColor } func (t *BaseTheme) DiffHighlightAdded() compat.AdaptiveColor { return t.DiffHighlightAddedColor }
func (t *BaseTheme) DiffHighlightRemoved() lipgloss.AdaptiveColor { return t.DiffHighlightRemovedColor } func (t *BaseTheme) DiffHighlightRemoved() compat.AdaptiveColor { return t.DiffHighlightRemovedColor }
func (t *BaseTheme) DiffAddedBg() lipgloss.AdaptiveColor { return t.DiffAddedBgColor } func (t *BaseTheme) DiffAddedBg() compat.AdaptiveColor { return t.DiffAddedBgColor }
func (t *BaseTheme) DiffRemovedBg() lipgloss.AdaptiveColor { return t.DiffRemovedBgColor } func (t *BaseTheme) DiffRemovedBg() compat.AdaptiveColor { return t.DiffRemovedBgColor }
func (t *BaseTheme) DiffContextBg() lipgloss.AdaptiveColor { return t.DiffContextBgColor } func (t *BaseTheme) DiffContextBg() compat.AdaptiveColor { return t.DiffContextBgColor }
func (t *BaseTheme) DiffLineNumber() lipgloss.AdaptiveColor { return t.DiffLineNumberColor } func (t *BaseTheme) DiffLineNumber() compat.AdaptiveColor { return t.DiffLineNumberColor }
func (t *BaseTheme) DiffAddedLineNumberBg() lipgloss.AdaptiveColor { func (t *BaseTheme) DiffAddedLineNumberBg() compat.AdaptiveColor {
return t.DiffAddedLineNumberBgColor return t.DiffAddedLineNumberBgColor
} }
func (t *BaseTheme) DiffRemovedLineNumberBg() lipgloss.AdaptiveColor { func (t *BaseTheme) DiffRemovedLineNumberBg() compat.AdaptiveColor {
return t.DiffRemovedLineNumberBgColor return t.DiffRemovedLineNumberBgColor
} }
func (t *BaseTheme) MarkdownText() lipgloss.AdaptiveColor { return t.MarkdownTextColor } func (t *BaseTheme) MarkdownText() compat.AdaptiveColor { return t.MarkdownTextColor }
func (t *BaseTheme) MarkdownHeading() lipgloss.AdaptiveColor { return t.MarkdownHeadingColor } func (t *BaseTheme) MarkdownHeading() compat.AdaptiveColor { return t.MarkdownHeadingColor }
func (t *BaseTheme) MarkdownLink() lipgloss.AdaptiveColor { return t.MarkdownLinkColor } func (t *BaseTheme) MarkdownLink() compat.AdaptiveColor { return t.MarkdownLinkColor }
func (t *BaseTheme) MarkdownLinkText() lipgloss.AdaptiveColor { return t.MarkdownLinkTextColor } func (t *BaseTheme) MarkdownLinkText() compat.AdaptiveColor { return t.MarkdownLinkTextColor }
func (t *BaseTheme) MarkdownCode() lipgloss.AdaptiveColor { return t.MarkdownCodeColor } func (t *BaseTheme) MarkdownCode() compat.AdaptiveColor { return t.MarkdownCodeColor }
func (t *BaseTheme) MarkdownBlockQuote() lipgloss.AdaptiveColor { return t.MarkdownBlockQuoteColor } func (t *BaseTheme) MarkdownBlockQuote() compat.AdaptiveColor { return t.MarkdownBlockQuoteColor }
func (t *BaseTheme) MarkdownEmph() lipgloss.AdaptiveColor { return t.MarkdownEmphColor } func (t *BaseTheme) MarkdownEmph() compat.AdaptiveColor { return t.MarkdownEmphColor }
func (t *BaseTheme) MarkdownStrong() lipgloss.AdaptiveColor { return t.MarkdownStrongColor } func (t *BaseTheme) MarkdownStrong() compat.AdaptiveColor { return t.MarkdownStrongColor }
func (t *BaseTheme) MarkdownHorizontalRule() lipgloss.AdaptiveColor { func (t *BaseTheme) MarkdownHorizontalRule() compat.AdaptiveColor {
return t.MarkdownHorizontalRuleColor return t.MarkdownHorizontalRuleColor
} }
func (t *BaseTheme) MarkdownListItem() lipgloss.AdaptiveColor { return t.MarkdownListItemColor } func (t *BaseTheme) MarkdownListItem() compat.AdaptiveColor { return t.MarkdownListItemColor }
func (t *BaseTheme) MarkdownListEnumeration() lipgloss.AdaptiveColor { func (t *BaseTheme) MarkdownListEnumeration() compat.AdaptiveColor {
return t.MarkdownListEnumerationColor return t.MarkdownListEnumerationColor
} }
func (t *BaseTheme) MarkdownImage() lipgloss.AdaptiveColor { return t.MarkdownImageColor } func (t *BaseTheme) MarkdownImage() compat.AdaptiveColor { return t.MarkdownImageColor }
func (t *BaseTheme) MarkdownImageText() lipgloss.AdaptiveColor { return t.MarkdownImageTextColor } func (t *BaseTheme) MarkdownImageText() compat.AdaptiveColor { return t.MarkdownImageTextColor }
func (t *BaseTheme) MarkdownCodeBlock() lipgloss.AdaptiveColor { return t.MarkdownCodeBlockColor } func (t *BaseTheme) MarkdownCodeBlock() compat.AdaptiveColor { return t.MarkdownCodeBlockColor }
func (t *BaseTheme) SyntaxComment() lipgloss.AdaptiveColor { return t.SyntaxCommentColor } func (t *BaseTheme) SyntaxComment() compat.AdaptiveColor { return t.SyntaxCommentColor }
func (t *BaseTheme) SyntaxKeyword() lipgloss.AdaptiveColor { return t.SyntaxKeywordColor } func (t *BaseTheme) SyntaxKeyword() compat.AdaptiveColor { return t.SyntaxKeywordColor }
func (t *BaseTheme) SyntaxFunction() lipgloss.AdaptiveColor { return t.SyntaxFunctionColor } func (t *BaseTheme) SyntaxFunction() compat.AdaptiveColor { return t.SyntaxFunctionColor }
func (t *BaseTheme) SyntaxVariable() lipgloss.AdaptiveColor { return t.SyntaxVariableColor } func (t *BaseTheme) SyntaxVariable() compat.AdaptiveColor { return t.SyntaxVariableColor }
func (t *BaseTheme) SyntaxString() lipgloss.AdaptiveColor { return t.SyntaxStringColor } func (t *BaseTheme) SyntaxString() compat.AdaptiveColor { return t.SyntaxStringColor }
func (t *BaseTheme) SyntaxNumber() lipgloss.AdaptiveColor { return t.SyntaxNumberColor } func (t *BaseTheme) SyntaxNumber() compat.AdaptiveColor { return t.SyntaxNumberColor }
func (t *BaseTheme) SyntaxType() lipgloss.AdaptiveColor { return t.SyntaxTypeColor } func (t *BaseTheme) SyntaxType() compat.AdaptiveColor { return t.SyntaxTypeColor }
func (t *BaseTheme) SyntaxOperator() lipgloss.AdaptiveColor { return t.SyntaxOperatorColor } func (t *BaseTheme) SyntaxOperator() compat.AdaptiveColor { return t.SyntaxOperatorColor }
func (t *BaseTheme) SyntaxPunctuation() lipgloss.AdaptiveColor { return t.SyntaxPunctuationColor } func (t *BaseTheme) SyntaxPunctuation() compat.AdaptiveColor { return t.SyntaxPunctuationColor }
// ParseAdaptiveColor parses a color value from the config file into a lipgloss.AdaptiveColor. // ParseAdaptiveColor parses a color value from the config file into a compat.AdaptiveColor.
// It accepts either a string (hex color) or a map with "dark" and "light" keys. // It accepts either a string (hex color) or a map with "dark" and "light" keys.
func ParseAdaptiveColor(value any) (lipgloss.AdaptiveColor, error) { func ParseAdaptiveColor(value any) (compat.AdaptiveColor, error) {
// Regular expression to validate hex color format // Regular expression to validate hex color format
hexColorRegex := regexp.MustCompile(`^#[0-9a-fA-F]{6}$`) hexColorRegex := regexp.MustCompile(`^#[0-9a-fA-F]{6}$`)
// Case 1: String value (same color for both dark and light modes) // Case 1: String value (same color for both dark and light modes)
if hexColor, ok := value.(string); ok { if hexColor, ok := value.(string); ok {
if !hexColorRegex.MatchString(hexColor) { if !hexColorRegex.MatchString(hexColor) {
return lipgloss.AdaptiveColor{}, fmt.Errorf("invalid hex color format: %s", hexColor) return compat.AdaptiveColor{}, fmt.Errorf("invalid hex color format: %s", hexColor)
} }
return lipgloss.AdaptiveColor{ return compat.AdaptiveColor{
Dark: hexColor, Dark: lipgloss.Color(hexColor),
Light: hexColor, Light: lipgloss.Color(hexColor),
}, nil }, nil
} }
@@ -236,11 +237,11 @@ func ParseAdaptiveColor(value any) (lipgloss.AdaptiveColor, error) {
if numericVal, ok := value.(float64); ok { if numericVal, ok := value.(float64); ok {
intVal := int(numericVal) intVal := int(numericVal)
if intVal < 0 || intVal > 255 { if intVal < 0 || intVal > 255 {
return lipgloss.AdaptiveColor{}, fmt.Errorf("invalid int color value (must be between 0 and 255): %d", intVal) return compat.AdaptiveColor{}, fmt.Errorf("invalid int color value (must be between 0 and 255): %d", intVal)
} }
return lipgloss.AdaptiveColor{ return compat.AdaptiveColor{
Dark: fmt.Sprintf("%d", intVal), Dark: lipgloss.Color(fmt.Sprintf("%d", intVal)),
Light: fmt.Sprintf("%d", intVal), Light: lipgloss.Color(fmt.Sprintf("%d", intVal)),
}, nil }, nil
} }
@@ -250,7 +251,7 @@ func ParseAdaptiveColor(value any) (lipgloss.AdaptiveColor, error) {
lightVal, lightOk := colorMap["light"] lightVal, lightOk := colorMap["light"]
if !darkOk || !lightOk { if !darkOk || !lightOk {
return lipgloss.AdaptiveColor{}, fmt.Errorf("color map must contain both 'dark' and 'light' keys") return compat.AdaptiveColor{}, fmt.Errorf("color map must contain both 'dark' and 'light' keys")
} }
darkHex, darkIsString := darkVal.(string) darkHex, darkIsString := darkVal.(string)
@@ -261,27 +262,27 @@ func ParseAdaptiveColor(value any) (lipgloss.AdaptiveColor, error) {
lightVal, lightIsNumber := lightVal.(float64) lightVal, lightIsNumber := lightVal.(float64)
if !darkIsNumber || !lightIsNumber { if !darkIsNumber || !lightIsNumber {
return lipgloss.AdaptiveColor{}, fmt.Errorf("color map values must be strings or ints") return compat.AdaptiveColor{}, fmt.Errorf("color map values must be strings or ints")
} }
darkInt := int(darkVal) darkInt := int(darkVal)
lightInt := int(lightVal) lightInt := int(lightVal)
return lipgloss.AdaptiveColor{ return compat.AdaptiveColor{
Dark: fmt.Sprintf("%d", darkInt), Dark: lipgloss.Color(fmt.Sprintf("%d", darkInt)),
Light: fmt.Sprintf("%d", lightInt), Light: lipgloss.Color(fmt.Sprintf("%d", lightInt)),
}, nil }, nil
} }
if !hexColorRegex.MatchString(darkHex) || !hexColorRegex.MatchString(lightHex) { if !hexColorRegex.MatchString(darkHex) || !hexColorRegex.MatchString(lightHex) {
return lipgloss.AdaptiveColor{}, fmt.Errorf("invalid hex color format") return compat.AdaptiveColor{}, fmt.Errorf("invalid hex color format")
} }
return lipgloss.AdaptiveColor{ return compat.AdaptiveColor{
Dark: darkHex, Dark: lipgloss.Color(darkHex),
Light: lightHex, Light: lipgloss.Color(lightHex),
}, nil }, nil
} }
return lipgloss.AdaptiveColor{}, fmt.Errorf("color must be either a hex string or an object with dark/light keys") return compat.AdaptiveColor{}, fmt.Errorf("color must be either a hex string or an object with dark/light keys")
} }

View File

@@ -1,7 +1,8 @@
package theme package theme
import ( import (
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/charmbracelet/lipgloss/v2/compat"
) )
// TokyoNightTheme implements the Theme interface with Tokyo Night colors. // TokyoNightTheme implements the Theme interface with Tokyo Night colors.
@@ -70,219 +71,219 @@ func NewTokyoNightTheme() *TokyoNightTheme {
theme := &TokyoNightTheme{} theme := &TokyoNightTheme{}
// Base colors // Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{ theme.PrimaryColor = compat.AdaptiveColor{
Dark: darkBlue, Dark: lipgloss.Color(darkBlue),
Light: lightBlue, Light: lipgloss.Color(lightBlue),
} }
theme.SecondaryColor = lipgloss.AdaptiveColor{ theme.SecondaryColor = compat.AdaptiveColor{
Dark: darkPurple, Dark: lipgloss.Color(darkPurple),
Light: lightPurple, Light: lipgloss.Color(lightPurple),
} }
theme.AccentColor = lipgloss.AdaptiveColor{ theme.AccentColor = compat.AdaptiveColor{
Dark: darkOrange, Dark: lipgloss.Color(darkOrange),
Light: lightOrange, Light: lipgloss.Color(lightOrange),
} }
// Status colors // Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{ theme.ErrorColor = compat.AdaptiveColor{
Dark: darkRed, Dark: lipgloss.Color(darkRed),
Light: lightRed, Light: lipgloss.Color(lightRed),
} }
theme.WarningColor = lipgloss.AdaptiveColor{ theme.WarningColor = compat.AdaptiveColor{
Dark: darkOrange, Dark: lipgloss.Color(darkOrange),
Light: lightOrange, Light: lipgloss.Color(lightOrange),
} }
theme.SuccessColor = lipgloss.AdaptiveColor{ theme.SuccessColor = compat.AdaptiveColor{
Dark: darkGreen, Dark: lipgloss.Color(darkGreen),
Light: lightGreen, Light: lipgloss.Color(lightGreen),
} }
theme.InfoColor = lipgloss.AdaptiveColor{ theme.InfoColor = compat.AdaptiveColor{
Dark: darkBlue, Dark: lipgloss.Color(darkBlue),
Light: lightBlue, Light: lipgloss.Color(lightBlue),
} }
// Text colors // Text colors
theme.TextColor = lipgloss.AdaptiveColor{ theme.TextColor = compat.AdaptiveColor{
Dark: darkStep12, Dark: lipgloss.Color(darkStep12),
Light: lightStep12, Light: lipgloss.Color(lightStep12),
} }
theme.TextMutedColor = lipgloss.AdaptiveColor{ theme.TextMutedColor = compat.AdaptiveColor{
Dark: darkStep11, Dark: lipgloss.Color(darkStep11),
Light: lightStep11, Light: lipgloss.Color(lightStep11),
} }
// Background colors // Background colors
theme.BackgroundColor = lipgloss.AdaptiveColor{ theme.BackgroundColor = compat.AdaptiveColor{
Dark: darkStep1, Dark: lipgloss.Color(darkStep1),
Light: lightStep1, Light: lipgloss.Color(lightStep1),
} }
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{ theme.BackgroundSubtleColor = compat.AdaptiveColor{
Dark: darkStep2, Dark: lipgloss.Color(darkStep2),
Light: lightStep2, Light: lipgloss.Color(lightStep2),
} }
theme.BackgroundElementColor = lipgloss.AdaptiveColor{ theme.BackgroundElementColor = compat.AdaptiveColor{
Dark: darkStep3, Dark: lipgloss.Color(darkStep3),
Light: lightStep3, Light: lipgloss.Color(lightStep3),
} }
// Border colors // Border colors
theme.BorderColor = lipgloss.AdaptiveColor{ theme.BorderColor = compat.AdaptiveColor{
Dark: darkStep7, Dark: lipgloss.Color(darkStep7),
Light: lightStep7, Light: lipgloss.Color(lightStep7),
} }
theme.BorderActiveColor = lipgloss.AdaptiveColor{ theme.BorderActiveColor = compat.AdaptiveColor{
Dark: darkStep8, Dark: lipgloss.Color(darkStep8),
Light: lightStep8, Light: lipgloss.Color(lightStep8),
} }
theme.BorderSubtleColor = lipgloss.AdaptiveColor{ theme.BorderSubtleColor = compat.AdaptiveColor{
Dark: darkStep6, Dark: lipgloss.Color(darkStep6),
Light: lightStep6, Light: lipgloss.Color(lightStep6),
} }
// Diff view colors // Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{ theme.DiffAddedColor = compat.AdaptiveColor{
Dark: "#4fd6be", // teal from palette Dark: lipgloss.Color("#4fd6be"), // teal from palette
Light: "#1e725c", Light: lipgloss.Color("#1e725c"),
} }
theme.DiffRemovedColor = lipgloss.AdaptiveColor{ theme.DiffRemovedColor = compat.AdaptiveColor{
Dark: "#c53b53", // red1 from palette Dark: lipgloss.Color("#c53b53"), // red1 from palette
Light: "#c53b53", Light: lipgloss.Color("#c53b53"),
} }
theme.DiffContextColor = lipgloss.AdaptiveColor{ theme.DiffContextColor = compat.AdaptiveColor{
Dark: "#828bb8", // fg_dark from palette Dark: lipgloss.Color("#828bb8"), // fg_dark from palette
Light: "#7086b5", Light: lipgloss.Color("#7086b5"),
} }
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{ theme.DiffHunkHeaderColor = compat.AdaptiveColor{
Dark: "#828bb8", // fg_dark from palette Dark: lipgloss.Color("#828bb8"), // fg_dark from palette
Light: "#7086b5", Light: lipgloss.Color("#7086b5"),
} }
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{ theme.DiffHighlightAddedColor = compat.AdaptiveColor{
Dark: "#b8db87", // git.add from palette Dark: lipgloss.Color("#b8db87"), // git.add from palette
Light: "#4db380", Light: lipgloss.Color("#4db380"),
} }
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{ theme.DiffHighlightRemovedColor = compat.AdaptiveColor{
Dark: "#e26a75", // git.delete from palette Dark: lipgloss.Color("#e26a75"), // git.delete from palette
Light: "#f52a65", Light: lipgloss.Color("#f52a65"),
} }
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{ theme.DiffAddedBgColor = compat.AdaptiveColor{
Dark: "#20303b", Dark: lipgloss.Color("#20303b"),
Light: "#d5e5d5", Light: lipgloss.Color("#d5e5d5"),
} }
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{ theme.DiffRemovedBgColor = compat.AdaptiveColor{
Dark: "#37222c", Dark: lipgloss.Color("#37222c"),
Light: "#f7d8db", Light: lipgloss.Color("#f7d8db"),
} }
theme.DiffContextBgColor = lipgloss.AdaptiveColor{ theme.DiffContextBgColor = compat.AdaptiveColor{
Dark: darkStep2, Dark: lipgloss.Color(darkStep2),
Light: lightStep2, Light: lipgloss.Color(lightStep2),
} }
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{ theme.DiffLineNumberColor = compat.AdaptiveColor{
Dark: darkStep3, // dark3 from palette Dark: lipgloss.Color(darkStep3), // dark3 from palette
Light: lightStep3, Light: lipgloss.Color(lightStep3),
} }
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{ theme.DiffAddedLineNumberBgColor = compat.AdaptiveColor{
Dark: "#1b2b34", Dark: lipgloss.Color("#1b2b34"),
Light: "#c5d5c5", Light: lipgloss.Color("#c5d5c5"),
} }
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{ theme.DiffRemovedLineNumberBgColor = compat.AdaptiveColor{
Dark: "#2d1f26", Dark: lipgloss.Color("#2d1f26"),
Light: "#e7c8cb", Light: lipgloss.Color("#e7c8cb"),
} }
// Markdown colors // Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{ theme.MarkdownTextColor = compat.AdaptiveColor{
Dark: darkStep12, Dark: lipgloss.Color(darkStep12),
Light: lightStep12, Light: lipgloss.Color(lightStep12),
} }
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{ theme.MarkdownHeadingColor = compat.AdaptiveColor{
Dark: darkPurple, Dark: lipgloss.Color(darkPurple),
Light: lightPurple, Light: lipgloss.Color(lightPurple),
} }
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{ theme.MarkdownLinkColor = compat.AdaptiveColor{
Dark: darkBlue, Dark: lipgloss.Color(darkBlue),
Light: lightBlue, Light: lipgloss.Color(lightBlue),
} }
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{ theme.MarkdownLinkTextColor = compat.AdaptiveColor{
Dark: darkCyan, Dark: lipgloss.Color(darkCyan),
Light: lightCyan, Light: lipgloss.Color(lightCyan),
} }
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{ theme.MarkdownCodeColor = compat.AdaptiveColor{
Dark: darkGreen, Dark: lipgloss.Color(darkGreen),
Light: lightGreen, Light: lipgloss.Color(lightGreen),
} }
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{ theme.MarkdownBlockQuoteColor = compat.AdaptiveColor{
Dark: darkYellow, Dark: lipgloss.Color(darkYellow),
Light: lightYellow, Light: lipgloss.Color(lightYellow),
} }
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{ theme.MarkdownEmphColor = compat.AdaptiveColor{
Dark: darkYellow, Dark: lipgloss.Color(darkYellow),
Light: lightYellow, Light: lipgloss.Color(lightYellow),
} }
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{ theme.MarkdownStrongColor = compat.AdaptiveColor{
Dark: darkOrange, Dark: lipgloss.Color(darkOrange),
Light: lightOrange, Light: lipgloss.Color(lightOrange),
} }
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{ theme.MarkdownHorizontalRuleColor = compat.AdaptiveColor{
Dark: darkStep11, Dark: lipgloss.Color(darkStep11),
Light: lightStep11, Light: lipgloss.Color(lightStep11),
} }
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{ theme.MarkdownListItemColor = compat.AdaptiveColor{
Dark: darkBlue, Dark: lipgloss.Color(darkBlue),
Light: lightBlue, Light: lipgloss.Color(lightBlue),
} }
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{ theme.MarkdownListEnumerationColor = compat.AdaptiveColor{
Dark: darkCyan, Dark: lipgloss.Color(darkCyan),
Light: lightCyan, Light: lipgloss.Color(lightCyan),
} }
theme.MarkdownImageColor = lipgloss.AdaptiveColor{ theme.MarkdownImageColor = compat.AdaptiveColor{
Dark: darkBlue, Dark: lipgloss.Color(darkBlue),
Light: lightBlue, Light: lipgloss.Color(lightBlue),
} }
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{ theme.MarkdownImageTextColor = compat.AdaptiveColor{
Dark: darkCyan, Dark: lipgloss.Color(darkCyan),
Light: lightCyan, Light: lipgloss.Color(lightCyan),
} }
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{ theme.MarkdownCodeBlockColor = compat.AdaptiveColor{
Dark: darkStep12, Dark: lipgloss.Color(darkStep12),
Light: lightStep12, Light: lipgloss.Color(lightStep12),
} }
// Syntax highlighting colors // Syntax highlighting colors
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{ theme.SyntaxCommentColor = compat.AdaptiveColor{
Dark: darkStep11, Dark: lipgloss.Color(darkStep11),
Light: lightStep11, Light: lipgloss.Color(lightStep11),
} }
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{ theme.SyntaxKeywordColor = compat.AdaptiveColor{
Dark: darkPurple, Dark: lipgloss.Color(darkPurple),
Light: lightPurple, Light: lipgloss.Color(lightPurple),
} }
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{ theme.SyntaxFunctionColor = compat.AdaptiveColor{
Dark: darkBlue, Dark: lipgloss.Color(darkBlue),
Light: lightBlue, Light: lipgloss.Color(lightBlue),
} }
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{ theme.SyntaxVariableColor = compat.AdaptiveColor{
Dark: darkRed, Dark: lipgloss.Color(darkRed),
Light: lightRed, Light: lipgloss.Color(lightRed),
} }
theme.SyntaxStringColor = lipgloss.AdaptiveColor{ theme.SyntaxStringColor = compat.AdaptiveColor{
Dark: darkGreen, Dark: lipgloss.Color(darkGreen),
Light: lightGreen, Light: lipgloss.Color(lightGreen),
} }
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{ theme.SyntaxNumberColor = compat.AdaptiveColor{
Dark: darkOrange, Dark: lipgloss.Color(darkOrange),
Light: lightOrange, Light: lipgloss.Color(lightOrange),
} }
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{ theme.SyntaxTypeColor = compat.AdaptiveColor{
Dark: darkYellow, Dark: lipgloss.Color(darkYellow),
Light: lightYellow, Light: lipgloss.Color(lightYellow),
} }
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{ theme.SyntaxOperatorColor = compat.AdaptiveColor{
Dark: darkCyan, Dark: lipgloss.Color(darkCyan),
Light: lightCyan, Light: lipgloss.Color(lightCyan),
} }
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{ theme.SyntaxPunctuationColor = compat.AdaptiveColor{
Dark: darkStep12, Dark: lipgloss.Color(darkStep12),
Light: lightStep12, Light: lipgloss.Color(lightStep12),
} }
return theme return theme

View File

@@ -1,272 +0,0 @@
package theme
import (
"github.com/charmbracelet/lipgloss"
)
// TronTheme implements the Theme interface with Tron-inspired colors.
// It provides both dark and light variants, though Tron is primarily a dark theme.
type TronTheme struct {
BaseTheme
}
// NewTronTheme creates a new instance of the Tron theme.
func NewTronTheme() *TronTheme {
// Tron color palette
// Inspired by the Tron movie's neon aesthetic
darkBackground := "#0c141f"
darkCurrentLine := "#1a2633"
darkSelection := "#1a2633"
darkForeground := "#caf0ff"
darkComment := "#4d6b87"
darkCyan := "#00d9ff"
darkBlue := "#007fff"
darkOrange := "#ff9000"
darkPink := "#ff00a0"
darkPurple := "#b73fff"
darkRed := "#ff3333"
darkYellow := "#ffcc00"
darkGreen := "#00ff8f"
darkBorder := "#1a2633"
// Light mode approximation
lightBackground := "#f0f8ff"
lightCurrentLine := "#e0f0ff"
lightSelection := "#d0e8ff"
lightForeground := "#0c141f"
lightComment := "#4d6b87"
lightCyan := "#0097b3"
lightBlue := "#0066cc"
lightOrange := "#cc7300"
lightPink := "#cc0080"
lightPurple := "#9932cc"
lightRed := "#cc2929"
lightYellow := "#cc9900"
lightGreen := "#00cc72"
lightBorder := "#d0e8ff"
theme := &TronTheme{}
// Base colors
theme.PrimaryColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.SecondaryColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.AccentColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
// Status colors
theme.ErrorColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.WarningColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.SuccessColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.InfoColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
// Text colors
theme.TextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.TextMutedColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
// Background colors
theme.BackgroundColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.BackgroundSubtleColor = lipgloss.AdaptiveColor{
Dark: darkCurrentLine,
Light: lightCurrentLine,
}
theme.BackgroundElementColor = lipgloss.AdaptiveColor{
Dark: "#070d14", // Slightly darker than background
Light: "#ffffff", // Slightly lighter than background
}
// Border colors
theme.BorderColor = lipgloss.AdaptiveColor{
Dark: darkBorder,
Light: lightBorder,
}
theme.BorderActiveColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.BorderSubtleColor = lipgloss.AdaptiveColor{
Dark: darkSelection,
Light: lightSelection,
}
// Diff view colors
theme.DiffAddedColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.DiffRemovedColor = lipgloss.AdaptiveColor{
Dark: darkRed,
Light: lightRed,
}
theme.DiffContextColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.DiffHunkHeaderColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.DiffHighlightAddedColor = lipgloss.AdaptiveColor{
Dark: "#00ff8f",
Light: "#a5d6a7",
}
theme.DiffHighlightRemovedColor = lipgloss.AdaptiveColor{
Dark: "#ff3333",
Light: "#ef9a9a",
}
theme.DiffAddedBgColor = lipgloss.AdaptiveColor{
Dark: "#0a2a1a",
Light: "#e8f5e9",
}
theme.DiffRemovedBgColor = lipgloss.AdaptiveColor{
Dark: "#2a0a0a",
Light: "#ffebee",
}
theme.DiffContextBgColor = lipgloss.AdaptiveColor{
Dark: darkBackground,
Light: lightBackground,
}
theme.DiffLineNumberColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.DiffAddedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#082015",
Light: "#c8e6c9",
}
theme.DiffRemovedLineNumberBgColor = lipgloss.AdaptiveColor{
Dark: "#200808",
Light: "#ffcdd2",
}
// Markdown colors
theme.MarkdownTextColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
theme.MarkdownHeadingColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownLinkColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownLinkTextColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownCodeColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.MarkdownBlockQuoteColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.MarkdownEmphColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.MarkdownStrongColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.MarkdownHorizontalRuleColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.MarkdownListItemColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownListEnumerationColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownImageColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.MarkdownImageTextColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.MarkdownCodeBlockColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
// Syntax highlighting colors
theme.SyntaxCommentColor = lipgloss.AdaptiveColor{
Dark: darkComment,
Light: lightComment,
}
theme.SyntaxKeywordColor = lipgloss.AdaptiveColor{
Dark: darkCyan,
Light: lightCyan,
}
theme.SyntaxFunctionColor = lipgloss.AdaptiveColor{
Dark: darkGreen,
Light: lightGreen,
}
theme.SyntaxVariableColor = lipgloss.AdaptiveColor{
Dark: darkOrange,
Light: lightOrange,
}
theme.SyntaxStringColor = lipgloss.AdaptiveColor{
Dark: darkYellow,
Light: lightYellow,
}
theme.SyntaxNumberColor = lipgloss.AdaptiveColor{
Dark: darkBlue,
Light: lightBlue,
}
theme.SyntaxTypeColor = lipgloss.AdaptiveColor{
Dark: darkPurple,
Light: lightPurple,
}
theme.SyntaxOperatorColor = lipgloss.AdaptiveColor{
Dark: darkPink,
Light: lightPink,
}
theme.SyntaxPunctuationColor = lipgloss.AdaptiveColor{
Dark: darkForeground,
Light: lightForeground,
}
return theme
}
func init() {
// Register the Tron theme with the theme manager
RegisterTheme("tron", NewTronTheme())
}

View File

@@ -5,11 +5,11 @@ import (
"log/slog" "log/slog"
"strings" "strings"
"github.com/charmbracelet/bubbles/cursor" "github.com/charmbracelet/bubbles/v2/cursor"
"github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/v2/key"
"github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/v2/spinner"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app" "github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/components/core" "github.com/sst/opencode/internal/components/core"
@@ -18,6 +18,8 @@ import (
"github.com/sst/opencode/internal/page" "github.com/sst/opencode/internal/page"
"github.com/sst/opencode/internal/state" "github.com/sst/opencode/internal/state"
"github.com/sst/opencode/internal/status" "github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
"github.com/sst/opencode/internal/util" "github.com/sst/opencode/internal/util"
"github.com/sst/opencode/pkg/client" "github.com/sst/opencode/pkg/client"
) )
@@ -90,16 +92,16 @@ type appModel struct {
width, height int width, height int
currentPage page.PageID currentPage page.PageID
previousPage page.PageID previousPage page.PageID
pages map[page.PageID]tea.Model pages map[page.PageID]layout.ModelWithView
loadedPages map[page.PageID]bool loadedPages map[page.PageID]bool
status core.StatusCmp status core.StatusComponent
app *app.App app *app.App
showPermissions bool showPermissions bool
permissions dialog.PermissionDialogCmp permissions dialog.PermissionDialogComponent
showHelp bool showHelp bool
help dialog.HelpCmp help dialog.HelpComponent
showQuit bool showQuit bool
quit dialog.QuitDialog quit dialog.QuitDialog
@@ -118,7 +120,7 @@ type appModel struct {
initDialog dialog.InitDialogCmp initDialog dialog.InitDialogCmp
showFilepicker bool showFilepicker bool
filepicker dialog.FilepickerCmp filepicker dialog.FilepickerComponent
showThemeDialog bool showThemeDialog bool
themeDialog dialog.ThemeDialog themeDialog dialog.ThemeDialog
@@ -131,7 +133,12 @@ type appModel struct {
} }
func (a appModel) Init() tea.Cmd { func (a appModel) Init() tea.Cmd {
t := theme.CurrentTheme()
var cmds []tea.Cmd var cmds []tea.Cmd
cmds = append(cmds, tea.SetBackgroundColor(t.Background()))
// cmds = append(cmds, tea.SetForegroundColor(t.Background()))
cmds = append(cmds, tea.RequestBackgroundColor)
cmd := a.pages[a.currentPage].Init() cmd := a.pages[a.currentPage].Init()
a.loadedPages[a.currentPage] = true a.loadedPages[a.currentPage] = true
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
@@ -170,13 +177,14 @@ func (a appModel) updateAllPages(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd var cmd tea.Cmd
for id := range a.pages { for id := range a.pages {
a.pages[id], cmd = a.pages[id].Update(msg) updated, cmd := a.pages[id].Update(msg)
a.pages[id] = updated.(layout.ModelWithView)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
} }
s, cmd := a.status.Update(msg) s, cmd := a.status.Update(msg)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
a.status = s.(core.StatusCmp) a.status = s.(core.StatusComponent)
return a, tea.Batch(cmds...) return a, tea.Batch(cmds...)
} }
@@ -185,6 +193,10 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd var cmds []tea.Cmd
var cmd tea.Cmd var cmd tea.Cmd
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.BackgroundColorMsg:
styles.Terminal = &styles.TerminalInfo{
BackgroundIsDark: msg.IsDark(),
}
case cursor.BlinkMsg: case cursor.BlinkMsg:
return a.updateAllPages(msg) return a.updateAllPages(msg)
case spinner.TickMsg: case spinner.TickMsg:
@@ -234,16 +246,17 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
s, _ := a.status.Update(msg) s, _ := a.status.Update(msg)
a.status = s.(core.StatusCmp) a.status = s.(core.StatusComponent)
a.pages[a.currentPage], cmd = a.pages[a.currentPage].Update(msg) updated, cmd := a.pages[a.currentPage].Update(msg)
a.pages[a.currentPage] = updated.(layout.ModelWithView)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
prm, permCmd := a.permissions.Update(msg) prm, permCmd := a.permissions.Update(msg)
a.permissions = prm.(dialog.PermissionDialogCmp) a.permissions = prm.(dialog.PermissionDialogComponent)
cmds = append(cmds, permCmd) cmds = append(cmds, permCmd)
help, helpCmd := a.help.Update(msg) help, helpCmd := a.help.Update(msg)
a.help = help.(dialog.HelpCmp) a.help = help.(dialog.HelpComponent)
cmds = append(cmds, helpCmd) cmds = append(cmds, helpCmd)
session, sessionCmd := a.sessionDialog.Update(msg) session, sessionCmd := a.sessionDialog.Update(msg)
@@ -255,7 +268,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, commandCmd) cmds = append(cmds, commandCmd)
filepicker, filepickerCmd := a.filepicker.Update(msg) filepicker, filepickerCmd := a.filepicker.Update(msg)
a.filepicker = filepicker.(dialog.FilepickerCmp) a.filepicker = filepicker.(dialog.FilepickerComponent)
cmds = append(cmds, filepickerCmd) cmds = append(cmds, filepickerCmd)
a.initDialog.SetSize(msg.Width, msg.Height) a.initDialog.SetSize(msg.Width, msg.Height)
@@ -342,10 +355,19 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
a.app.Config.Theme = msg.ThemeName a.app.Config.Theme = msg.ThemeName
a.app.SaveConfig() a.app.SaveConfig()
a.pages[a.currentPage], cmd = a.pages[a.currentPage].Update(msg) updated, cmd := a.pages[a.currentPage].Update(msg)
if cmd != nil {
cmds = append(cmds, cmd)
}
t := theme.CurrentTheme()
cmds = append(cmds, tea.SetBackgroundColor(t.Background()))
// cmds = append(cmds, tea.RequestBackgroundColor)
a.pages[a.currentPage] = updated.(layout.ModelWithView)
a.showThemeDialog = false a.showThemeDialog = false
status.Info("Theme changed to: " + msg.ThemeName) status.Info("Theme changed to: " + msg.ThemeName)
return a, cmd return a, tea.Batch(cmds...)
case dialog.ShowInitDialogMsg: case dialog.ShowInitDialogMsg:
a.showInitDialog = msg.Show a.showInitDialog = msg.Show
@@ -597,13 +619,13 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
default: default:
f, filepickerCmd := a.filepicker.Update(msg) f, filepickerCmd := a.filepicker.Update(msg)
a.filepicker = f.(dialog.FilepickerCmp) a.filepicker = f.(dialog.FilepickerComponent)
cmds = append(cmds, filepickerCmd) cmds = append(cmds, filepickerCmd)
} }
if a.showFilepicker { if a.showFilepicker {
f, filepickerCmd := a.filepicker.Update(msg) f, filepickerCmd := a.filepicker.Update(msg)
a.filepicker = f.(dialog.FilepickerCmp) a.filepicker = f.(dialog.FilepickerComponent)
cmds = append(cmds, filepickerCmd) cmds = append(cmds, filepickerCmd)
// Only block key messages send all other messages down // Only block key messages send all other messages down
if _, ok := msg.(tea.KeyMsg); ok { if _, ok := msg.(tea.KeyMsg); ok {
@@ -623,7 +645,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if a.showPermissions { if a.showPermissions {
d, permissionsCmd := a.permissions.Update(msg) d, permissionsCmd := a.permissions.Update(msg)
a.permissions = d.(dialog.PermissionDialogCmp) a.permissions = d.(dialog.PermissionDialogComponent)
cmds = append(cmds, permissionsCmd) cmds = append(cmds, permissionsCmd)
// Only block key messages send all other messages down // Only block key messages send all other messages down
if _, ok := msg.(tea.KeyMsg); ok { if _, ok := msg.(tea.KeyMsg); ok {
@@ -693,9 +715,10 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
s, cmd := a.status.Update(msg) s, cmd := a.status.Update(msg)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
a.status = s.(core.StatusCmp) a.status = s.(core.StatusComponent)
a.pages[a.currentPage], cmd = a.pages[a.currentPage].Update(msg) updated, cmd := a.pages[a.currentPage].Update(msg)
a.pages[a.currentPage] = updated.(layout.ModelWithView)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
return a, tea.Batch(cmds...) return a, tea.Batch(cmds...)
} }
@@ -930,7 +953,7 @@ func NewModel(app *app.App) tea.Model {
toolsDialog: dialog.NewToolsDialogCmp(), toolsDialog: dialog.NewToolsDialogCmp(),
app: app, app: app,
commands: []dialog.Command{}, commands: []dialog.Command{},
pages: map[page.PageID]tea.Model{ pages: map[page.PageID]layout.ModelWithView{
page.ChatPage: page.NewChatPage(app), page.ChatPage: page.NewChatPage(app),
}, },
filepicker: dialog.NewFilepickerCmp(app), filepicker: dialog.NewFilepickerCmp(app),

View File

@@ -1,7 +1,7 @@
package util package util
import ( import (
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea/v2"
) )
func CmdHandler(msg tea.Msg) tea.Cmd { func CmdHandler(msg tea.Msg) tea.Cmd {