-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathview.go
More file actions
163 lines (142 loc) · 5.28 KB
/
view.go
File metadata and controls
163 lines (142 loc) · 5.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package devtui
import (
"github.com/charmbracelet/lipgloss"
. "github.com/tinywasm/fmt"
)
func (h *DevTUI) View() string {
if !h.ready {
return "\n Initializing..."
}
return Sprintf("%s\n%s\n%s", h.headerView(), h.viewport.View(), h.footerView())
}
// ContentView renderiza los mensajes para una sección de contenido
func (h *DevTUI) ContentView() string {
if len(h.TabSections) == 0 {
return "No tabs created yet"
}
if h.activeTab >= len(h.TabSections) {
h.activeTab = 0
}
// Proteger el acceso a tabContents con mutex
section := h.TabSections[h.activeTab]
section.mu.RLock()
tabContent := make([]tabContent, len(section.tabContents)) // Copia para evitar retener el lock
copy(tabContent, section.tabContents)
section.mu.RUnlock()
var contentLines []string
// NEW: Add display handler content if active field is a Display handler
fieldHandlers := section.FieldHandlers
if len(fieldHandlers) > 0 && section.IndexActiveEditField < len(fieldHandlers) {
activeField := fieldHandlers[section.IndexActiveEditField]
if activeField.hasContentMethod() {
displayContent := activeField.getDisplayContent()
if displayContent != "" {
// Add display content at the top of the content view with Primary color
highlightStyle := h.textContentStyle.Foreground(lipgloss.Color(h.Primary))
contentLines = append(contentLines, highlightStyle.Render(displayContent))
// Add separator line if there are also tab messages
if len(tabContent) > 0 {
contentLines = append(contentLines, "")
}
}
}
}
// Add regular tab content messages
for _, content := range tabContent {
formattedMsg := h.formatMessage(content, true)
contentLines = append(contentLines, h.textContentStyle.Render(formattedMsg))
}
return Convert(contentLines).Join("\n").String()
}
// ContentViewPlain renders messages for a content section without ANSI codes (for MCP)
func (h *DevTUI) ContentViewPlain(tabIndex int) string {
if len(h.TabSections) == 0 {
return "No tabs created yet"
}
// Use provided index or default to active tab if out of bounds (though usually caller provides valid index)
idx := tabIndex
if idx < 0 || idx >= len(h.TabSections) {
return "Invalid tab index"
}
section := h.TabSections[idx]
section.mu.RLock()
tabContent := make([]tabContent, len(section.tabContents))
copy(tabContent, section.tabContents)
section.mu.RUnlock()
var contentLines []string
// NEW: Add display handler content if active field is a Display handler
fieldHandlers := section.FieldHandlers
if len(fieldHandlers) > 0 && section.IndexActiveEditField < len(fieldHandlers) {
activeField := fieldHandlers[section.IndexActiveEditField]
if activeField.hasContentMethod() {
displayContent := activeField.getDisplayContent()
if displayContent != "" {
contentLines = append(contentLines, displayContent)
if len(tabContent) > 0 {
contentLines = append(contentLines, "")
}
}
}
}
for _, content := range tabContent {
// styled=false for plain text
formattedMsg := h.formatMessage(content, false)
contentLines = append(contentLines, formattedMsg)
}
return Convert(contentLines).Join("\n").String()
}
func (h *DevTUI) headerView() string {
if len(h.TabSections) == 0 {
return h.headerTitleStyle.Render(h.AppName + "/No tabs")
}
if h.activeTab >= len(h.TabSections) {
h.activeTab = 0
}
tab := h.TabSections[h.activeTab]
// Truncar el título si es necesario
leftText := h.AppName + "/" + tab.Title
rightText := ""
if h.AppVersion != "" {
rightText = h.AppVersion
}
// Calculate available space in UIColumnWidth
rightWidth := lipgloss.Width(rightText)
// Truncate left side if necessary (leave at least 1 space if version exists)
maxLeftWidth := UIColumnWidth
if rightWidth > 0 {
maxLeftWidth = UIColumnWidth - rightWidth - 1
}
if maxLeftWidth < 0 {
maxLeftWidth = 0
}
truncatedLeft := Convert(leftText).Truncate(maxLeftWidth, 0).String()
numSpaces := UIColumnWidth - lipgloss.Width(truncatedLeft) - rightWidth
if numSpaces < 0 {
numSpaces = 0
}
combinedHeader := truncatedLeft + Convert(" ").Repeat(numSpaces).String() + rightText
// Aplicar el estilo base para garantizar un ancho fijo
// Use UIColumnWidth to match the full left column width
fixedWidthHeader := lipgloss.NewStyle().Width(UIColumnWidth).Align(lipgloss.Left).Render(combinedHeader)
// Aplicar el estilo visual manteniendo el ancho fijo
title := h.headerTitleStyle.Render(fixedWidthHeader)
// Pagination logic
currentTab := h.activeTab
totalTabs := len(h.TabSections)
if currentTab > 99 || totalTabs > 99 {
if h.Logger != nil {
h.Logger("Tab limit exceeded:", currentTab, "/", totalTabs)
}
}
displayCurrent := min(currentTab, 99) + 1 // 1-based for display
displayTotal := min(totalTabs, 99)
pagination := Sprintf("%2d/%2d", displayCurrent, displayTotal)
paginationStyled := h.paginationStyle.Render(pagination)
// Remove safety margin to fill full width
// Also account for spacers (width 1 * 2)
horizontalPadding := 1
spacerStyle := lipgloss.NewStyle().Width(horizontalPadding).Render("")
lineWidth := h.viewport.Width - lipgloss.Width(title) - lipgloss.Width(paginationStyled) - horizontalPadding*2
line := h.lineHeadFootStyle.Render(Convert("─").Repeat(max(0, lineWidth)).String())
return lipgloss.JoinHorizontal(lipgloss.Center, title, spacerStyle, line, spacerStyle, paginationStyled)
}