package main import ( "encoding/base64" "fmt" "os" "golang.design/x/clipboard" "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textarea" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) var ( cursorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("212")) cursorLineStyle = lipgloss.NewStyle(). Background(lipgloss.Color("57")). Foreground(lipgloss.Color("230")) placeholderStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("238")) endOfBufferStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("235")) focusedPlaceholderStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("99")) focusedBorderStyle = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color("238")) blurredBorderStyle = lipgloss.NewStyle(). Border(lipgloss.HiddenBorder()) ) type keymap = struct { next, copy, clear, quit key.Binding } func newTextarea() textarea.Model { t := textarea.New() t.Prompt = "" t.Placeholder = "Type something" t.ShowLineNumbers = true t.Cursor.Style = cursorStyle t.FocusedStyle.Placeholder = focusedPlaceholderStyle t.BlurredStyle.Placeholder = placeholderStyle t.FocusedStyle.CursorLine = cursorLineStyle t.FocusedStyle.Base = focusedBorderStyle t.BlurredStyle.Base = blurredBorderStyle t.FocusedStyle.EndOfBuffer = endOfBufferStyle t.BlurredStyle.EndOfBuffer = endOfBufferStyle t.KeyMap.DeleteWordBackward.SetEnabled(false) t.KeyMap.LineNext = key.NewBinding(key.WithKeys("down")) t.KeyMap.LinePrevious = key.NewBinding(key.WithKeys("up")) t.CharLimit = (1024 * 8) * 8 t.MaxHeight = 1024 * 8 t.Blur() return t } type model struct { width int height int keymap keymap help help.Model decoded textarea.Model encoded textarea.Model } func newModel() model { m := model{ encoded: newTextarea(), decoded: newTextarea(), help: help.New(), keymap: keymap{ next: key.NewBinding( key.WithKeys("tab"), key.WithHelp("tab", "next"), ), quit: key.NewBinding( key.WithKeys("esc", "ctrl+c"), key.WithHelp("esc", "quit"), ), copy: key.NewBinding( key.WithKeys("ctrl+x"), ), clear: key.NewBinding( key.WithKeys("ctrl+r"), ), }, } m.decoded.Focus() return m } func (m model) Init() tea.Cmd { return textarea.Blink } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: switch { case key.Matches(msg, m.keymap.quit): m.encoded.Blur() m.decoded.Blur() return m, tea.Quit case key.Matches(msg, m.keymap.next): if m.encoded.Focused() { cmds = append(cmds, m.decoded.Focus()) m.encoded.Blur() } else if m.decoded.Focused() { cmds = append(cmds, m.encoded.Focus()) m.decoded.Blur() } case key.Matches(msg, m.keymap.copy): if m.encoded.Focused() { clipboard.Write(clipboard.FmtText, []byte(m.encoded.Value())) } else if m.decoded.Focused() { clipboard.Write(clipboard.FmtText, []byte(m.decoded.Value())) } case key.Matches(msg, m.keymap.clear): m.encoded.SetValue("") m.decoded.SetValue("") } case tea.WindowSizeMsg: m.height = msg.Height m.width = msg.Width } m.sizeInputs() if m.encoded.Focused() { decoded, err := base64.StdEncoding.DecodeString(m.encoded.Value()) if err != nil { // log.Printf("failed to decode: %v", err) } else { m.decoded.SetValue(string(decoded)) } } else if m.decoded.Focused() { encoded := base64.StdEncoding.EncodeToString([]byte(m.decoded.Value())) m.encoded.SetValue(string(encoded)) } encoded, cmd := m.encoded.Update(msg) cmds = append(cmds, cmd) m.encoded = encoded decoded, cmd := m.decoded.Update(msg) cmds = append(cmds, cmd) m.decoded = decoded return m, tea.Batch(cmds...) } func (m *model) sizeInputs() { m.encoded.SetHeight(m.height - 5) m.decoded.SetHeight(m.height - 5) m.encoded.SetWidth(m.width/2 - 5) m.decoded.SetWidth(m.width/2 - 5) } func (m model) View() string { help := m.help.ShortHelpView([]key.Binding{ m.keymap.next, m.keymap.copy, m.keymap.quit, }) var views []string views = append(views, m.decoded.View()) views = append(views, m.encoded.View()) return lipgloss.JoinHorizontal(lipgloss.Top, views...) + "\n" + help } func main() { clipboard.Init() if _, err := tea.NewProgram(newModel(), tea.WithAltScreen()).Run(); err != nil { fmt.Println("Error while running program:", err) os.Exit(1) } }