Работа над отображением объектов, работа со стилями, работа с позиционированиме, изменение таблицы переменных

This commit is contained in:
XeRo 2026-01-14 18:08:43 +05:00
parent ada7db46cf
commit 2e8eee135e
7 changed files with 477 additions and 431 deletions

View file

@ -6,11 +6,11 @@ import (
"bubble/types"
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
"github.com/muesli/reflow/wordwrap"
"github.com/sirupsen/logrus"
"strings"
"unicode/utf8"
)
type FormModel struct {
@ -77,12 +77,7 @@ func NewFormModel(application *types.PackageApplication, sectionIdx, focus, widt
height: height,
}
if currentSection.Type == types.SectionAbout {
md := strings.Join(currentSection.Paragraphs, "\n\n")
renderer, _ := glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithWordWrap(width-10))
rendered, _ := renderer.Render(md)
m.renderedContent = rendered
m.renderedContent = simpleAboutRender(currentSection.Paragraphs, width)
m.focus = 0
}
return m
@ -93,24 +88,17 @@ func (m *FormModel) Init() tea.Cmd {
}
func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
if m.CurrentSection.Type == types.SectionAbout {
md := strings.Join(m.CurrentSection.Paragraphs, "\n\n")
renderer, _ := glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithWordWrap(m.width-10),
)
rendered, _ := renderer.Render(md)
m.renderedContent = rendered
m.renderedContent = simpleAboutRender(m.CurrentSection.Paragraphs, m.width)
}
return m, nil
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
case "esc":
for _, field := range m.FieldModels {
if field.CaptureKeys() {
@ -134,7 +122,6 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
},
}
}
//вернуться к списку приложений, чтобы выбрать другое приложение.
case "enter":
logrus.Infof("sections %s, focus %d, fields len %d", m.CurrentSection.Name, m.focus, len(m.FieldModels))
if len(m.FieldModels) == m.focus {
@ -176,71 +163,45 @@ func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case types.SelectedOptionMsg:
logrus.Infof("chose %+v", msg)
}
cmd := m.updateFields(msg)
return m, cmd
return m, m.updateFields(msg)
}
func (m *FormModel) View() string {
contentWidth := ui.ContentWidth(m.width)
var res strings.Builder
containerWidth, containerHeight, contentWidth, _ := ui.ContentSizes(m.width, m.height)
formWidth := contentWidth - 2*1
var strokes []string
for i, field := range m.FieldModels {
fieldStr := wordwrap.String(field.View(), contentWidth-10)
fieldStroke := wordwrap.String(field.View(), formWidth)
if i == m.focus {
res.WriteString(ui.FormFieldFocused.Render(fieldStr))
strokes = append(strokes, ui.FormFieldFocused.Render(fieldStroke))
} else {
res.WriteString(ui.FormFieldUnfocused.Render(fieldStr))
strokes = append(strokes, ui.FormFieldUnfocused.Render(fieldStroke))
}
res.WriteString("\n\n")
}
if m.CurrentSection.Type == types.SectionAbout {
res.WriteString(m.renderedContent)
strokes = append([]string{}, m.renderedContent)
}
nextBtn := "[ next section ]"
if m.focus == len(m.FieldModels) {
res.WriteString(ui.FormNextButton.Render(nextBtn))
strokes = append(strokes, ui.FormNextButton.Render(nextBtn))
} else {
res.WriteString(ui.FormFieldUnfocused.Render(nextBtn))
strokes = append(strokes, ui.FormFieldUnfocused.Render(nextBtn))
}
container := ui.FormContainer.Width(contentWidth).Render(res.String())
return lipgloss.Place(m.width, m.height,
container := ui.AppContainer.
Width(containerWidth).
Height(containerHeight).
Render(strings.Join(strokes, "\n\n"))
return lipgloss.Place(
m.width, m.height,
lipgloss.Center, lipgloss.Center,
container,
lipgloss.WithWhitespaceChars(" "),
)
//for i, field := range m.FieldModels {
// fieldStr := wordwrap.String(field.View(), contentWidth-8)
// if i == m.focus {
// res.WriteString(ui.FormFocusedStyle.Render(fieldStr))
// } else {
// res.WriteString(ui.FormUnfocusedStyle.Render(fieldStr))
// }
// res.WriteString("\n\n")
//}
//
//if m.CurrentSection.Type == types.SectionAbout {
// res.WriteString("\n\n" + m.renderedContent)
//}
//
//button := "[ next section ]"
//if m.focus == len(m.FieldModels) {
// res.WriteString(ui.FormFocusedStyle.Render(button))
//} else {
// res.WriteString(ui.FormUnfocusedStyle.Render(button))
//}
//
//container := ui.FormStyle.Width(contentWidth).Render(res.String())
//
//return lipgloss.Place(m.width, m.height,
// lipgloss.Center, lipgloss.Top,
// container,
// lipgloss.WithWhitespaceChars(" "),
//)
}
func (m *FormModel) updateFields(msg tea.Msg) tea.Cmd {
@ -283,3 +244,42 @@ func (m *FormModel) updateFields(msg tea.Msg) tea.Cmd {
}
return tea.Batch(cmds...)
}
func simpleAboutRender(paragraphs []string, width int) string {
var sb strings.Builder
for _, p := range paragraphs {
p = strings.TrimSpace(p)
if p == "" {
sb.WriteString("\n")
continue
}
switch {
case strings.HasPrefix(p, "# "):
// Заголовок 1 уровня
h1Len := utf8.RuneCountInString(p[2:])
h1Offset := strings.Repeat(" ", (width-h1Len)/2)
sb.WriteString(ui.AboutTitle.Render(h1Offset+p[2:]) + "\n\n")
case strings.HasPrefix(p, "## "):
// Заголовок 2 уровня
sb.WriteString(ui.AboutH2.Render(p[3:]) + "\n\n")
case strings.HasPrefix(p, "### "):
// Заголовок 3 уровня
sb.WriteString(" " + ui.AboutH3.Render(p[4:]) + "\n\n")
case strings.HasPrefix(p, "- "), strings.HasPrefix(p, "* "):
// Список простой
text := strings.TrimLeft(p[2:], " ")
wrapped := wordwrap.String(text, width-10)
sb.WriteString(" " + ui.AboutBullet.Render("• ") + ui.AboutNormal.Render(wrapped) + "\n")
case strings.HasPrefix(p, "> "):
// Цитаты
text := strings.TrimPrefix(p, "> ")
wrapped := wordwrap.String(text, width-12)
sb.WriteString(" " + ui.AboutNormal.Foreground(ui.ColorGrey).Render("│ "+wrapped) + "\n")
default:
// Обычный текст
wrapped := wordwrap.String(p, width-8)
sb.WriteString(" " + ui.AboutNormal.Render(wrapped) + "\n\n")
}
}
return sb.String()
}