package views import ( "fmt" "strings" "unicode" ) // SyncAction is a YouTrack sync menu choice. type SyncAction string const ( SyncActionPull SyncAction = "pull" SyncActionPush SyncAction = "push" ) // SyncOption is one row in the sync menu. type SyncOption struct { Action SyncAction Label string Shortcut rune } // AllSyncOptions is the ordered sync menu. var AllSyncOptions = []SyncOption{ {Action: SyncActionPull, Label: "Pull from YouTrack", Shortcut: 'p'}, {Action: SyncActionPush, Label: "Push to YouTrack", Shortcut: 'u'}, } // SyncMenu renders a pull/push picker overlay. type SyncMenu struct { Cursor int Width int Styles StatusMenuStyles } // NewSyncMenu creates a sync action menu. func NewSyncMenu(width int, styles StatusMenuStyles) SyncMenu { return SyncMenu{Cursor: 0, Width: width, Styles: styles} } // MoveDown moves the selection down. func (m *SyncMenu) MoveDown() { if m.Cursor < len(AllSyncOptions)-1 { m.Cursor++ } } // MoveUp moves the selection up. func (m *SyncMenu) MoveUp() { if m.Cursor > 0 { m.Cursor-- } } // JumpToShortcut selects the option whose shortcut matches r. func (m *SyncMenu) JumpToShortcut(r rune) bool { r = unicode.ToLower(r) for i, opt := range AllSyncOptions { if opt.Shortcut == r { m.Cursor = i return true } } return false } // Selected returns the highlighted action. func (m SyncMenu) Selected() SyncAction { if m.Cursor < 0 || m.Cursor >= len(AllSyncOptions) { return SyncActionPull } return AllSyncOptions[m.Cursor].Action } // View renders the sync menu. func (m SyncMenu) View() string { var lines []string lines = append(lines, m.Styles.Title.Render("YouTrack")) for i, opt := range AllSyncOptions { entry := fmt.Sprintf("%c %s", opt.Shortcut, opt.Label) if i == m.Cursor { lines = append(lines, m.Styles.Selected.Render("▸ "+entry)) } else { lines = append(lines, m.Styles.Normal.Render(" "+entry)) } } body := strings.Join(lines, "\n") w := m.Width if w < 20 { w = 20 } return m.Styles.Box.Width(min(44, w-2)).Render(body) }