package main // A program demonstrating how to use the WithFilter option to intercept events. import ( "fmt" "log" "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 ( choiceStyle = lipgloss.NewStyle().PaddingLeft(1).Foreground(lipgloss.Color("241")) saveTextStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("170")) quitViewStyle = lipgloss.NewStyle().Padding(1).Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("170")) ) func main() { p := tea.NewProgram(initialModel(), tea.WithFilter(filter)) if _, err := p.Run(); err != nil { log.Fatal(err) } } func filter(teaModel tea.Model, msg tea.Msg) tea.Msg { if _, ok := msg.(tea.QuitMsg); !ok { return msg } m := teaModel.(model) if m.hasChanges { return nil } return msg } type model struct { textarea textarea.Model help help.Model keymap keymap saveText string hasChanges bool quitting bool } type keymap struct { save key.Binding quit key.Binding } func initialModel() model { ti := textarea.New() ti.Placeholder = "Only the best words" ti.Focus() return model{ textarea: ti, help: help.NewModel(), keymap: keymap{ save: key.NewBinding( key.WithKeys("ctrl+s"), key.WithHelp("ctrl+s", "save"), ), quit: key.NewBinding( key.WithKeys("esc", "ctrl+c"), key.WithHelp("esc", "quit"), ), }, } } func (m model) Init() tea.Cmd { return textarea.Blink } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.quitting { return m.updatePromptView(msg) } return m.updateTextView(msg) } func (m model) updateTextView(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd var cmd tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: m.saveText = "" switch { case key.Matches(msg, m.keymap.save): m.saveText = "Changes saved!" m.hasChanges = false case key.Matches(msg, m.keymap.quit): m.quitting = true return m, tea.Quit case msg.Type == tea.KeyRunes: m.saveText = "" m.hasChanges = true fallthrough default: if !m.textarea.Focused() { cmd = m.textarea.Focus() cmds = append(cmds, cmd) } } } m.textarea, cmd = m.textarea.Update(msg) cmds = append(cmds, cmd) return m, tea.Batch(cmds...) } func (m model) updatePromptView(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: // For simplicity's sake, we'll treat any key besides "y" as "no" if key.Matches(msg, m.keymap.quit) || msg.String() == "y" { m.hasChanges = false return m, tea.Quit } m.quitting = false } return m, nil } func (m model) View() string { if m.quitting { if m.hasChanges { text := lipgloss.JoinHorizontal(lipgloss.Top, "You have unsaved changes. Quit without saving?", choiceStyle.Render("[yn]")) return quitViewStyle.Render(text) } return "Very important, thank you\n" } helpView := m.help.ShortHelpView([]key.Binding{ m.keymap.save, m.keymap.quit, }) return fmt.Sprintf( "\nType some important things.\n\n%s\n\n %s\n %s", m.textarea.View(), saveTextStyle.Render(m.saveText), helpView, ) + "\n\n" }