summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/tui/internal/components/dialog/session.go173
-rw-r--r--packages/tui/internal/styles/markdown.go5
2 files changed, 73 insertions, 105 deletions
diff --git a/packages/tui/internal/components/dialog/session.go b/packages/tui/internal/components/dialog/session.go
index a524f494c..c64043709 100644
--- a/packages/tui/internal/components/dialog/session.go
+++ b/packages/tui/internal/components/dialog/session.go
@@ -4,6 +4,7 @@ import (
"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"
"github.com/sst/opencode/internal/theme"
@@ -24,32 +25,43 @@ type SessionDialog interface {
SetSelectedSession(sessionID string)
}
+type sessionItem struct {
+ session client.SessionInfo
+}
+
+func (s sessionItem) Render(selected bool, width int) string {
+ t := theme.CurrentTheme()
+ baseStyle := styles.BaseStyle().
+ Width(width - 2).
+ Background(t.Background())
+
+ if selected {
+ baseStyle = baseStyle.
+ Background(t.Primary()).
+ Foreground(t.Background()).
+ Bold(true)
+ } else {
+ baseStyle = baseStyle.
+ Foreground(t.Text())
+ }
+
+ return baseStyle.Padding(0, 1).Render(s.session.Title)
+}
+
type sessionDialogComponent struct {
sessions []client.SessionInfo
- selectedIdx int
width int
height int
selectedSessionID string
+ list utilComponents.SimpleList[sessionItem]
}
type sessionKeyMap struct {
- Up key.Binding
- Down key.Binding
Enter key.Binding
Escape key.Binding
- J key.Binding
- K key.Binding
}
var sessionKeys = sessionKeyMap{
- Up: key.NewBinding(
- key.WithKeys("up"),
- key.WithHelp("↑", "previous session"),
- ),
- Down: key.NewBinding(
- key.WithKeys("down"),
- key.WithHelp("↓", "next session"),
- ),
Enter: key.NewBinding(
key.WithKeys("enter"),
key.WithHelp("enter", "select session"),
@@ -58,14 +70,6 @@ var sessionKeys = sessionKeyMap{
key.WithKeys("esc"),
key.WithHelp("esc", "close"),
),
- J: key.NewBinding(
- key.WithKeys("j"),
- key.WithHelp("j", "next session"),
- ),
- K: key.NewBinding(
- key.WithKeys("k"),
- key.WithHelp("k", "previous session"),
- ),
}
func (s *sessionDialogComponent) Init() tea.Cmd {
@@ -79,19 +83,9 @@ func (s *sessionDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
s.height = msg.Height
case tea.KeyMsg:
switch {
- case key.Matches(msg, sessionKeys.Up) || key.Matches(msg, sessionKeys.K):
- if s.selectedIdx > 0 {
- s.selectedIdx--
- }
- return s, nil
- case key.Matches(msg, sessionKeys.Down) || key.Matches(msg, sessionKeys.J):
- if s.selectedIdx < len(s.sessions)-1 {
- s.selectedIdx++
- }
- return s, nil
case key.Matches(msg, sessionKeys.Enter):
- if len(s.sessions) > 0 {
- selectedSession := s.sessions[s.selectedIdx]
+ if item, idx := s.list.GetSelectedItem(); idx >= 0 {
+ selectedSession := item.session
s.selectedSessionID = selectedSession.Id
return s, util.CmdHandler(CloseSessionDialogMsg{
@@ -100,110 +94,82 @@ func (s *sessionDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
case key.Matches(msg, sessionKeys.Escape):
return s, util.CmdHandler(CloseSessionDialogMsg{})
+ default:
+ // Pass other key messages to the list component
+ var cmd tea.Cmd
+ listModel, cmd := s.list.Update(msg)
+ s.list = listModel.(utilComponents.SimpleList[sessionItem])
+ return s, cmd
}
}
- return s, nil
+
+ // For non-key messages
+ var cmd tea.Cmd
+ listModel, cmd := s.list.Update(msg)
+ s.list = listModel.(utilComponents.SimpleList[sessionItem])
+ return s, cmd
}
func (s *sessionDialogComponent) View() string {
t := theme.CurrentTheme()
- baseStyle := styles.BaseStyle()
+ baseStyle := styles.BaseStyle().Background(t.Background())
+ width := layout.Current.Container.Width - 4
if len(s.sessions) == 0 {
return baseStyle.Padding(1, 2).
Border(lipgloss.RoundedBorder()).
BorderBackground(t.Background()).
BorderForeground(t.TextMuted()).
- Width(40).
+ Width(width).
Render("No sessions available")
}
- // Calculate max width needed for session titles
- maxWidth := 40 // Minimum width
- for _, sess := range s.sessions {
- if len(sess.Title) > maxWidth-4 { // Account for padding
- maxWidth = len(sess.Title) + 0
- }
- }
-
- maxWidth = max(30, min(maxWidth, s.width-15)) // Limit width to avoid overflow
-
- // Limit height to avoid taking up too much screen space
- maxVisibleSessions := min(10, len(s.sessions))
-
- // Build the session list
- sessionItems := make([]string, 0, maxVisibleSessions)
- startIdx := 0
-
- // If we have more sessions than can be displayed, adjust the start index
- if len(s.sessions) > maxVisibleSessions {
- // Center the selected item when possible
- halfVisible := maxVisibleSessions / 2
- if s.selectedIdx >= halfVisible && s.selectedIdx < len(s.sessions)-halfVisible {
- startIdx = s.selectedIdx - halfVisible
- } else if s.selectedIdx >= len(s.sessions)-halfVisible {
- startIdx = len(s.sessions) - maxVisibleSessions
- }
- }
-
- endIdx := min(startIdx+maxVisibleSessions, len(s.sessions))
-
- for i := startIdx; i < endIdx; i++ {
- sess := s.sessions[i]
- itemStyle := baseStyle.Width(maxWidth)
-
- if i == s.selectedIdx {
- itemStyle = itemStyle.
- Background(t.Primary()).
- Foreground(t.Background()).
- Bold(true)
- }
-
- sessionItems = append(sessionItems, itemStyle.Padding(0, 1).Render(sess.Title))
- }
+ // Set the max width for the list
+ s.list.SetMaxWidth(width)
title := baseStyle.
Foreground(t.Primary()).
Bold(true).
- Width(maxWidth).
+ Width(width).
Padding(0, 1).
Render("Switch Session")
content := lipgloss.JoinVertical(
lipgloss.Left,
title,
- baseStyle.Width(maxWidth).Render(""),
- baseStyle.Width(maxWidth-2).Render(lipgloss.JoinVertical(lipgloss.Left, sessionItems...)),
- baseStyle.Width(maxWidth).Render(""),
+ // baseStyle.Width(width).Render(""),
+ "",
+ s.list.View(),
+ "",
+ // baseStyle.Width(width).Render(""),
)
return baseStyle.Padding(1, 2).
Border(lipgloss.RoundedBorder()).
BorderBackground(t.Background()).
BorderForeground(t.TextMuted()).
- Width(lipgloss.Width(content) + 4).
+ Width(layout.Current.Container.Width).
Render(content)
}
func (s *sessionDialogComponent) BindingKeys() []key.Binding {
- return layout.KeyMapToSlice(sessionKeys)
+ // Combine session dialog keys with list keys
+ dialogKeys := layout.KeyMapToSlice(sessionKeys)
+ listKeys := s.list.BindingKeys()
+ return append(dialogKeys, listKeys...)
}
func (s *sessionDialogComponent) SetSessions(sessions []client.SessionInfo) {
s.sessions = sessions
- // If we have a selected session ID, find its index
- if s.selectedSessionID != "" {
- for i, sess := range sessions {
- if sess.Id == s.selectedSessionID {
- s.selectedIdx = i
- return
- }
- }
+ // Convert sessions to sessionItems
+ var sessionItems []sessionItem
+
+ for _, sess := range sessions {
+ sessionItems = append(sessionItems, sessionItem{session: sess})
}
- // Default to first session if selected not found
- s.selectedIdx = 0
+ s.list.SetItems(sessionItems)
}
func (s *sessionDialogComponent) SetSelectedSession(sessionID string) {
@@ -211,20 +177,23 @@ func (s *sessionDialogComponent) SetSelectedSession(sessionID string) {
// Update the selected index if sessions are already loaded
if len(s.sessions) > 0 {
- for i, sess := range s.sessions {
- if sess.Id == sessionID {
- s.selectedIdx = i
- return
- }
- }
+ // Re-set the sessions to update the selection
+ s.SetSessions(s.sessions)
}
}
// NewSessionDialogCmp creates a new session switching dialog
func NewSessionDialogCmp() SessionDialog {
+ list := utilComponents.NewSimpleList[sessionItem](
+ []sessionItem{},
+ 10, // maxVisibleSessions
+ "No sessions available",
+ true, // useAlphaNumericKeys
+ )
+
return &sessionDialogComponent{
sessions: []client.SessionInfo{},
- selectedIdx: 0,
selectedSessionID: "",
+ list: list,
}
}
diff --git a/packages/tui/internal/styles/markdown.go b/packages/tui/internal/styles/markdown.go
index a8681e191..f28e26793 100644
--- a/packages/tui/internal/styles/markdown.go
+++ b/packages/tui/internal/styles/markdown.go
@@ -132,9 +132,8 @@ func generateMarkdownStyleConfig(backgroundColor compat.AdaptiveColor) ansi.Styl
Color: stringPtr(AdaptiveColorToString(t.MarkdownListEnumeration())),
},
Task: ansi.StyleTask{
- StylePrimitive: ansi.StylePrimitive{},
- Ticked: "[✓] ",
- Unticked: "[ ] ",
+ Ticked: "[✓] ",
+ Unticked: "[ ] ",
},
Link: ansi.StylePrimitive{
Color: stringPtr(AdaptiveColorToString(t.MarkdownLink())),