package tui import ( "fmt" "strings" "tui-ascii-mapper/internal/model" "github.com/charmbracelet/lipgloss" ) func (m *AppModel) View() string { if m.quitting { return "" } if m.mode == model.ModeDialog { return m.renderDialogFullscreen() } if m.colorPicker != nil && m.colorPicker.Active { return m.renderColorPickerFullscreen() } return m.baseView() } func (m *AppModel) baseView() string { gridAreaW := m.width - sidebarW gridH := m.height - 1 showStatusHelp := m.height > 6 if showStatusHelp { gridH -= 2 } if gridAreaW < 1 { gridAreaW = 1 } if gridH < 1 { gridH = 1 } m.scrollToCursor(gridAreaW, gridH) var sb strings.Builder sb.WriteString(m.renderToolbar()) sb.WriteByte('\n') gridLines := strings.Split(m.renderGrid(gridAreaW, gridH), "\n") sidebarLines := strings.Split(m.renderSidebar(gridH), "\n") n := max(len(gridLines), len(sidebarLines)) for i := 0; i < n; i++ { if i < len(gridLines) { sb.WriteString(padRight(gridLines[i], gridAreaW)) } else { sb.WriteString(strings.Repeat(" ", gridAreaW)) } if i < len(sidebarLines) { sb.WriteString(sidebarLines[i]) } sb.WriteByte('\n') } if showStatusHelp { sb.WriteString(m.renderStatus()) sb.WriteByte('\n') sb.WriteString(m.renderHelp()) } return sb.String() } func (m *AppModel) showFeedback(msg string) { m.dialogMsg = msg } func (m *AppModel) scrollToCursor(gw, gh int) { if m.cursor.X < m.offset.X { m.offset.X = m.cursor.X } if m.cursor.X >= m.offset.X+gw { m.offset.X = m.cursor.X - gw + 1 } if m.cursor.Y < m.offset.Y { m.offset.Y = m.cursor.Y } if m.cursor.Y >= m.offset.Y+gh { m.offset.Y = m.cursor.Y - gh + 1 } m.offset.X = model.Clamp(m.offset.X, 0, max(0, m.curMap().Width-gw)) m.offset.Y = model.Clamp(m.offset.Y, 0, max(0, m.curMap().Height-gh)) } func (m *AppModel) centeredFullscreen(content string) string { return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, content) } func renderPopup(title, label, value string) string { lines := []string{ lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("33")).Render(title), "", label + " " + value, } w := 40 for i, l := range lines { lines[i] = lipgloss.NewStyle().Width(w).Render(l) } return lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). Background(lipgloss.Color("234")). Padding(1, 2). Width(w + 4). Render(strings.Join(lines, "\n")) } func padRight(s string, w int) string { for lipgloss.Width(s) < w { s += " " } return s } func formatSize(size int64) string { const unit = 1024 if size < unit { return fmt.Sprintf("%4dB", size) } div, exp := int64(unit), 0 for n := size / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%3.0f%c", float64(size)/float64(div), "KMGTPE"[exp]) } func shortFilename(path string) string { if path == "" { return "(unsaved)" } for i := len(path) - 1; i >= 0; i-- { if path[i] == '/' || path[i] == '\\' { return path[i+1:] } } return path }