package ui import ( "fmt" "strings" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" ) const ( // Status is the status buffer. Status = iota // Profile is the profile buffer. Profile ) // BufferHandler is the concrete functionality of a Buffer. type BufferHandler interface { validateInput(m model, input string) error // validate incoming input handleInput(input, hiddenInput string) tea.Msg // handle and dispatch messages from input logToFile(m model, input string) // log input to file if persistence is enabled } // Buffer is a user-facing interactive buffer in the TUI. type Buffer struct { menuBarName string // user-friendly buffer name for the menu inputChannel chan string // channel for passing input to handlers viewport viewport.Model // viewport for rendering lines viewportLines []string // lines for viewport renderer viewportIsReady bool // whether or not the viewport is ready to render persistenceIsEnabled bool // whether or not to persist buffer input to file BufferHandler } // StatusBuffer is the privileged status buffer where e.g. you input // program-wide commands. It can also report general status information, e.g. // the status of ACN connectivity. type StatusBufferHandler Buffer func (s StatusBufferHandler) validateInput(m model, input string) error { if string(input[0]) != "/" { return fmt.Errorf("Woops, this is not a chat buffer. Only commands are allowed") } return nil } func (s StatusBufferHandler) handleInput(input, hiddenInput string) tea.Msg { cmds := strings.Split(input, " ") switch { case cmds[0] == "help": return cmdMsg{output: strings.Split(cmdHelp, "\n")} case cmds[0] == "quit": return turnAcnOffMsg{quitProgram: true} case cmds[0] == "start": return showGettingStartedGuideMsg{} case cmds[0] == "profile": if cmds[1] != "unlock" && cmds[1] != "info" && cmds[1] != "create" { profileHelp := `Unknown "profile" command? /profile create | Create a new profile /profile info | Show profile information /profile unlock | Unlock profile(s)` return cmdMsg{output: strings.Split(profileHelp, "\n")} } switch { case cmds[1] == "unlock": if len(cmds) != 3 { return cmdMsg{output: strings.Split(cmdHelp, "\n")} } return profileUnlockMsg{ password: strings.TrimSpace(hiddenInput), } case cmds[1] == "info": return profileInfoMsg{} case cmds[1] == "create": if len(cmds) != 5 { return cmdMsg{output: strings.Split(cmdHelp, "\n")} } passwords := strings.Split(strings.TrimSpace(hiddenInput), " ") if len(passwords) != 2 { return cmdMsg{output: []string{"Profile create: unable to parse hidden input"}} } if passwords[0] != passwords[1] { return cmdMsg{output: []string{"Profile create: passwords do not match?"}} } return profileCreateMsg{ name: cmds[2], password: passwords[1], } } case cmds[0] == "acn": if cmds[1] != "on" && cmds[1] != "off" { acnHelp := `Unknown "acn" command? /acn on | Turn on the Tor ACN /acn off | Turn off the Tor ACN` return cmdMsg{output: strings.Split(acnHelp, "\n")} } switch { case cmds[1] == "on": return turnAcnOnMsg{} case cmds[1] == "off": return turnAcnOffMsg{} } case cmds[0] == "clear": return clearScreenMsg{} } return cmdMsg{output: []string{unknownCmdHelp}} } func (s StatusBufferHandler) logToFile(m model, input string) { if !s.persistenceIsEnabled { return } // TODO: implement saving to file under ~/.cairde } // ProfileBuffer is the profile buffer. There can be several profile buffers // which correspond to several profiles. Profile buffers take input and report // output for profile-specific information, e.g. invites from a new contact. type ProfileBuffer Buffer