Add YouTrack pull/push sync with project settings and time tracking.
Wire manual Y menu and CLI sync for issues and work items, persist per-project credentials, and keep local edits until an explicit push. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user