Files
LazyPM/pkg/task/taskui.go
Robin Olsen 1cfd23f1b0 Enhance command structure and user feedback in task management
- Added SprintCmd to issues command group
- taskLoop hide survey related commands when in repl
- Updated coding task instructions for clarity
- Removed redundant SprintCmd registration
- Improved REPL command handling for better user experience avoiding a chash if users type pm
- Enhanced task UI to display additional information
2026-04-03 11:19:57 +02:00

212 lines
4.7 KiB
Go

package task
import (
"fmt"
"charm.land/bubbles/v2/key"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/LazyBachelor/LazyPM/internal/models"
"github.com/LazyBachelor/LazyPM/internal/style"
)
type TaskDetails = models.TaskDetails
type TaskModel struct {
TaskDetails
keys TaskHelpKeys
width, height int
userQuit bool
aboutVisible bool
}
type TaskHelpKeys struct {
Quit key.Binding
Start key.Binding
About key.Binding
}
var DefaultTaskKeys = TaskHelpKeys{
Quit: key.NewBinding(
key.WithKeys("ctrl+c"),
key.WithHelp("ctrl+c", "quit"),
),
Start: key.NewBinding(
key.WithKeys("enter"),
key.WithHelp("enter", "start"),
),
About: key.NewBinding(
key.WithKeys("?"),
key.WithHelp("?", "interface info"),
),
}
func NewTaskModel(details TaskDetails) TaskModel {
return TaskModel{
TaskDetails: details,
keys: DefaultTaskKeys,
}
}
func (m TaskModel) Init() tea.Cmd {
return nil
}
func (m TaskModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.SetSize(msg.Width, msg.Height)
case tea.KeyPressMsg:
switch {
case key.Matches(msg, m.keys.Quit):
m.userQuit = true
return m, tea.Quit
case key.Matches(msg, m.keys.Start):
return m, tea.Quit
case key.Matches(msg, m.keys.About):
m.aboutVisible = !m.aboutVisible
return m, nil
}
}
return m, nil
}
func (m TaskModel) View() tea.View {
if m.width < 55 || m.height < 16 {
content := lipgloss.Place(
m.width,
m.height,
lipgloss.Center,
lipgloss.Center,
style.TextStyle.Render("Terminal too small."),
)
v := tea.NewView(content)
v.AltScreen = true
return v
}
boxWidth := min(m.width-8, 120)
cardStyle := style.BorderStyle.Padding(1, 2)
bodyWidth := boxWidth - cardStyle.GetHorizontalFrameSize()
cardStyle = cardStyle.Width(bodyWidth)
textWidth := min(bodyWidth, 64)
titleWrap := lipgloss.NewStyle().
Width(boxWidth).
Align(lipgloss.Center)
helpWrap := lipgloss.NewStyle().
Width(boxWidth).
Align(lipgloss.Center)
descStyle := style.TextStyle.
Width(textWidth).
Align(lipgloss.Left)
detailsStyle := style.TextStyle.
Foreground(style.SecondaryText).
Width(textWidth).
Align(lipgloss.Center)
infoStyle := style.ErrorStyle.
Width(textWidth).
Align(lipgloss.Center)
centerInCard := func(s string) string {
return lipgloss.PlaceHorizontal(bodyWidth, lipgloss.Center, s)
}
detailsText := fmt.Sprintf(
"Interface Type: %s | Time to complete: %s | Difficulty: %s\n",
m.InterfaceType,
m.TimeToComplete,
m.Difficulty,
)
var body string
if m.aboutVisible {
about := style.TextStyle.
Width(bodyWidth - 2).
Align(lipgloss.Left).
Render(m.InterfaceDescription)
body = centerInCard(about)
} else {
body = lipgloss.JoinVertical(
lipgloss.Left,
centerInCard(descStyle.Render(m.Description)),
"",
centerInCard(detailsStyle.Render(detailsText)),
centerInCard(style.TextStyle.Render(`On your desktop "Task Details.txt" will contain the same details`)),
centerInCard(infoStyle.Render(m.interfaceHelpText())),
centerInCard(infoStyle.Render(m.getQuitHelpText())),
)
}
card := cardStyle.Render(body)
helpText := "Press " + m.keys.Start.Help().Key + " to " + m.keys.Start.Help().Desc +
" • " + m.keys.Quit.Help().Key + " to " + m.keys.Quit.Help().Desc +
" • " + m.keys.About.Help().Key + " for " + m.keys.About.Help().Desc
view := lipgloss.JoinVertical(
lipgloss.Center,
titleWrap.Render(style.TitleStyle.Render(m.Title)),
"",
card,
"",
helpWrap.Render(style.HelpStyle.Render(helpText)),
)
final := lipgloss.Place(
m.width,
m.height,
lipgloss.Center,
lipgloss.Center,
view,
)
v := tea.NewView(final)
v.AltScreen = true
return v
}
func (m TaskModel) getQuitHelpText() string {
switch m.InterfaceType {
case models.InterfaceTypeWeb:
return "Press 'q' in the server terminal to skip the task"
case models.InterfaceTypeTUI:
return "Press 'q' to skip task during the survey"
case models.InterfaceTypeCLI, models.InterfaceTypeREPL:
return `Write 'exit' to skip task during the survey
Note: Use 'tab' to scroll through suggestions`
default:
return m.keys.Quit.Help().Key + " to quit"
}
}
func (m TaskModel) interfaceHelpText() string {
switch m.InterfaceType {
case models.InterfaceTypeWeb:
return "Press the button in the upper right corner to view task progress"
case models.InterfaceTypeTUI:
return "Press Shift+S to view task progress and completion criteria"
case models.InterfaceTypeCLI, models.InterfaceTypeREPL:
return "Write 'status' to view task progress and completion criteria"
default:
return m.keys.Quit.Help().Key + " to quit"
}
}
func (m *TaskModel) SetSize(width, height int) {
m.width, m.height = width, height
}
func (m TaskModel) GetUserQuit() bool {
return m.userQuit
}