aboutsummaryrefslogtreecommitdiff
path: root/internal/tui/view.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-29 04:03:38 -0400
committerhistoria <[not public]>2026-06-29 04:03:38 -0400
commitba34490aaed5f7c242c2b0453130fefc07d0d5d0 (patch)
treedb5818757d5080221494cead1c7e61a576d1cb65 /internal/tui/view.go
parentb1e252c996a6332d391f96624a7d6f149eb097c4 (diff)
downloadtui-ascii-mapper-main.tar.gz
restructured projectHEADmain
Diffstat (limited to 'internal/tui/view.go')
-rw-r--r--internal/tui/view.go142
1 files changed, 142 insertions, 0 deletions
diff --git a/internal/tui/view.go b/internal/tui/view.go
new file mode 100644
index 0000000..fd8acdc
--- /dev/null
+++ b/internal/tui/view.go
@@ -0,0 +1,142 @@
+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
+}