Skip to content

Commit

Permalink
docs(examples): improve wish example
Browse files Browse the repository at this point in the history
  • Loading branch information
meowgorithm committed Mar 6, 2023
1 parent d79fb28 commit c633d7d
Showing 1 changed file with 110 additions and 66 deletions.
176 changes: 110 additions & 66 deletions examples/ssh/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package main
// This example demonstrates how to use a custom Lip Gloss renderer with Wish,
// a package for building custom SSH servers.
//
// The big advantage to using custom renderers here is that we can accurately
// detect the background color and color profile for each client and render
// against that accordingly.
//
// For details on wish see: https://github.com/charmbracelet/wish/

import (
Expand All @@ -19,6 +23,41 @@ import (
"github.com/muesli/termenv"
)

// Available styles.
type styles struct {
bold lipgloss.Style
faint lipgloss.Style
italic lipgloss.Style
underline lipgloss.Style
strikethrough lipgloss.Style
red lipgloss.Style
green lipgloss.Style
yellow lipgloss.Style
blue lipgloss.Style
magenta lipgloss.Style
cyan lipgloss.Style
gray lipgloss.Style
}

// Create new styles against a given renderer.
func makeStyles(r *lipgloss.Renderer) styles {
return styles{
bold: r.NewStyle().SetString("bold").Bold(true),
faint: r.NewStyle().SetString("faint").Faint(true),
italic: r.NewStyle().SetString("italic").Italic(true),
underline: r.NewStyle().SetString("underline").Underline(true),
strikethrough: r.NewStyle().SetString("strikethrough").Strikethrough(true),
red: r.NewStyle().SetString("red").Foreground(lipgloss.Color("#E88388")),
green: r.NewStyle().SetString("green").Foreground(lipgloss.Color("#A8CC8C")),
yellow: r.NewStyle().SetString("yellow").Foreground(lipgloss.Color("#DBAB79")),
blue: r.NewStyle().SetString("blue").Foreground(lipgloss.Color("#71BEF2")),
magenta: r.NewStyle().SetString("magenta").Foreground(lipgloss.Color("#D290E4")),
cyan: r.NewStyle().SetString("cyan").Foreground(lipgloss.Color("#66C2CD")),
gray: r.NewStyle().SetString("gray").Foreground(lipgloss.Color("#B9BFCA")),
}
}

// Bridge Wish and Termenv so we can query for a user's terminal capabilities.
type sshOutput struct {
ssh.Session
tty *os.File
Expand Down Expand Up @@ -49,88 +88,93 @@ func (s *sshEnviron) Environ() []string {
return s.environ
}

func outputFromSession(s ssh.Session) *termenv.Output {
sshPty, _, _ := s.Pty()
// Create a termenv.Output from the session.
func outputFromSession(sess ssh.Session) *termenv.Output {
sshPty, _, _ := sess.Pty()
_, tty, err := pty.Open()
if err != nil {
log.Fatal(err)
}
o := &sshOutput{
Session: s,
Session: sess,
tty: tty,
}
environ := s.Environ()
environ := sess.Environ()
environ = append(environ, fmt.Sprintf("TERM=%s", sshPty.Term))
e := &sshEnviron{
environ: environ,
}
e := &sshEnviron{environ: environ}
return termenv.NewOutput(o, termenv.WithEnvironment(e))
}

// Handle SSH requests.
func handler(next ssh.Handler) ssh.Handler {
return func(sess ssh.Session) {
// Get client's output.
clientOutput := outputFromSession(sess)

pty, _, active := sess.Pty()
if !active {
next(sess)
return
}
width := pty.Window.Width

// Initialize new renderer for the client.
renderer := lipgloss.NewRenderer(lipgloss.WithTermenvOutput(clientOutput))

// Initialize new styles against the renderer.
styles := makeStyles(renderer)

str := strings.Builder{}

fmt.Fprintf(&str, "\n\n%s %s %s %s %s",
styles.bold,
styles.faint,
styles.italic,
styles.underline,
styles.strikethrough,
)

fmt.Fprintf(&str, "\n%s %s %s %s %s %s %s",
styles.red,
styles.green,
styles.yellow,
styles.blue,
styles.magenta,
styles.cyan,
styles.gray,
)

fmt.Fprintf(&str, "\n%s %s %s %s %s %s %s\n\n",
styles.red,
styles.green,
styles.yellow,
styles.blue,
styles.magenta,
styles.cyan,
styles.gray,
)

fmt.Fprintf(&str, "%s %t\n\n", styles.bold.Copy().UnsetString().Render("Has dark background?"), renderer.HasDarkBackground())

block := renderer.Place(width,
lipgloss.Height(str.String()), lipgloss.Center, lipgloss.Center, str.String(),
lipgloss.WithWhitespaceChars("/"),
lipgloss.WithWhitespaceForeground(lipgloss.AdaptiveColor{Light: "250", Dark: "236"}),
)

// Render to client.
wish.WriteString(sess, block)

next(sess)
}
}

func main() {
port := 3456
s, err := wish.NewServer(
wish.WithAddress(fmt.Sprintf(":%d", port)),
wish.WithHostKeyPath("ssh_example"),
wish.WithMiddleware(
func(sh ssh.Handler) ssh.Handler {
return func(s ssh.Session) {
output := outputFromSession(s)
pty, _, active := s.Pty()
if !active {
sh(s)
return
}
width := pty.Window.Width

// Initialize new renderer.
renderer := lipgloss.NewRenderer(lipgloss.WithTermenvOutput(output))

str := strings.Builder{}
fmt.Fprintln(&str)

fmt.Fprintf(&str, "\n%s %s %s %s %s",
renderer.NewStyle().SetString("bold").Bold(true),
renderer.NewStyle().SetString("faint").Faint(true),
renderer.NewStyle().SetString("italic").Italic(true),
renderer.NewStyle().SetString("underline").Underline(true),
renderer.NewStyle().SetString("crossout").Strikethrough(true),
)

fmt.Fprintf(&str, "\n%s %s %s %s %s %s %s",
renderer.NewStyle().SetString("red").Foreground(lipgloss.Color("#E88388")),
renderer.NewStyle().SetString("green").Foreground(lipgloss.Color("#A8CC8C")),
renderer.NewStyle().SetString("yellow").Foreground(lipgloss.Color("#DBAB79")),
renderer.NewStyle().SetString("blue").Foreground(lipgloss.Color("#71BEF2")),
renderer.NewStyle().SetString("magenta").Foreground(lipgloss.Color("#D290E4")),
renderer.NewStyle().SetString("cyan").Foreground(lipgloss.Color("#66C2CD")),
renderer.NewStyle().SetString("gray").Foreground(lipgloss.Color("#B9BFCA")),
)

fmt.Fprintf(&str, "\n%s %s %s %s %s %s %s\n\n",
renderer.NewStyle().SetString("red").Foreground(lipgloss.Color("0")).Background(lipgloss.Color("#E88388")),
renderer.NewStyle().SetString("green").Foreground(lipgloss.Color("0")).Background(lipgloss.Color("#A8CC8C")),
renderer.NewStyle().SetString("yellow").Foreground(lipgloss.Color("0")).Background(lipgloss.Color("#DBAB79")),
renderer.NewStyle().SetString("blue").Foreground(lipgloss.Color("0")).Background(lipgloss.Color("#71BEF2")),
renderer.NewStyle().SetString("magenta").Foreground(lipgloss.Color("0")).Background(lipgloss.Color("#D290E4")),
renderer.NewStyle().SetString("cyan").Foreground(lipgloss.Color("0")).Background(lipgloss.Color("#66C2CD")),
renderer.NewStyle().SetString("gray").Foreground(lipgloss.Color("0")).Background(lipgloss.Color("#B9BFCA")),
)

fmt.Fprintf(&str, "%s %t\n", renderer.NewStyle().SetString("Has dark background?").Bold(true), renderer.HasDarkBackground())
fmt.Fprintln(&str)

final := renderer.Place(width, lipgloss.Height(str.String()), lipgloss.Center, lipgloss.Center, str.String(),
lipgloss.WithWhitespaceChars("/"),
lipgloss.WithWhitespaceForeground(lipgloss.AdaptiveColor{Light: "250", Dark: "236"}),
)
wish.WriteString(s, final)

sh(s)
}
},
lm.Middleware(),
),
wish.WithMiddleware(handler, lm.Middleware()),
)
if err != nil {
log.Fatal(err)
Expand Down

0 comments on commit c633d7d

Please sign in to comment.