From 3a6a26981a8074b6ab0eaadb520db986e04799ff Mon Sep 17 00:00:00 2001 From: Kujtim Hoxha Date: Mon, 21 Apr 2025 19:48:36 +0200 Subject: init command --- internal/tui/components/dialog/init.go | 191 +++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 internal/tui/components/dialog/init.go (limited to 'internal/tui/components/dialog/init.go') diff --git a/internal/tui/components/dialog/init.go b/internal/tui/components/dialog/init.go new file mode 100644 index 000000000..6098ca755 --- /dev/null +++ b/internal/tui/components/dialog/init.go @@ -0,0 +1,191 @@ +package dialog + +import ( + "github.com/charmbracelet/bubbles/key" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + + "github.com/kujtimiihoxha/opencode/internal/tui/styles" + "github.com/kujtimiihoxha/opencode/internal/tui/util" +) + +// InitDialogCmp is a component that asks the user if they want to initialize the project. +type InitDialogCmp struct { + width, height int + selected int + keys initDialogKeyMap +} + +// NewInitDialogCmp creates a new InitDialogCmp. +func NewInitDialogCmp() InitDialogCmp { + return InitDialogCmp{ + selected: 0, + keys: initDialogKeyMap{}, + } +} + +type initDialogKeyMap struct { + Tab key.Binding + Left key.Binding + Right key.Binding + Enter key.Binding + Escape key.Binding + Y key.Binding + N key.Binding +} + +// ShortHelp implements key.Map. +func (k initDialogKeyMap) ShortHelp() []key.Binding { + return []key.Binding{ + key.NewBinding( + key.WithKeys("tab", "left", "right"), + key.WithHelp("tab/←/→", "toggle selection"), + ), + key.NewBinding( + key.WithKeys("enter"), + key.WithHelp("enter", "confirm"), + ), + key.NewBinding( + key.WithKeys("esc"), + key.WithHelp("esc", "cancel"), + ), + key.NewBinding( + key.WithKeys("y", "n"), + key.WithHelp("y/n", "yes/no"), + ), + } +} + +// FullHelp implements key.Map. +func (k initDialogKeyMap) FullHelp() [][]key.Binding { + return [][]key.Binding{k.ShortHelp()} +} + +// Init implements tea.Model. +func (m InitDialogCmp) Init() tea.Cmd { + return nil +} + +// Update implements tea.Model. +func (m InitDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch { + case key.Matches(msg, key.NewBinding(key.WithKeys("esc"))): + return m, util.CmdHandler(CloseInitDialogMsg{Initialize: false}) + case key.Matches(msg, key.NewBinding(key.WithKeys("tab", "left", "right", "h", "l"))): + m.selected = (m.selected + 1) % 2 + return m, nil + case key.Matches(msg, key.NewBinding(key.WithKeys("enter"))): + return m, util.CmdHandler(CloseInitDialogMsg{Initialize: m.selected == 0}) + case key.Matches(msg, key.NewBinding(key.WithKeys("y"))): + return m, util.CmdHandler(CloseInitDialogMsg{Initialize: true}) + case key.Matches(msg, key.NewBinding(key.WithKeys("n"))): + return m, util.CmdHandler(CloseInitDialogMsg{Initialize: false}) + } + case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + } + return m, nil +} + +// View implements tea.Model. +func (m InitDialogCmp) View() string { + // Calculate width needed for content + maxWidth := 60 // Width for explanation text + + title := styles.BaseStyle. + Foreground(styles.PrimaryColor). + Bold(true). + Width(maxWidth). + Padding(0, 1). + Render("Initialize Project") + + explanation := styles.BaseStyle. + Foreground(styles.Forground). + Width(maxWidth). + Padding(0, 1). + Render("Initialization generates a new OpenCode.md file that contains information about your codebase, this file serves as memory for each project, you can freely add to it to help the agents be better at their job.") + + question := styles.BaseStyle. + Foreground(styles.Forground). + Width(maxWidth). + Padding(1, 1). + Render("Would you like to initialize this project?") + + yesStyle := styles.BaseStyle + noStyle := styles.BaseStyle + + if m.selected == 0 { + yesStyle = yesStyle. + Background(styles.PrimaryColor). + Foreground(styles.Background). + Bold(true) + noStyle = noStyle. + Background(styles.Background). + Foreground(styles.PrimaryColor) + } else { + noStyle = noStyle. + Background(styles.PrimaryColor). + Foreground(styles.Background). + Bold(true) + yesStyle = yesStyle. + Background(styles.Background). + Foreground(styles.PrimaryColor) + } + + yes := yesStyle.Padding(0, 3).Render("Yes") + no := noStyle.Padding(0, 3).Render("No") + + buttons := lipgloss.JoinHorizontal(lipgloss.Center, yes, styles.BaseStyle.Render(" "), no) + buttons = styles.BaseStyle. + Width(maxWidth). + Padding(1, 0). + Render(buttons) + + help := styles.BaseStyle. + Width(maxWidth). + Padding(0, 1). + Foreground(styles.ForgroundDim). + Render("tab/←/→: toggle y/n: yes/no enter: confirm esc: cancel") + + content := lipgloss.JoinVertical( + lipgloss.Left, + title, + styles.BaseStyle.Width(maxWidth).Render(""), + explanation, + question, + buttons, + styles.BaseStyle.Width(maxWidth).Render(""), + help, + ) + + return styles.BaseStyle.Padding(1, 2). + Border(lipgloss.RoundedBorder()). + BorderBackground(styles.Background). + BorderForeground(styles.ForgroundDim). + Width(lipgloss.Width(content) + 4). + Render(content) +} + +// SetSize sets the size of the component. +func (m *InitDialogCmp) SetSize(width, height int) { + m.width = width + m.height = height +} + +// Bindings implements layout.Bindings. +func (m InitDialogCmp) Bindings() []key.Binding { + return m.keys.ShortHelp() +} + +// CloseInitDialogMsg is a message that is sent when the init dialog is closed. +type CloseInitDialogMsg struct { + Initialize bool +} + +// ShowInitDialogMsg is a message that is sent to show the init dialog. +type ShowInitDialogMsg struct { + Show bool +} -- cgit v1.2.3