package tui import ( "fmt" "strings" "github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/r3g1tpr0cs/burterm/internal/decoder" ) // decoderModel — вкладка Decoder: вход + выход, набор кодировок за // ctrl-хоткеями (работают независимо от того, в каком поле курсор — // как и остальные ctrl-комбинации в приложении, они не печатаются как // текст, поэтому их можно ловить прямо поверх textarea.Update) и тумблер // направления encode/decode. type decoderModel struct { input textarea.Model output viewport.Model encode bool // true — кодировать, false — декодировать (для MD5 неприменимо) lastOp string errMsg string focus int // 0 — вход, 1 — выход (скролл) } func newDecoderView() decoderModel { ta := textarea.New() ta.Placeholder = "Вставь текст для кодирования/декодирования" ta.Focus() return decoderModel{ input: ta, output: viewport.New(0, 0), encode: true, } } func (m *decoderModel) SetSize(width, height int) { if width < 0 { width = 0 } if height < 0 { height = 0 } inHeight := height / 2 m.input.SetWidth(width) m.input.SetHeight(inHeight) m.output.Width = width // 8 фиксированных строк вокруг output-viewport (построчный подсчёт по // View(): "Вход"-метка 1, разделитель 1, строка статуса 1, // разделитель+"Выход"-метка 2, разделитель+футер(две строки) 3 — итого // 8). Раньше здесь стояло -4 — не совпадало с тем, что реально рисует // View(), причём задолго до того, как футер стал двухстрочным: это // самостоятельная, отдельная от разбиения футера ошибка в подсчёте. m.output.Height = height - inHeight - 8 } func (m decoderModel) Update(msg tea.Msg) (decoderModel, tea.Cmd) { if key, ok := msg.(tea.KeyMsg); ok { switch key.String() { case "tab": m.focus = (m.focus + 1) % 2 if m.focus == 0 { m.input.Focus() } else { m.input.Blur() } return m, nil case "ctrl+z": m.encode = !m.encode return m, nil case "ctrl+u": m.apply("URL", func(s string) (string, error) { if m.encode { return decoder.URLEncode(s), nil } return decoder.URLDecode(s) }) return m, nil case "ctrl+b": m.apply("Base64", func(s string) (string, error) { if m.encode { return decoder.Base64Encode(s), nil } return decoder.Base64Decode(s) }) return m, nil case "ctrl+e": m.apply("HTML", func(s string) (string, error) { if m.encode { return decoder.HTMLEncode(s), nil } return decoder.HTMLDecode(s), nil }) return m, nil case "ctrl+x": m.apply("Hex", func(s string) (string, error) { if m.encode { return decoder.HexEncode(s), nil } return decoder.HexDecode(s) }) return m, nil case "ctrl+k": m.errMsg = "" m.lastOp = "MD5" m.output.SetContent(decoder.MD5Hash(m.input.Value())) return m, nil case "ctrl+a": m.autoDecode() return m, nil } } var cmd tea.Cmd if m.focus == 0 { m.input, cmd = m.input.Update(msg) } else { m.output, cmd = m.output.Update(msg) } return m, cmd } func (m *decoderModel) apply(name string, fn func(string) (string, error)) { out, err := fn(m.input.Value()) if err != nil { m.errMsg = fmt.Sprintf("%s: %v", name, err) return } m.errMsg = "" dir := "encode" if !m.encode { dir = "decode" } m.lastOp = name + " " + dir m.output.SetContent(out) } func (m *decoderModel) autoDecode() { steps := decoder.SmartDecode(m.input.Value()) if len(steps) == 0 { m.errMsg = "не удалось распознать ни одной известной кодировки" return } m.errMsg = "" var b strings.Builder for i, s := range steps { b.WriteString(fmt.Sprintf("[%d] %s:\n%s\n\n", i+1, s.Operation, s.Result)) } m.lastOp = fmt.Sprintf("авто-цепочка (%d шаг(ов))", len(steps)) m.output.SetContent(b.String()) } func (m decoderModel) View() string { focusMark := func(i int) string { if m.focus == i { return "▸ " } return " " } dir := "encode" if !m.encode { dir = "decode" } var b strings.Builder b.WriteString(focusMark(0) + "Вход\n") b.WriteString(m.input.View()) b.WriteString("\n\n") status := fmt.Sprintf("Направление: %s (ctrl+z)", dir) if m.lastOp != "" { status += " · последняя операция: " + m.lastOp } b.WriteString(status + "\n") if m.errMsg != "" { b.WriteString("Ошибка: " + m.errMsg + "\n") } b.WriteString("\n" + focusMark(1) + "Выход\n") b.WriteString(m.output.View()) b.WriteString("\n\nctrl+u URL · ctrl+b Base64 · ctrl+e HTML · ctrl+x Hex · ctrl+k MD5 · ctrl+a авто-цепочка\n") b.WriteString("f1-f12, pgup — вкладки\n") return b.String() }