summaryrefslogtreecommitdiffhomepage
path: root/packages/tui/internal/components
diff options
context:
space:
mode:
authoradamdottv <[email protected]>2025-06-12 05:35:40 -0500
committeradamdottv <[email protected]>2025-06-12 16:00:24 -0500
commitcce2e4ad754479917fc8f6f24c1421cf19c04573 (patch)
tree62c0ec32358932a468af73b44fd28e7de7859bbe /packages/tui/internal/components
parenta1ce35c208bf9ebca37f722e845035bd7fd5e801 (diff)
downloadopencode-cce2e4ad754479917fc8f6f24c1421cf19c04573.tar.gz
opencode-cce2e4ad754479917fc8f6f24c1421cf19c04573.zip
wip: refactoring tui
Diffstat (limited to 'packages/tui/internal/components')
-rw-r--r--packages/tui/internal/components/chat/editor.go61
-rw-r--r--packages/tui/internal/components/chat/message.go49
-rw-r--r--packages/tui/internal/components/chat/messages.go116
-rw-r--r--packages/tui/internal/components/core/status.go129
-rw-r--r--packages/tui/internal/components/dialog/arguments.go41
-rw-r--r--packages/tui/internal/components/dialog/commands.go22
-rw-r--r--packages/tui/internal/components/dialog/complete.go12
-rw-r--r--packages/tui/internal/components/dialog/custom_commands.go2
-rw-r--r--packages/tui/internal/components/dialog/filepicker.go49
-rw-r--r--packages/tui/internal/components/dialog/help.go31
-rw-r--r--packages/tui/internal/components/dialog/init.go6
-rw-r--r--packages/tui/internal/components/dialog/models.go34
-rw-r--r--packages/tui/internal/components/dialog/permission.go60
-rw-r--r--packages/tui/internal/components/dialog/quit.go20
-rw-r--r--packages/tui/internal/components/dialog/session.go24
-rw-r--r--packages/tui/internal/components/dialog/theme.go20
-rw-r--r--packages/tui/internal/components/dialog/tools.go40
-rw-r--r--packages/tui/internal/components/diff/diff.go20
-rw-r--r--packages/tui/internal/components/qr/qr.go2
-rw-r--r--packages/tui/internal/components/util/simple-list.go8
20 files changed, 284 insertions, 462 deletions
diff --git a/packages/tui/internal/components/chat/editor.go b/packages/tui/internal/components/chat/editor.go
index 52f198492..198958b09 100644
--- a/packages/tui/internal/components/chat/editor.go
+++ b/packages/tui/internal/components/chat/editor.go
@@ -5,15 +5,13 @@ import (
"log/slog"
"os"
"os/exec"
- "slices"
"strings"
- "unicode"
- "github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/spinner"
- "github.com/charmbracelet/bubbles/textarea"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ "github.com/charmbracelet/bubbles/v2/spinner"
+ "github.com/charmbracelet/bubbles/v2/textarea"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/components/dialog"
"github.com/sst/opencode/internal/image"
@@ -129,18 +127,18 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.attachments = nil
return m, nil
}
- if m.deleteMode && len(msg.Runes) > 0 && unicode.IsDigit(msg.Runes[0]) {
- num := int(msg.Runes[0] - '0')
- m.deleteMode = false
- if num < 10 && len(m.attachments) > num {
- if num == 0 {
- m.attachments = m.attachments[num+1:]
- } else {
- m.attachments = slices.Delete(m.attachments, num, num+1)
- }
- return m, nil
- }
- }
+ // if m.deleteMode && len(msg.Runes) > 0 && unicode.IsDigit(msg.Runes[0]) {
+ // num := int(msg.Runes[0] - '0')
+ // m.deleteMode = false
+ // if num < 10 && len(m.attachments) > num {
+ // if num == 0 {
+ // m.attachments = m.attachments[num+1:]
+ // } else {
+ // m.attachments = slices.Delete(m.attachments, num, num+1)
+ // }
+ // return m, nil
+ // }
+ // }
if key.Matches(msg, messageKeys.PageUp) || key.Matches(msg, messageKeys.PageDown) ||
key.Matches(msg, messageKeys.HalfPageUp) || key.Matches(msg, messageKeys.HalfPageDown) {
return m, nil
@@ -258,7 +256,7 @@ func (m *editorComponent) View() string {
m.textarea.View(),
)
textarea = styles.BaseStyle().
- Width(m.width-2).
+ Width(m.width). // -2).
Border(lipgloss.NormalBorder(), true, true).
BorderForeground(t.Border()).
Render(textarea)
@@ -286,10 +284,7 @@ func (m *editorComponent) View() string {
info,
)
- return styles.ForceReplaceBackgroundWithLipgloss(
- content,
- t.Background(),
- )
+ return content
}
func (m *editorComponent) SetSize(width, height int) tea.Cmd {
@@ -414,14 +409,14 @@ func createTextArea(existing *textarea.Model) textarea.Model {
ta := textarea.New()
ta.Placeholder = "It's prompting time..."
- ta.BlurredStyle.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor)
- ta.BlurredStyle.CursorLine = styles.BaseStyle().Background(bgColor)
- ta.BlurredStyle.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor)
- ta.BlurredStyle.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor)
- ta.FocusedStyle.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor)
- ta.FocusedStyle.CursorLine = styles.BaseStyle().Background(bgColor)
- ta.FocusedStyle.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor)
- ta.FocusedStyle.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor)
+ ta.Styles.Blurred.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor)
+ ta.Styles.Blurred.CursorLine = styles.BaseStyle().Background(bgColor)
+ ta.Styles.Blurred.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor)
+ ta.Styles.Blurred.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor)
+ ta.Styles.Focused.Base = styles.BaseStyle().Background(bgColor).Foreground(textColor)
+ ta.Styles.Focused.CursorLine = styles.BaseStyle().Background(bgColor)
+ ta.Styles.Focused.Placeholder = styles.BaseStyle().Background(bgColor).Foreground(textMutedColor)
+ ta.Styles.Focused.Text = styles.BaseStyle().Background(bgColor).Foreground(textColor)
ta.Prompt = " "
ta.ShowLineNumbers = false
@@ -437,7 +432,7 @@ func createTextArea(existing *textarea.Model) textarea.Model {
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)))
ta := createTextArea(nil)
diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go
index 131898c84..199b4a070 100644
--- a/packages/tui/internal/components/chat/message.go
+++ b/packages/tui/internal/components/chat/message.go
@@ -9,7 +9,8 @@ import (
"time"
"unicode"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/lipgloss/v2"
+ "github.com/charmbracelet/lipgloss/v2/compat"
"github.com/charmbracelet/x/ansi"
"github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/components/diff"
@@ -21,8 +22,8 @@ import (
"golang.org/x/text/language"
)
-func toMarkdown(content string, width int) string {
- r := styles.GetMarkdownRenderer(width)
+func toMarkdown(content string, width int, backgroundColor compat.AdaptiveColor) string {
+ r := styles.GetMarkdownRenderer(width, backgroundColor)
content = strings.ReplaceAll(content, app.Info.Path.Root+"/", "")
rendered, _ := r.Render(content)
lines := strings.Split(rendered, "\n")
@@ -50,7 +51,7 @@ func toMarkdown(content string, width int) string {
type blockRenderer struct {
align *lipgloss.Position
- borderColor *lipgloss.AdaptiveColor
+ borderColor *compat.AdaptiveColor
fullWidth bool
paddingTop 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) {
c.borderColor = &color
}
@@ -137,9 +138,8 @@ func renderContentBlock(content string, options ...renderingOption) string {
BorderLeftBackground(t.Background())
}
- content = styles.ForceReplaceBackgroundWithLipgloss(content, t.BackgroundSubtle())
if renderer.fullWidth {
- style = style.Width(layout.Current.Container.Width - 2)
+ style = style.Width(layout.Current.Container.Width)
}
content = style.Render(content)
if renderer.paddingTop > 0 {
@@ -152,13 +152,11 @@ func renderContentBlock(content string, options ...renderingOption) string {
layout.Current.Container.Width,
align,
content,
- lipgloss.WithWhitespaceBackground(t.Background()),
)
content = lipgloss.PlaceHorizontal(
layout.Current.Viewport.Width,
lipgloss.Center,
content,
- lipgloss.WithWhitespaceBackground(t.Background()),
)
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
timestamp = timestamp[12:]
}
- info := styles.BaseStyle().
- Foreground(t.TextMuted()).
- Render(fmt.Sprintf("%s (%s)", author, timestamp))
+ info := fmt.Sprintf("%s (%s)", author, timestamp)
align := lipgloss.Left
switch message.Role {
@@ -195,7 +191,7 @@ func renderText(message client.MessageInfo, text string, author string) string {
textWidth := lipgloss.Width(text)
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)
switch message.Role {
@@ -313,7 +309,7 @@ func renderToolInvocation(
lipgloss.Center,
lipgloss.Center,
body,
- lipgloss.WithWhitespaceBackground(t.Background()),
+ lipgloss.WithWhitespaceStyle(lipgloss.NewStyle().Background(t.Background())),
)
}
case "opencode_write":
@@ -328,7 +324,7 @@ func renderToolInvocation(
command := toolArgsMap["command"].(string)
stdout := metadata["stdout"].(string)
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))
}
case "opencode_webfetch":
@@ -336,7 +332,7 @@ func renderToolInvocation(
format := toolArgsMap["format"].(string)
body = truncateHeight(body, 10)
if format == "html" || format == "markdown" {
- body = toMarkdown(body, innerWidth)
+ body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
}
body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
case "opencode_todowrite":
@@ -356,7 +352,7 @@ func renderToolInvocation(
body += fmt.Sprintf("- [ ] %s\n", content)
}
}
- body = toMarkdown(body, innerWidth)
+ body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
body = renderContentBlock(body, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
}
default:
@@ -368,7 +364,7 @@ func renderToolInvocation(
content := style.Render(title)
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 != "" {
content += "\n" + body
}
@@ -411,6 +407,7 @@ func WithTruncate(height int) fileRenderingOption {
}
func renderFile(filename string, content string, options ...fileRenderingOption) string {
+ t := theme.CurrentTheme()
renderer := &fileRenderer{
filename: filename,
content: content,
@@ -419,7 +416,6 @@ func renderFile(filename string, content string, options ...fileRenderingOption)
option(renderer)
}
- // TODO: is this even needed?
lines := []string{}
for line := range strings.SplitSeq(content, "\n") {
line = strings.TrimRightFunc(line, unicode.IsSpace)
@@ -428,23 +424,12 @@ func renderFile(filename string, content string, options ...fileRenderingOption)
}
content = strings.Join(lines, "\n")
- width := layout.Current.Container.Width - 6
+ width := layout.Current.Container.Width - 8
if renderer.height > 0 {
content = truncateHeight(content, renderer.height)
}
content = fmt.Sprintf("```%s\n%s\n```", extension(renderer.filename), content)
- content = toMarkdown(content, width)
-
- // 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")
+ content = toMarkdown(content, width, t.BackgroundSubtle())
return renderContentBlock(content, WithFullWidth(), WithPaddingTop(1), WithPaddingBottom(1))
}
diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go
index 093c8cf94..0036d0669 100644
--- a/packages/tui/internal/components/chat/messages.go
+++ b/packages/tui/internal/components/chat/messages.go
@@ -4,11 +4,11 @@ import (
"strings"
"time"
- "github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/spinner"
- "github.com/charmbracelet/bubbles/viewport"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ "github.com/charmbracelet/bubbles/v2/spinner"
+ "github.com/charmbracelet/bubbles/v2/viewport"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/components/dialog"
"github.com/sst/opencode/internal/layout"
@@ -220,11 +220,11 @@ func (m *messagesComponent) renderView() {
m.width,
lipgloss.Center,
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"))
}
@@ -238,7 +238,7 @@ func (m *messagesComponent) header() string {
base := styles.BaseStyle().Render
muted := styles.Muted().Render
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 != "" {
headerLines = append(headerLines, muted(m.app.Session.Share.Url))
} else {
@@ -255,7 +255,7 @@ func (m *messagesComponent) header() string {
Background(t.Background()).
Render(header)
- return styles.ForceReplaceBackgroundWithLipgloss(header, t.Background())
+ return header
}
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 {
- t := theme.CurrentTheme()
+ // t := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
base := baseStyle.Render
muted := styles.Muted().Render
@@ -398,16 +333,13 @@ func (m *messagesComponent) home() string {
lines = append(lines, "")
}
- return styles.ForceReplaceBackgroundWithLipgloss(
- lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center,
- baseStyle.Width(lipgloss.Width(logoAndVersion)).Render(
- lipgloss.JoinVertical(
- lipgloss.Top,
- lines...,
- ),
- )),
- t.Background(),
- )
+ return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center,
+ baseStyle.Width(lipgloss.Width(logoAndVersion)).Render(
+ lipgloss.JoinVertical(
+ lipgloss.Top,
+ lines...,
+ ),
+ ))
}
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.height = height
- m.viewport.Width = width
- m.viewport.Height = height - lipgloss.Height(m.header())
- m.attachments.Width = width + 40
- m.attachments.Height = 3
+ m.viewport.SetWidth(width)
+ m.viewport.SetHeight(height - lipgloss.Height(m.header()))
+ m.attachments.SetWidth(width + 40)
+ m.attachments.SetHeight(3)
m.renderView()
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{
Frames: []string{" ", "┃", "┃"},
FPS: time.Second / 3,
}
s := spinner.New(spinner.WithSpinner(customSpinner))
- vp := viewport.New(0, 0)
- attachments := viewport.New(0, 0)
+ vp := viewport.New() //(0, 0)
+ attachments := viewport.New() //(0, 0)
vp.KeyMap.PageUp = messageKeys.PageUp
vp.KeyMap.PageDown = messageKeys.PageDown
vp.KeyMap.HalfPageUp = messageKeys.HalfPageUp
diff --git a/packages/tui/internal/components/core/status.go b/packages/tui/internal/components/core/status.go
index 10a123f02..2b9929222 100644
--- a/packages/tui/internal/components/core/status.go
+++ b/packages/tui/internal/components/core/status.go
@@ -5,20 +5,21 @@ import (
"strings"
"time"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app"
+ "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/pubsub"
"github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
)
-type StatusCmp interface {
- tea.Model
+type StatusComponent interface {
+ layout.ModelWithView
}
-type statusCmp struct {
+type statusComponent struct {
app *app.App
queue []status.StatusMessage
width int
@@ -27,7 +28,7 @@ type statusCmp struct {
}
// 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 statusCleanupMsg{time: t}
})
@@ -38,11 +39,11 @@ type statusCleanupMsg struct {
time time.Time
}
-func (m statusCmp) Init() tea.Cmd {
+func (m statusComponent) Init() tea.Cmd {
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) {
case tea.WindowSizeMsg:
m.width = msg.Width
@@ -104,10 +105,9 @@ func logo() string {
open := styles.Muted().Render("open")
code := styles.BaseStyle().Bold(true).Render("code")
version := styles.Muted().Render(app.Info.Version)
- return styles.ForceReplaceBackgroundWithLipgloss(
- styles.Padded().Render(mark+open+code+" "+version),
- t.BackgroundElement(),
- )
+ return styles.Padded().
+ Background(t.BackgroundElement()).
+ Render(mark + open + code + " " + version)
}
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)
}
-func (m statusCmp) View() string {
+func (m statusComponent) View() string {
if m.app.Session.Id == "" {
return styles.BaseStyle().
Width(m.width).
@@ -250,107 +250,8 @@ func (m statusCmp) View() string {
// }
}
-func (m *statusCmp) projectDiagnostics() string {
- t := theme.CurrentTheme()
-
- // 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{
+func NewStatusCmp(app *app.App) StatusComponent {
+ statusComponent := &statusComponent{
app: app,
queue: []status.StatusMessage{},
messageTTL: 4 * time.Second,
diff --git a/packages/tui/internal/components/dialog/arguments.go b/packages/tui/internal/components/dialog/arguments.go
index 2faa36070..4ef21fcf4 100644
--- a/packages/tui/internal/components/dialog/arguments.go
+++ b/packages/tui/internal/components/dialog/arguments.go
@@ -2,10 +2,10 @@ package dialog
import (
"fmt"
- "github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/textinput"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ "github.com/charmbracelet/bubbles/v2/textinput"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
@@ -70,17 +70,24 @@ func NewMultiArgumentsDialogCmp(commandID, content string, argNames []string) Mu
for i, name := range argNames {
ti := textinput.New()
ti.Placeholder = fmt.Sprintf("Enter value for %s...", name)
- ti.Width = 40
+ ti.SetWidth(40)
ti.Prompt = ""
- ti.PlaceholderStyle = ti.PlaceholderStyle.Background(t.Background())
- ti.PromptStyle = ti.PromptStyle.Background(t.Background())
- ti.TextStyle = ti.TextStyle.Background(t.Background())
+ ti.Styles.Blurred.Placeholder = ti.Styles.Blurred.Placeholder.Background(t.Background())
+ ti.Styles.Blurred.Text = ti.Styles.Blurred.Text.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
if i == 0 {
ti.Focus()
- ti.PromptStyle = ti.PromptStyle.Foreground(t.Primary())
- ti.TextStyle = ti.TextStyle.Foreground(t.Primary())
+ // ti.PromptStyle = ti.PromptStyle.Foreground(t.Primary())
+ // ti.TextStyle = ti.TextStyle.Foreground(t.Primary())
} else {
ti.Blur()
}
@@ -115,7 +122,7 @@ func (m MultiArgumentsDialogCmp) Init() tea.Cmd {
// Update implements tea.Model.
func (m MultiArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
- t := theme.CurrentTheme()
+ // t := theme.CurrentTheme()
switch msg := msg.(type) {
case tea.KeyMsg:
@@ -145,22 +152,22 @@ func (m MultiArgumentsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.inputs[m.focusIndex].Blur()
m.focusIndex++
m.inputs[m.focusIndex].Focus()
- 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].PromptStyle = m.inputs[m.focusIndex].PromptStyle.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"))):
// Move to the next input
m.inputs[m.focusIndex].Blur()
m.focusIndex = (m.focusIndex + 1) % len(m.inputs)
m.inputs[m.focusIndex].Focus()
- 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].PromptStyle = m.inputs[m.focusIndex].PromptStyle.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"))):
// Move to the previous input
m.inputs[m.focusIndex].Blur()
m.focusIndex = (m.focusIndex - 1 + len(m.inputs)) % len(m.inputs)
m.inputs[m.focusIndex].Focus()
- 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].PromptStyle = m.inputs[m.focusIndex].PromptStyle.Foreground(t.Primary())
+ // m.inputs[m.focusIndex].TextStyle = m.inputs[m.focusIndex].TextStyle.Foreground(t.Primary())
}
case tea.WindowSizeMsg:
m.width = msg.Width
diff --git a/packages/tui/internal/components/dialog/commands.go b/packages/tui/internal/components/dialog/commands.go
index 1e99c09e0..39fe793d7 100644
--- a/packages/tui/internal/components/dialog/commands.go
+++ b/packages/tui/internal/components/dialog/commands.go
@@ -1,9 +1,9 @@
package dialog
import (
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
utilComponents "github.com/sst/opencode/internal/components/util"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
@@ -56,12 +56,12 @@ type CloseCommandDialogMsg struct{}
// CommandDialog interface for the command selection dialog
type CommandDialog interface {
- tea.Model
+ layout.ModelWithView
layout.Bindings
SetCommands(commands []Command)
}
-type commandDialogCmp struct {
+type commandDialogComponent struct {
listView utilComponents.SimpleList[Command]
width 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()
}
-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
switch msg := msg.(type) {
case tea.KeyMsg:
@@ -114,7 +114,7 @@ func (c *commandDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return c, tea.Batch(cmds...)
}
-func (c *commandDialogCmp) View() string {
+func (c *commandDialogComponent) View() string {
t := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
@@ -158,11 +158,11 @@ func (c *commandDialogCmp) View() string {
Render(content)
}
-func (c *commandDialogCmp) BindingKeys() []key.Binding {
+func (c *commandDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(commandKeys)
}
-func (c *commandDialogCmp) SetCommands(commands []Command) {
+func (c *commandDialogComponent) SetCommands(commands []Command) {
c.listView.SetItems(commands)
}
@@ -174,7 +174,7 @@ func NewCommandDialogCmp() CommandDialog {
"No commands available",
true,
)
- return &commandDialogCmp{
+ return &commandDialogComponent{
listView: listView,
}
}
diff --git a/packages/tui/internal/components/dialog/complete.go b/packages/tui/internal/components/dialog/complete.go
index ca209014e..91ac8f7b8 100644
--- a/packages/tui/internal/components/dialog/complete.go
+++ b/packages/tui/internal/components/dialog/complete.go
@@ -1,13 +1,13 @@
package dialog
import (
- "github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/textarea"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
- "github.com/sst/opencode/internal/status"
+ "github.com/charmbracelet/bubbles/v2/key"
+ "github.com/charmbracelet/bubbles/v2/textarea"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
utilComponents "github.com/sst/opencode/internal/components/util"
"github.com/sst/opencode/internal/layout"
+ "github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
"github.com/sst/opencode/internal/util"
@@ -77,7 +77,7 @@ type CompletionDialogCompleteItemMsg struct {
type CompletionDialogCloseMsg struct{}
type CompletionDialog interface {
- tea.Model
+ layout.ModelWithView
layout.Bindings
SetWidth(width int)
}
diff --git a/packages/tui/internal/components/dialog/custom_commands.go b/packages/tui/internal/components/dialog/custom_commands.go
index c0115ad7a..5e0671b80 100644
--- a/packages/tui/internal/components/dialog/custom_commands.go
+++ b/packages/tui/internal/components/dialog/custom_commands.go
@@ -7,7 +7,7 @@ import (
"regexp"
"strings"
- tea "github.com/charmbracelet/bubbletea"
+ tea "github.com/charmbracelet/bubbletea/v2"
"github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/util"
)
diff --git a/packages/tui/internal/components/dialog/filepicker.go b/packages/tui/internal/components/dialog/filepicker.go
index 3fad90e3f..408b9cc00 100644
--- a/packages/tui/internal/components/dialog/filepicker.go
+++ b/packages/tui/internal/components/dialog/filepicker.go
@@ -12,13 +12,14 @@ import (
"log/slog"
"github.com/atotto/clipboard"
- "github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/textinput"
- "github.com/charmbracelet/bubbles/viewport"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ "github.com/charmbracelet/bubbles/v2/textinput"
+ "github.com/charmbracelet/bubbles/v2/viewport"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/image"
+ "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
@@ -82,7 +83,7 @@ var filePickerKeyMap = FilePrickerKeyMap{
),
}
-type filepickerCmp struct {
+type filepickerComponent struct {
basePath string
width int
height int
@@ -118,18 +119,18 @@ type AttachmentAddedMsg struct {
Attachment app.Attachment
}
-func (f *filepickerCmp) Init() tea.Cmd {
+func (f *filepickerComponent) Init() tea.Cmd {
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
switch msg := msg.(type) {
case tea.WindowSizeMsg:
f.width = 60
f.height = 20
- f.viewport.Width = 80
- f.viewport.Height = 22
+ f.viewport.SetWidth(80)
+ f.viewport.SetHeight(22)
f.cursor = 0
f.getCurrentFileBelowCursor()
case tea.KeyMsg:
@@ -236,7 +237,7 @@ func (f *filepickerCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return f, cmd
}
-func (f *filepickerCmp) addAttachmentToMessage() (tea.Model, tea.Cmd) {
+func (f *filepickerComponent) addAttachmentToMessage() (tea.Model, tea.Cmd) {
// modeInfo := GetSelectedModel(config.Get())
// if !modeInfo.SupportsAttachments {
// 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})
}
-func (f *filepickerCmp) View() string {
+func (f *filepickerComponent) View() string {
t := theme.CurrentTheme()
const maxVisibleDirs = 20
const maxWidth = 80
@@ -333,7 +334,7 @@ func (f *filepickerCmp) View() string {
Render(f.cwd.View())
viewportstyle := lipgloss.NewStyle().
- Width(f.viewport.Width).
+ Width(f.viewport.Width()).
Background(t.Background()).
Border(lipgloss.RoundedBorder()).
BorderForeground(t.TextMuted()).
@@ -366,21 +367,21 @@ func (f *filepickerCmp) View() string {
return lipgloss.JoinHorizontal(lipgloss.Center, contentStyle.Render(content), viewportstyle)
}
-type FilepickerCmp interface {
- tea.Model
+type FilepickerComponent interface {
+ layout.ModelWithView
ToggleFilepicker(showFilepicker bool)
IsCWDFocused() bool
}
-func (f *filepickerCmp) ToggleFilepicker(showFilepicker bool) {
+func (f *filepickerComponent) ToggleFilepicker(showFilepicker bool) {
f.ShowFilePicker = showFilepicker
}
-func (f *filepickerCmp) IsCWDFocused() bool {
+func (f *filepickerComponent) IsCWDFocused() bool {
return f.cwd.Focused()
}
-func NewFilepickerCmp(app *app.App) FilepickerCmp {
+func NewFilepickerCmp(app *app.App) FilepickerComponent {
homepath, err := os.UserHomeDir()
if err != nil {
slog.Error("error loading user files")
@@ -388,16 +389,16 @@ func NewFilepickerCmp(app *app.App) FilepickerCmp {
}
baseDir := DirNode{parent: nil, directory: homepath}
dirs := readDir(homepath, false)
- viewport := viewport.New(0, 0)
+ viewport := viewport.New() // viewport.New(0, 0)
currentDirectory := textinput.New()
currentDirectory.CharLimit = 200
- currentDirectory.Width = 44
- currentDirectory.Cursor.Blink = true
+ currentDirectory.SetWidth(44)
+ // currentDirectory.Cursor.Blink = true
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) {
slog.Error(fmt.Sprintf("Invalid cursor position. Dirs length: %d, Cursor: %d", len(f.dirs), f.cursor))
f.viewport.SetContent("Preview unavailable")
@@ -410,7 +411,7 @@ func (f *filepickerCmp) getCurrentFileBelowCursor() {
fullPath := f.cwdDetails.directory + "/" + dir.Name()
go func() {
- imageString, err := image.ImagePreview(f.viewport.Width-4, fullPath)
+ imageString, err := image.ImagePreview(f.viewport.Width()-4, fullPath)
if err != nil {
slog.Error(err.Error())
f.viewport.SetContent("Preview unavailable")
diff --git a/packages/tui/internal/components/dialog/help.go b/packages/tui/internal/components/dialog/help.go
index a55e39e72..6ceeb40b2 100644
--- a/packages/tui/internal/components/dialog/help.go
+++ b/packages/tui/internal/components/dialog/help.go
@@ -3,28 +3,29 @@ package dialog
import (
"strings"
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
+ "github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
)
-type helpCmp struct {
+type helpComponent struct {
width int
height int
keys []key.Binding
}
-func (h *helpCmp) Init() tea.Cmd {
+func (h *helpComponent) Init() tea.Cmd {
return nil
}
-func (h *helpCmp) SetBindings(k []key.Binding) {
+func (h *helpComponent) SetBindings(k []key.Binding) {
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) {
case tea.WindowSizeMsg:
h.width = 90
@@ -53,7 +54,7 @@ func removeDuplicateBindings(bindings []key.Binding) []key.Binding {
return result
}
-func (h *helpCmp) render() string {
+func (h *helpComponent) render() string {
t := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
@@ -134,7 +135,7 @@ func (h *helpCmp) render() string {
pairs = append(pairs, pair)
}
- // https://github.com/charmbracelet/lipgloss/issues/209
+ // https://github.com/charmbracelet/lipgloss/v2/issues/209
if len(pairs) > 1 {
prefix := pairs[:len(pairs)-1]
lastPair := pairs[len(pairs)-1]
@@ -144,7 +145,7 @@ func (h *helpCmp) render() string {
lipgloss.Left, // x
lipgloss.Top, // y
lastPair, // content
- lipgloss.WithWhitespaceBackground(t.Background()),
+ // lipgloss.WithWhitespaceBackground(t.Background()),
))
content := baseStyle.Width(h.width).Render(
lipgloss.JoinHorizontal(
@@ -165,7 +166,7 @@ func (h *helpCmp) render() string {
return content
}
-func (h *helpCmp) View() string {
+func (h *helpComponent) View() string {
t := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
@@ -190,11 +191,11 @@ func (h *helpCmp) View() string {
)
}
-type HelpCmp interface {
- tea.Model
+type HelpComponent interface {
+ layout.ModelWithView
SetBindings([]key.Binding)
}
-func NewHelpCmp() HelpCmp {
- return &helpCmp{}
+func NewHelpCmp() HelpComponent {
+ return &helpComponent{}
}
diff --git a/packages/tui/internal/components/dialog/init.go b/packages/tui/internal/components/dialog/init.go
index 01bdbe577..d939427c1 100644
--- a/packages/tui/internal/components/dialog/init.go
+++ b/packages/tui/internal/components/dialog/init.go
@@ -1,9 +1,9 @@
package dialog
import (
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
diff --git a/packages/tui/internal/components/dialog/models.go b/packages/tui/internal/components/dialog/models.go
index 5d0ee772b..50d1791cd 100644
--- a/packages/tui/internal/components/dialog/models.go
+++ b/packages/tui/internal/components/dialog/models.go
@@ -7,9 +7,9 @@ import (
"slices"
"strings"
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
@@ -31,13 +31,13 @@ type CloseModelDialogMsg struct {
// ModelDialog interface for the model selection dialog
type ModelDialog interface {
- tea.Model
+ layout.ModelWithView
layout.Bindings
SetProviders(providers []client.ProviderInfo)
}
-type modelDialogCmp struct {
+type modelDialogComponent struct {
app *app.App
availableProviders []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()
// modelInfo := GetSelectedModel(cfg)
// m.availableProviders = getEnabledProviders(cfg)
@@ -125,11 +125,11 @@ func (m *modelDialogCmp) Init() tea.Cmd {
return nil
}
-func (m *modelDialogCmp) SetProviders(providers []client.ProviderInfo) {
+func (m *modelDialogComponent) SetProviders(providers []client.ProviderInfo) {
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) {
case tea.KeyMsg:
switch {
@@ -159,7 +159,7 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
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 {
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
-func (m *modelDialogCmp) moveSelectionUp() {
+func (m *modelDialogComponent) moveSelectionUp() {
if m.selectedIdx > 0 {
m.selectedIdx--
} else {
@@ -182,7 +182,7 @@ func (m *modelDialogCmp) moveSelectionUp() {
}
// 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 {
m.selectedIdx++
} 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
// Ensure we stay within bounds
@@ -212,7 +212,7 @@ func (m *modelDialogCmp) switchProvider(offset int) {
m.setupModelsForProvider(m.provider.Id)
}
-func (m *modelDialogCmp) View() string {
+func (m *modelDialogComponent) View() string {
t := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
@@ -255,7 +255,7 @@ func (m *modelDialogCmp) View() string {
Render(content)
}
-func (m *modelDialogCmp) getScrollIndicators(maxWidth int) string {
+func (m *modelDialogComponent) getScrollIndicators(maxWidth int) string {
var indicator string
if len(m.provider.Models) > numVisibleModels {
@@ -291,7 +291,7 @@ func (m *modelDialogCmp) getScrollIndicators(maxWidth int) string {
Render(indicator)
}
-func (m *modelDialogCmp) BindingKeys() []key.Binding {
+func (m *modelDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(modelKeys)
}
@@ -305,7 +305,7 @@ func (m *modelDialogCmp) BindingKeys() []key.Binding {
// return -1
// }
-func (m *modelDialogCmp) setupModelsForProvider(_ string) {
+func (m *modelDialogComponent) setupModelsForProvider(_ string) {
m.selectedIdx = 0
m.scrollOffset = 0
@@ -332,7 +332,7 @@ func (m *modelDialogCmp) setupModelsForProvider(_ string) {
}
func NewModelDialogCmp(app *app.App) ModelDialog {
- return &modelDialogCmp{
+ return &modelDialogComponent{
app: app,
}
}
diff --git a/packages/tui/internal/components/dialog/permission.go b/packages/tui/internal/components/dialog/permission.go
index bb71fc35d..526e03483 100644
--- a/packages/tui/internal/components/dialog/permission.go
+++ b/packages/tui/internal/components/dialog/permission.go
@@ -2,10 +2,10 @@ package dialog
import (
"fmt"
- "github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/viewport"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ "github.com/charmbracelet/bubbles/v2/viewport"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
@@ -28,9 +28,9 @@ type PermissionResponseMsg struct {
Action PermissionAction
}
-// PermissionDialogCmp interface for permission dialog component
-type PermissionDialogCmp interface {
- tea.Model
+// PermissionDialogComponent interface for permission dialog component
+type PermissionDialogComponent interface {
+ layout.ModelWithView
layout.Bindings
// SetPermissions(permission permission.PermissionRequest) tea.Cmd
}
@@ -76,8 +76,8 @@ var permissionsKeys = permissionsMapping{
),
}
-// permissionDialogCmp is the implementation of PermissionDialog
-type permissionDialogCmp struct {
+// permissionDialogComponent is the implementation of PermissionDialog
+type permissionDialogComponent struct {
width int
height int
// permission permission.PermissionRequest
@@ -89,11 +89,11 @@ type permissionDialogCmp struct {
markdownCache map[string]string
}
-func (p *permissionDialogCmp) Init() tea.Cmd {
+func (p *permissionDialogComponent) Init() tea.Cmd {
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
switch msg := msg.(type) {
@@ -129,7 +129,7 @@ func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return p, tea.Batch(cmds...)
}
-func (p *permissionDialogCmp) selectCurrentOption() tea.Cmd {
+func (p *permissionDialogComponent) selectCurrentOption() tea.Cmd {
var action PermissionAction
switch p.selectedOption {
@@ -144,7 +144,7 @@ func (p *permissionDialogCmp) selectCurrentOption() tea.Cmd {
return util.CmdHandler(PermissionResponseMsg{Action: action}) // , Permission: p.permission})
}
-func (p *permissionDialogCmp) renderButtons() string {
+func (p *permissionDialogComponent) renderButtons() string {
t := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
@@ -190,7 +190,7 @@ func (p *permissionDialogCmp) renderButtons() string {
return content
}
-func (p *permissionDialogCmp) renderHeader() string {
+func (p *permissionDialogComponent) renderHeader() string {
return "NOT IMPLEMENTED"
// t := theme.CurrentTheme()
// baseStyle := styles.BaseStyle()
@@ -246,7 +246,7 @@ func (p *permissionDialogCmp) renderHeader() string {
// 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()
// baseStyle := styles.BaseStyle()
//
@@ -269,7 +269,7 @@ func (p *permissionDialogCmp) renderBashContent() string {
return ""
}
-func (p *permissionDialogCmp) renderEditContent() string {
+func (p *permissionDialogComponent) renderEditContent() string {
// if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok {
// diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
// return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width))
@@ -281,7 +281,7 @@ func (p *permissionDialogCmp) renderEditContent() string {
return ""
}
-func (p *permissionDialogCmp) renderPatchContent() string {
+func (p *permissionDialogComponent) renderPatchContent() string {
// if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok {
// diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
// return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width))
@@ -293,7 +293,7 @@ func (p *permissionDialogCmp) renderPatchContent() string {
return ""
}
-func (p *permissionDialogCmp) renderWriteContent() string {
+func (p *permissionDialogComponent) renderWriteContent() string {
// if pr, ok := p.permission.Params.(tools.WritePermissionsParams); ok {
// // Use the cache for diff rendering
// diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
@@ -306,7 +306,7 @@ func (p *permissionDialogCmp) renderWriteContent() string {
return ""
}
-func (p *permissionDialogCmp) renderFetchContent() string {
+func (p *permissionDialogComponent) renderFetchContent() string {
// t := theme.CurrentTheme()
// baseStyle := styles.BaseStyle()
//
@@ -329,7 +329,7 @@ func (p *permissionDialogCmp) renderFetchContent() string {
return ""
}
-func (p *permissionDialogCmp) renderDefaultContent() string {
+func (p *permissionDialogComponent) renderDefaultContent() string {
// t := theme.CurrentTheme()
// baseStyle := styles.BaseStyle()
//
@@ -354,7 +354,7 @@ func (p *permissionDialogCmp) renderDefaultContent() string {
return p.styleViewport()
}
-func (p *permissionDialogCmp) styleViewport() string {
+func (p *permissionDialogComponent) styleViewport() string {
t := theme.CurrentTheme()
contentStyle := lipgloss.NewStyle().
Background(t.Background())
@@ -362,7 +362,7 @@ func (p *permissionDialogCmp) styleViewport() string {
return contentStyle.Render(p.contentViewPort.View())
}
-func (p *permissionDialogCmp) render() string {
+func (p *permissionDialogComponent) render() string {
return "NOT IMPLEMENTED"
// t := theme.CurrentTheme()
// 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()
}
-func (p *permissionDialogCmp) BindingKeys() []key.Binding {
+func (p *permissionDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(permissionsKeys)
}
-func (p *permissionDialogCmp) SetSize() tea.Cmd {
+func (p *permissionDialogComponent) SetSize() tea.Cmd {
// if p.permission.ID == "" {
// return nil
// }
@@ -458,7 +458,7 @@ func (p *permissionDialogCmp) SetSize() tea.Cmd {
// }
// 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 {
return cached
}
@@ -474,7 +474,7 @@ func (c *permissionDialogCmp) GetOrSetDiff(key string, generator func() (string,
}
// 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 {
return cached
}
@@ -489,11 +489,11 @@ func (c *permissionDialogCmp) GetOrSetMarkdown(key string, generator func() (str
return content
}
-func NewPermissionDialogCmp() PermissionDialogCmp {
+func NewPermissionDialogCmp() PermissionDialogComponent {
// Create viewport for content
- contentViewport := viewport.New(0, 0)
+ contentViewport := viewport.New() // (0, 0)
- return &permissionDialogCmp{
+ return &permissionDialogComponent{
contentViewPort: contentViewport,
selectedOption: 0, // Default to "Allow"
diffCache: make(map[string]string),
diff --git a/packages/tui/internal/components/dialog/quit.go b/packages/tui/internal/components/dialog/quit.go
index 78034c714..8362e7672 100644
--- a/packages/tui/internal/components/dialog/quit.go
+++ b/packages/tui/internal/components/dialog/quit.go
@@ -3,9 +3,9 @@ package dialog
import (
"strings"
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
@@ -17,11 +17,11 @@ const question = "Are you sure you want to quit?"
type CloseQuitMsg struct{}
type QuitDialog interface {
- tea.Model
+ layout.ModelWithView
layout.Bindings
}
-type quitDialogCmp struct {
+type quitDialogComponent struct {
selectedNo bool
}
@@ -56,11 +56,11 @@ var helpKeys = helpMapping{
),
}
-func (q *quitDialogCmp) Init() tea.Cmd {
+func (q *quitDialogComponent) Init() tea.Cmd {
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) {
case tea.KeyMsg:
switch {
@@ -81,7 +81,7 @@ func (q *quitDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return q, nil
}
-func (q *quitDialogCmp) View() string {
+func (q *quitDialogComponent) View() string {
t := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
@@ -125,12 +125,12 @@ func (q *quitDialogCmp) View() string {
Render(content)
}
-func (q *quitDialogCmp) BindingKeys() []key.Binding {
+func (q *quitDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(helpKeys)
}
func NewQuitCmp() QuitDialog {
- return &quitDialogCmp{
+ return &quitDialogComponent{
selectedNo: true,
}
}
diff --git a/packages/tui/internal/components/dialog/session.go b/packages/tui/internal/components/dialog/session.go
index 2d234529e..8ef9a701f 100644
--- a/packages/tui/internal/components/dialog/session.go
+++ b/packages/tui/internal/components/dialog/session.go
@@ -1,9 +1,9 @@
package dialog
import (
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
@@ -18,13 +18,13 @@ type CloseSessionDialogMsg struct {
// SessionDialog interface for the session switching dialog
type SessionDialog interface {
- tea.Model
+ layout.ModelWithView
layout.Bindings
SetSessions(sessions []client.SessionInfo)
SetSelectedSession(sessionID string)
}
-type sessionDialogCmp struct {
+type sessionDialogComponent struct {
sessions []client.SessionInfo
selectedIdx int
width int
@@ -68,11 +68,11 @@ var sessionKeys = sessionKeyMap{
),
}
-func (s *sessionDialogCmp) Init() tea.Cmd {
+func (s *sessionDialogComponent) Init() tea.Cmd {
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) {
case tea.WindowSizeMsg:
s.width = msg.Width
@@ -105,7 +105,7 @@ func (s *sessionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return s, nil
}
-func (s *sessionDialogCmp) View() string {
+func (s *sessionDialogComponent) View() string {
t := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
@@ -185,11 +185,11 @@ func (s *sessionDialogCmp) View() string {
Render(content)
}
-func (s *sessionDialogCmp) BindingKeys() []key.Binding {
+func (s *sessionDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(sessionKeys)
}
-func (s *sessionDialogCmp) SetSessions(sessions []client.SessionInfo) {
+func (s *sessionDialogComponent) SetSessions(sessions []client.SessionInfo) {
s.sessions = sessions
// If we have a selected session ID, find its index
@@ -206,7 +206,7 @@ func (s *sessionDialogCmp) SetSessions(sessions []client.SessionInfo) {
s.selectedIdx = 0
}
-func (s *sessionDialogCmp) SetSelectedSession(sessionID string) {
+func (s *sessionDialogComponent) SetSelectedSession(sessionID string) {
s.selectedSessionID = sessionID
// 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
func NewSessionDialogCmp() SessionDialog {
- return &sessionDialogCmp{
+ return &sessionDialogComponent{
sessions: []client.SessionInfo{},
selectedIdx: 0,
selectedSessionID: "",
diff --git a/packages/tui/internal/components/dialog/theme.go b/packages/tui/internal/components/dialog/theme.go
index 42c777c41..4704cb42e 100644
--- a/packages/tui/internal/components/dialog/theme.go
+++ b/packages/tui/internal/components/dialog/theme.go
@@ -1,9 +1,9 @@
package dialog
import (
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/status"
"github.com/sst/opencode/internal/styles"
@@ -21,11 +21,11 @@ type CloseThemeDialogMsg struct{}
// ThemeDialog interface for the theme switching dialog
type ThemeDialog interface {
- tea.Model
+ layout.ModelWithView
layout.Bindings
}
-type themeDialogCmp struct {
+type themeDialogComponent struct {
themes []string
selectedIdx 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
t.themes = theme.AvailableThemes()
t.currentTheme = theme.CurrentThemeName()
@@ -85,7 +85,7 @@ func (t *themeDialogCmp) Init() tea.Cmd {
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) {
case tea.KeyMsg:
switch {
@@ -124,7 +124,7 @@ func (t *themeDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return t, nil
}
-func (t *themeDialogCmp) View() string {
+func (t *themeDialogComponent) View() string {
currentTheme := theme.CurrentTheme()
baseStyle := styles.BaseStyle()
@@ -185,13 +185,13 @@ func (t *themeDialogCmp) View() string {
Render(content)
}
-func (t *themeDialogCmp) BindingKeys() []key.Binding {
+func (t *themeDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(themeKeys)
}
// NewThemeDialogCmp creates a new theme switching dialog
func NewThemeDialogCmp() ThemeDialog {
- return &themeDialogCmp{
+ return &themeDialogComponent{
themes: []string{},
selectedIdx: 0,
currentTheme: "",
diff --git a/packages/tui/internal/components/dialog/tools.go b/packages/tui/internal/components/dialog/tools.go
index a01d1911b..c91dfef9a 100644
--- a/packages/tui/internal/components/dialog/tools.go
+++ b/packages/tui/internal/components/dialog/tools.go
@@ -1,9 +1,9 @@
package dialog
import (
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
utilComponents "github.com/sst/opencode/internal/components/util"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
@@ -17,7 +17,7 @@ const (
// ToolsDialog interface for the tools list dialog
type ToolsDialog interface {
- tea.Model
+ layout.ModelWithView
layout.Bindings
SetTools(tools []string)
}
@@ -39,7 +39,7 @@ func (t toolItem) Render(selected bool, width int) string {
baseStyle := styles.BaseStyle().
Width(width).
Background(th.Background())
-
+
if selected {
baseStyle = baseStyle.
Background(th.Primary()).
@@ -49,15 +49,15 @@ func (t toolItem) Render(selected bool, width int) string {
baseStyle = baseStyle.
Foreground(th.Text())
}
-
+
return baseStyle.Render(t.name)
}
-type toolsDialogCmp struct {
- tools []toolItem
- width int
- height int
- list utilComponents.SimpleList[toolItem]
+type toolsDialogComponent struct {
+ tools []toolItem
+ width int
+ height int
+ list utilComponents.SimpleList[toolItem]
}
type toolsKeyMap struct {
@@ -91,21 +91,21 @@ var toolsKeys = toolsKeyMap{
),
}
-func (m *toolsDialogCmp) Init() tea.Cmd {
+func (m *toolsDialogComponent) Init() tea.Cmd {
return nil
}
-func (m *toolsDialogCmp) SetTools(tools []string) {
+func (m *toolsDialogComponent) SetTools(tools []string) {
var toolItems []toolItem
for _, name := range tools {
toolItems = append(toolItems, toolItem{name: name})
}
-
+
m.tools = 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) {
case tea.KeyMsg:
switch {
@@ -130,7 +130,7 @@ func (m *toolsDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, cmd
}
-func (m *toolsDialogCmp) View() string {
+func (m *toolsDialogComponent) View() string {
t := theme.CurrentTheme()
baseStyle := styles.BaseStyle().Background(t.Background())
@@ -144,7 +144,7 @@ func (m *toolsDialogCmp) View() string {
// Calculate dialog width based on content
dialogWidth := min(maxToolsDialogWidth, m.width/2)
m.list.SetMaxWidth(dialogWidth)
-
+
content := lipgloss.JoinVertical(
lipgloss.Left,
title,
@@ -160,7 +160,7 @@ func (m *toolsDialogCmp) View() string {
Render(content)
}
-func (m *toolsDialogCmp) BindingKeys() []key.Binding {
+func (m *toolsDialogComponent) BindingKeys() []key.Binding {
return layout.KeyMapToSlice(toolsKeys)
}
@@ -171,8 +171,8 @@ func NewToolsDialogCmp() ToolsDialog {
"No tools available",
true,
)
-
- return &toolsDialogCmp{
+
+ return &toolsDialogComponent{
list: list,
}
}
diff --git a/packages/tui/internal/components/diff/diff.go b/packages/tui/internal/components/diff/diff.go
index 65874ebb4..4551cf736 100644
--- a/packages/tui/internal/components/diff/diff.go
+++ b/packages/tui/internal/components/diff/diff.go
@@ -3,6 +3,7 @@ package diff
import (
"bytes"
"fmt"
+ "image/color"
"io"
"regexp"
"strconv"
@@ -12,9 +13,11 @@ import (
"github.com/alecthomas/chroma/v2/formatters"
"github.com/alecthomas/chroma/v2/lexers"
"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/sergi/go-diff/diffmatchpatch"
+ stylesi "github.com/sst/opencode/internal/styles"
"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
-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()
// 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
-func getColor(adaptiveColor lipgloss.AdaptiveColor) string {
- if lipgloss.HasDarkBackground() {
- return adaptiveColor.Dark
- }
- return adaptiveColor.Light
+func getColor(adaptiveColor compat.AdaptiveColor) string {
+ return stylesi.AdaptiveColorToString(adaptiveColor)
}
// 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
err := SyntaxHighlight(&buf, line, fileName, "terminal16m", bg)
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
-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
ansiRegex := regexp.MustCompile(`\x1b(?:[@-Z\\-_]|\[[0-9?]*(?:;[0-9?]*)*[@-~])`)
ansiMatches := ansiRegex.FindAllStringIndex(content, -1)
@@ -662,7 +662,7 @@ func renderDiffColumnLine(
var bgStyle lipgloss.Style
var lineNum string
var highlightType LineType
- var highlightColor lipgloss.AdaptiveColor
+ var highlightColor compat.AdaptiveColor
if isLeftColumn {
// Left column logic
diff --git a/packages/tui/internal/components/qr/qr.go b/packages/tui/internal/components/qr/qr.go
index df51a644d..82d597a3f 100644
--- a/packages/tui/internal/components/qr/qr.go
+++ b/packages/tui/internal/components/qr/qr.go
@@ -3,7 +3,7 @@ package qr
import (
"strings"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/theme"
"rsc.io/qr"
)
diff --git a/packages/tui/internal/components/util/simple-list.go b/packages/tui/internal/components/util/simple-list.go
index 0c0bb6556..d6e27db14 100644
--- a/packages/tui/internal/components/util/simple-list.go
+++ b/packages/tui/internal/components/util/simple-list.go
@@ -1,9 +1,9 @@
package utilComponents
import (
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/bubbles/v2/key"
+ tea "github.com/charmbracelet/bubbletea/v2"
+ "github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
@@ -14,7 +14,7 @@ type SimpleListItem interface {
}
type SimpleList[T SimpleListItem] interface {
- tea.Model
+ layout.ModelWithView
layout.Bindings
SetMaxWidth(maxWidth int)
GetSelectedItem() (item T, idx int)