diff options
| author | adamdottv <[email protected]> | 2025-06-19 15:56:20 -0500 |
|---|---|---|
| committer | adamdottv <[email protected]> | 2025-06-19 15:56:28 -0500 |
| commit | 3728a12bee441f559710b3813787d5f4dce7f5ef (patch) | |
| tree | fab57cc89828d5a682a6474bd0aba9a14d7a6a76 /packages/tui/internal/components/commands | |
| parent | af07e5121352ebb3a162b6fc16f29c41b928dc89 (diff) | |
| download | opencode-3728a12bee441f559710b3813787d5f4dce7f5ef.tar.gz opencode-3728a12bee441f559710b3813787d5f4dce7f5ef.zip | |
fix(tui): better help on home
Diffstat (limited to 'packages/tui/internal/components/commands')
| -rw-r--r-- | packages/tui/internal/components/commands/commands.go | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/packages/tui/internal/components/commands/commands.go b/packages/tui/internal/components/commands/commands.go new file mode 100644 index 000000000..75b27779b --- /dev/null +++ b/packages/tui/internal/components/commands/commands.go @@ -0,0 +1,196 @@ +package commands + +import ( + "fmt" + "strings" + + tea "github.com/charmbracelet/bubbletea/v2" + "github.com/charmbracelet/lipgloss/v2" + "github.com/charmbracelet/lipgloss/v2/compat" + "github.com/sst/opencode/internal/app" + "github.com/sst/opencode/internal/commands" + "github.com/sst/opencode/internal/layout" + "github.com/sst/opencode/internal/styles" + "github.com/sst/opencode/internal/theme" +) + +type CommandsComponent interface { + tea.Model + tea.ViewModel + layout.Sizeable +} + +type commandsComponent struct { + app *app.App + width, height int + showKeybinds bool + background *compat.AdaptiveColor + limit *int +} + +func (c *commandsComponent) SetSize(width, height int) tea.Cmd { + c.width = width + c.height = height + return nil +} + +func (c *commandsComponent) GetSize() (int, int) { + return c.width, c.height +} + +func (c *commandsComponent) Init() tea.Cmd { + return nil +} + +func (c *commandsComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + c.width = msg.Width + c.height = msg.Height + } + return c, nil +} + +func (c *commandsComponent) View() string { + t := theme.CurrentTheme() + + triggerStyle := lipgloss.NewStyle(). + Foreground(t.Primary()). + Bold(true) + + descriptionStyle := lipgloss.NewStyle(). + Foreground(t.Text()) + + keybindStyle := lipgloss.NewStyle(). + Foreground(t.TextMuted()) + + if c.background != nil { + triggerStyle = triggerStyle.Background(*c.background) + descriptionStyle = descriptionStyle.Background(*c.background) + keybindStyle = keybindStyle.Background(*c.background) + } + + var commandsWithTriggers []commands.Command + for _, cmd := range c.app.Commands.Sorted() { + if cmd.Trigger != "" { + commandsWithTriggers = append(commandsWithTriggers, cmd) + } + } + if c.limit != nil && len(commandsWithTriggers) > *c.limit { + commandsWithTriggers = commandsWithTriggers[:*c.limit] + } + + if len(commandsWithTriggers) == 0 { + return styles.Muted().Render("No commands with triggers available") + } + + // Calculate column widths + maxTriggerWidth := 0 + maxDescriptionWidth := 0 + maxKeybindWidth := 0 + + // Prepare command data + type commandRow struct { + trigger string + description string + keybinds string + } + + rows := make([]commandRow, 0, len(commandsWithTriggers)) + + for _, cmd := range commandsWithTriggers { + trigger := "/" + cmd.Trigger + description := cmd.Description + + // Format keybindings + var keybindStrs []string + if c.showKeybinds { + for _, kb := range cmd.Keybindings { + if kb.RequiresLeader { + keybindStrs = append(keybindStrs, *c.app.Config.Keybinds.Leader+" "+kb.Key) + } else { + keybindStrs = append(keybindStrs, kb.Key) + } + } + } + keybinds := strings.Join(keybindStrs, ", ") + + rows = append(rows, commandRow{ + trigger: trigger, + description: description, + keybinds: keybinds, + }) + + // Update max widths + if len(trigger) > maxTriggerWidth { + maxTriggerWidth = len(trigger) + } + if len(description) > maxDescriptionWidth { + maxDescriptionWidth = len(description) + } + if len(keybinds) > maxKeybindWidth { + maxKeybindWidth = len(keybinds) + } + } + + // Add padding between columns + columnPadding := 3 + + // Build the output + var output strings.Builder + + for _, row := range rows { + // Pad each column to align properly + trigger := fmt.Sprintf("%-*s", maxTriggerWidth, row.trigger) + description := fmt.Sprintf("%-*s", maxDescriptionWidth, row.description) + + // Apply styles and combine + line := triggerStyle.Render(trigger) + + triggerStyle.Render(strings.Repeat(" ", columnPadding)) + + descriptionStyle.Render(description) + + if c.showKeybinds && row.keybinds != "" { + line += keybindStyle.Render(strings.Repeat(" ", columnPadding)) + + keybindStyle.Render(row.keybinds) + } + + output.WriteString(line + "\n") + } + + // Remove trailing newline + result := strings.TrimSuffix(output.String(), "\n") + + return result +} + +type Option func(*commandsComponent) + +func WithKeybinds(show bool) Option { + return func(c *commandsComponent) { + c.showKeybinds = show + } +} + +func WithBackground(background compat.AdaptiveColor) Option { + return func(c *commandsComponent) { + c.background = &background + } +} + +func WithLimit(limit int) Option { + return func(c *commandsComponent) { + c.limit = &limit + } +} + +func New(app *app.App, opts ...Option) CommandsComponent { + c := &commandsComponent{ + app: app, + background: nil, + showKeybinds: true, + } + for _, opt := range opts { + opt(c) + } + return c +} |
