package main import ( "context" "fmt" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/wish" bm "github.com/charmbracelet/wish/bubbletea" lm "github.com/charmbracelet/wish/logging" "github.com/gliderlabs/ssh" "log" "os" "os/signal" "syscall" "time" ) func main() { log.Println("Welcome on Cemantics!") s, err := wish.NewServer( wish.WithAddress(fmt.Sprintf("%s:%d", "0.0.0.0", 2200)), wish.WithAddress(fmt.Sprintf("%s:%d", "[::]", 2200)), wish.WithHostKeyPath(".ssh/term_info_ed25519"), wish.WithMiddleware( lm.Middleware(), bm.Middleware(teaHandler), ), ) if err != nil { log.Fatalln(err) } done := make(chan os.Signal, 1) signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) log.Printf("Starting SSH server on %s:%d", "[::]", 2200) go func() { if err = s.ListenAndServe(); err != nil { log.Fatalln(err) } }() <-done log.Println("Stopping SSH server") ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer func() { cancel() }() if err := s.Shutdown(ctx); err != nil { log.Fatalln(err) } } func teaHandler(s ssh.Session) (tea.Model, []tea.ProgramOption) { _, _, active := s.Pty() if !active { fmt.Println("no active terminal, skipping") return nil, nil } log.Println(s.RemoteAddr()) ti := textinput.New() ti.Placeholder = "Choisissez un mot ..." ti.Focus() ti.CharLimit = 256 ti.Width = 20 m := model{ textInput: ti, err: nil, ip: s.RemoteAddr().String(), words: []string{}, } return m, []tea.ProgramOption{tea.WithAltScreen()} } type errMsg error type model struct { textInput textinput.Model err error ip string words []string lastWord string } func (m model) Init() tea.Cmd { return textinput.Blink } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: switch msg.Type { case tea.KeyEnter: return m.InputWord() case tea.KeyCtrlC, tea.KeyEsc: return m, tea.Quit } // We handle errors just like any other message case errMsg: m.err = msg return m, nil } m.textInput, cmd = m.textInput.Update(msg) return m, cmd } func (m model) InputWord() (model, tea.Cmd) { word := m.textInput.Value() log.Printf("Chosen word: %s", word) m.lastWord = word m.words = append(m.words, word) m.textInput.SetValue("") m.textInput.CursorStart() return m, nil } func (m model) View() string { msg := fmt.Sprintf( "Veuillez choisir un mot :\n\n%s\n\n%s", m.textInput.View(), "(Esc pour quitter)", ) + "\n\n" if m.lastWord != "" { msg += fmt.Sprintf("Dernier mot essayƩ : %s\n\n", m.lastWord) } if len(m.words) > 0 { msg += fmt.Sprintf("Liste des derniers mots :\n") for _, word := range m.words { msg += fmt.Sprintf("* %s\n", word) } } return msg }