mirror of
https://github.com/lukaszraczylo/kportal.git
synced 2026-07-12 07:41:42 +00:00
@@ -0,0 +1,211 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// Color palette for wizards
|
||||
var (
|
||||
primaryColor = lipgloss.Color("205") // Pink/Magenta
|
||||
successColor = lipgloss.Color("42") // Green
|
||||
errorColor = lipgloss.Color("196") // Red
|
||||
warningColor = lipgloss.Color("220") // Yellow
|
||||
mutedColor = lipgloss.Color("241") // Gray
|
||||
accentColor = lipgloss.Color("63") // Purple
|
||||
highlightColor = lipgloss.Color("117") // Light blue
|
||||
)
|
||||
|
||||
// Text styles
|
||||
var (
|
||||
wizardHeaderStyle = lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Foreground(primaryColor).
|
||||
MarginBottom(0)
|
||||
|
||||
wizardStepStyle = lipgloss.NewStyle().
|
||||
Foreground(mutedColor).
|
||||
Italic(true)
|
||||
|
||||
breadcrumbStyle = lipgloss.NewStyle().
|
||||
Foreground(highlightColor).
|
||||
Bold(true)
|
||||
|
||||
selectedStyle = lipgloss.NewStyle().
|
||||
Foreground(primaryColor).
|
||||
Bold(true)
|
||||
|
||||
successStyle = lipgloss.NewStyle().
|
||||
Foreground(successColor).
|
||||
Bold(true)
|
||||
|
||||
errorStyle = lipgloss.NewStyle().
|
||||
Foreground(errorColor).
|
||||
Bold(true)
|
||||
|
||||
warningStyle = lipgloss.NewStyle().
|
||||
Foreground(warningColor).
|
||||
Bold(true)
|
||||
|
||||
mutedStyle = lipgloss.NewStyle().
|
||||
Foreground(mutedColor)
|
||||
|
||||
helpStyle = lipgloss.NewStyle().
|
||||
Foreground(mutedColor).
|
||||
Italic(true)
|
||||
|
||||
spinnerStyle = lipgloss.NewStyle().
|
||||
Foreground(accentColor).
|
||||
Bold(true)
|
||||
)
|
||||
|
||||
// Input styles
|
||||
var (
|
||||
inputStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("252"))
|
||||
|
||||
validInputStyle = lipgloss.NewStyle().
|
||||
Foreground(successColor)
|
||||
)
|
||||
|
||||
// Checkbox styles
|
||||
var (
|
||||
checkedBoxStyle = lipgloss.NewStyle().
|
||||
Foreground(successColor).
|
||||
Bold(true)
|
||||
|
||||
uncheckedBoxStyle = lipgloss.NewStyle().
|
||||
Foreground(mutedColor)
|
||||
)
|
||||
|
||||
// Container styles
|
||||
var (
|
||||
wizardBoxStyle = lipgloss.NewStyle().
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(accentColor).
|
||||
Padding(1, 2).
|
||||
Width(60)
|
||||
)
|
||||
|
||||
// Helper functions for rendering
|
||||
|
||||
// renderProgress returns a step indicator like "Step 2/7"
|
||||
func renderProgress(current, total int) string {
|
||||
return wizardStepStyle.Render(fmt.Sprintf("Step %d/%d", current, total))
|
||||
}
|
||||
|
||||
// renderHeader returns a formatted header with title and progress
|
||||
func renderHeader(title, progress string) string {
|
||||
header := wizardHeaderStyle.Render(title)
|
||||
if progress != "" {
|
||||
header += " " + progress
|
||||
}
|
||||
return header + "\n\n"
|
||||
}
|
||||
|
||||
// renderBreadcrumb returns a formatted breadcrumb path
|
||||
func renderBreadcrumb(parts ...string) string {
|
||||
return breadcrumbStyle.Render(strings.Join(parts, " / "))
|
||||
}
|
||||
|
||||
// renderList renders a list of items with cursor selection and viewport scrolling
|
||||
func renderList(items []string, cursor int, prefix string, scrollOffset int) string {
|
||||
var b strings.Builder
|
||||
|
||||
const viewportHeight = 20
|
||||
totalItems := len(items)
|
||||
|
||||
// Show scroll up indicator if there are items above the viewport
|
||||
if scrollOffset > 0 {
|
||||
b.WriteString(mutedStyle.Render(" ↑ More above ↑") + "\n")
|
||||
}
|
||||
|
||||
// Calculate visible range
|
||||
start := scrollOffset
|
||||
end := scrollOffset + viewportHeight
|
||||
if end > totalItems {
|
||||
end = totalItems
|
||||
}
|
||||
|
||||
// Render visible items
|
||||
for i := start; i < end; i++ {
|
||||
cursorPrefix := prefix
|
||||
if i == cursor {
|
||||
cursorPrefix = "▸ "
|
||||
b.WriteString(selectedStyle.Render(cursorPrefix + items[i]))
|
||||
} else {
|
||||
b.WriteString(cursorPrefix + items[i])
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
// Show scroll down indicator if there are items below the viewport
|
||||
if end < totalItems {
|
||||
b.WriteString(mutedStyle.Render(" ↓ More below ↓") + "\n")
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// renderTextInput renders a text input field with a cursor
|
||||
func renderTextInput(label, value string, valid bool) string {
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString(label)
|
||||
|
||||
inputText := value + "█"
|
||||
if valid {
|
||||
b.WriteString(validInputStyle.Render(inputText))
|
||||
} else {
|
||||
b.WriteString(inputStyle.Render(inputText))
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// overlayContent overlays modal content centered on the base view
|
||||
func overlayContent(base, modal string, termWidth, termHeight int) string {
|
||||
baseLines := strings.Split(base, "\n")
|
||||
modalLines := strings.Split(modal, "\n")
|
||||
|
||||
// Ensure base has enough lines
|
||||
for len(baseLines) < termHeight {
|
||||
baseLines = append(baseLines, "")
|
||||
}
|
||||
|
||||
modalHeight := len(modalLines)
|
||||
modalWidth := 0
|
||||
for _, line := range modalLines {
|
||||
w := lipgloss.Width(line)
|
||||
if w > modalWidth {
|
||||
modalWidth = w
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate center position
|
||||
startRow := (termHeight - modalHeight) / 2
|
||||
if startRow < 0 {
|
||||
startRow = 0
|
||||
}
|
||||
|
||||
// Create result with modal overlaid
|
||||
result := make([]string, len(baseLines))
|
||||
copy(result, baseLines)
|
||||
|
||||
for i, modalLine := range modalLines {
|
||||
row := startRow + i
|
||||
if row >= 0 && row < len(result) {
|
||||
// Center the modal line
|
||||
padding := (termWidth - lipgloss.Width(modalLine)) / 2
|
||||
if padding < 0 {
|
||||
padding = 0
|
||||
}
|
||||
|
||||
result[row] = strings.Repeat(" ", padding) + modalLine
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(result, "\n")
|
||||
}
|
||||
Reference in New Issue
Block a user