package main import ( "fmt" "strings" "log" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" wishtea "github.com/charmbracelet/wish/bubbletea" ) var asciiArt = []string{} type model struct { title string sections []string selected int currentScreen string width int height int mouseX int mouseY int mouseActive bool isClicked bool } func initialModel() model { return model{ title: "Đorđe Kšan", sections: []string{"About", "Projects", "Contact"}, currentScreen: "menu", mouseActive: true, } } func (m model) Init() tea.Cmd { return nil } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height if m.mouseX < 0 || m.mouseX > m.width || m.mouseY < 0 || m.mouseY > m.height { m.mouseActive = false } case tea.KeyMsg: switch msg.String() { case "q", "ctrl+c": return m, tea.Quit case "up", "k": if m.selected > 0 { m.selected-- fmt.Println("up") } case "down", "j": if m.selected < len(m.sections)-1 { m.selected++ fmt.Println("down") } case "enter": m.currentScreen = m.sections[m.selected] return m, nil case "left", "h": case "right", "l": case "b", "u", "backspace", "esc": m.currentScreen = "menu" return m, nil default: fmt.Println("click") } //TODO add mouse logic case tea.MouseMsg: m.mouseX = msg.X m.mouseY = msg.Y m.mouseActive = m.mouseX >= 0 && m.mouseX < m.width && m.mouseY >= 0 && m.mouseY < m.height if msg.Button == tea.MouseButtonLeft && msg.Type == tea.MouseEventType(tea.MouseActionRelease) { fmt.Println("mouse click") } return m, nil } return m, nil } func (m model) View() string { titleStyle := lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color("#E2E8F0")). Background(lipgloss.Color("#0F172A")). Padding(0, 3) sectionStyle := lipgloss.NewStyle(). Foreground(lipgloss.Color("#94A3B8")) selectedSectionStyle := lipgloss.NewStyle(). Foreground(lipgloss.Color("#8A1D7D")). Bold(true) footerStyle := lipgloss.NewStyle(). Foreground(lipgloss.Color("#64748B")) var output string if m.currentScreen == "menu" { output = titleStyle.Render(m.title) + "\n\n" output += "Sections:\n" for i, section := range m.sections { if i == m.selected { output += selectedSectionStyle.Render("- "+section) + "\n" } else { output += sectionStyle.Render("- "+section) + "\n" } } output += "\n" + footerStyle.Render("Press q to quit.") + "\n" } else { switch m.currentScreen { case "About": output = m.renderAbout() case "Projects": output = m.renderProjects() case "Contact": output = m.renderContact() } } return lipgloss.Place( m.width, m.height, lipgloss.Center, lipgloss.Center, output, ) } var aboutText = ` Tbh I don't even know anymore. <3 ` var contactText = ` Get in touch with me at djordje@ksan.dev. ` var projectsText = ` Here are some projects I’ve worked on... ` var ( titleStyle = lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color("#E2E8F0")). Background(lipgloss.Color("#0F172A")). Padding(0, 3) sectionStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("#94A3B8")) selectedSectionStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("#8A1D7D")). Bold(true) footerStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("#64748B")) contentStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("#FFFFFF")) ) func (m model) renderAbout() string { var str strings.Builder str.WriteString(titleStyle.Render("━━━ About Me ━━━")) str.WriteString("\n") str.WriteString(contentStyle.Render(aboutText)) str.WriteString("\n") str.WriteString(footerStyle.Render("esc: back to menu")) return str.String() } func (m model) renderProjects() string { var str strings.Builder str.WriteString(titleStyle.Render("━━━ Projects ━━━")) str.WriteString("\n") str.WriteString(contentStyle.Render(projectsText)) str.WriteString("\n") str.WriteString(footerStyle.Render("esc: back to menu")) return str.String() } func (m model) renderContact() string { var str strings.Builder str.WriteString(titleStyle.Render("━━━ Contact ━━━")) str.WriteString("\n") str.WriteString(contentStyle.Render(contactText)) str.WriteString("\n") str.WriteString(footerStyle.Render("esc: back to menu")) return str.String() } func main() { server, _ := wish.NewServer( wish.WithAddress("0.0.0.0:2222"), wish.WithHostKeyPath(".ssh/host_ed25519"), wish.WithMiddleware(wishtea.Middleware(teaHandler)), ) _ = server.ListenAndServe() } func teaHandler(s ssh.Session) (tea.Model, []tea.ProgramOption) { ip := s.RemoteAddr().String() user := s.User() println("New connection:") log.Println("User:", user, "IP:", ip) return initialModel(), []tea.ProgramOption{ tea.WithAltScreen(), tea.WithMouseCellMotion(), } }