Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: v2 #392

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion accesscontrol/accesscontrol.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"fmt"

"github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
"github.com/charmbracelet/wish/v2"
)

// Middleware will exit 1 connections trying to execute commands that are not allowed.
Expand Down
4 changes: 2 additions & 2 deletions accesscontrol/accesscontrol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"testing"

"github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/accesscontrol"
"github.com/charmbracelet/wish/testsession"
"github.com/charmbracelet/wish/v2/accesscontrol"
"github.com/charmbracelet/wish/v2/testsession"
gossh "golang.org/x/crypto/ssh"
)

Expand Down
2 changes: 1 addition & 1 deletion activeterm/activeterm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package activeterm

import (
"github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
"github.com/charmbracelet/wish/v2"
)

// Middleware will exit 1 connections trying with no active terminals.
Expand Down
4 changes: 2 additions & 2 deletions activeterm/activeterm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"testing"

"github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/activeterm"
"github.com/charmbracelet/wish/testsession"
"github.com/charmbracelet/wish/v2/activeterm"
"github.com/charmbracelet/wish/v2/testsession"
gossh "golang.org/x/crypto/ssh"
)

Expand Down
95 changes: 0 additions & 95 deletions bubbletea/query.go

This file was deleted.

67 changes: 6 additions & 61 deletions bubbletea/tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ package bubbletea

import (
"context"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/log"
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/log/v2"
"github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
"github.com/muesli/termenv"
"github.com/charmbracelet/wish/v2"
)

// BubbleTeaHandler is the function Bubble Tea apps implement to hook into the
Expand Down Expand Up @@ -40,35 +37,21 @@ type ProgramHandler func(sess ssh.Session) *tea.Program
// It also captures window resize events and sends them to the tea.Program
// as tea.WindowSizeMsgs.
func Middleware(handler Handler) wish.Middleware {
return MiddlewareWithProgramHandler(newDefaultProgramHandler(handler), termenv.Ascii)
}

// MiddlewareWithColorProfile allows you to specify the minimum number of colors
// this program needs to work properly.
//
// If the client's color profile has less colors than p, p will be forced.
// Use with caution.
func MiddlewareWithColorProfile(handler Handler, profile termenv.Profile) wish.Middleware {
return MiddlewareWithProgramHandler(newDefaultProgramHandler(handler), profile)
return MiddlewareWithProgramHandler(newDefaultProgramHandler(handler))
}

// MiddlewareWithProgramHandler allows you to specify the ProgramHandler to be
// able to access the underlying tea.Program, and the minimum supported color
// profile.
// able to access the underlying tea.Program.
//
// This is useful for creating custom middlewares that need access to
// tea.Program for instance to use p.Send() to send messages to tea.Program.
//
// Make sure to set the tea.WithInput and tea.WithOutput to the ssh.Session
// otherwise the program will not function properly. The recommended way
// of doing so is by using MakeOptions.
//
// If the client's color profile has less colors than p, p will be forced.
// Use with caution.
func MiddlewareWithProgramHandler(handler ProgramHandler, profile termenv.Profile) wish.Middleware {
func MiddlewareWithProgramHandler(handler ProgramHandler) wish.Middleware {
return func(next ssh.Handler) ssh.Handler {
return func(sess ssh.Session) {
sess.Context().SetValue(minColorProfileKey, profile)
program := handler(sess)
if program == nil {
next(sess)
Expand Down Expand Up @@ -104,50 +87,12 @@ func MiddlewareWithProgramHandler(handler ProgramHandler, profile termenv.Profil
}
}

var minColorProfileKey struct{}

var profileNames = [4]string{"TrueColor", "ANSI256", "ANSI", "Ascii"}

// MakeRenderer returns a lipgloss renderer for the current session.
// This function handle PTYs as well, and should be used to style your application.
func MakeRenderer(sess ssh.Session) *lipgloss.Renderer {
cp, ok := sess.Context().Value(minColorProfileKey).(termenv.Profile)
if !ok {
cp = termenv.Ascii
}
r := newRenderer(sess)
if r.ColorProfile() > cp {
wish.Printf(sess, "Warning: Client's terminal is %q, forcing %q\r\n", profileNames[r.ColorProfile()], profileNames[cp])
r.SetColorProfile(cp)
}
return r
}

// MakeOptions returns the tea.WithInput and tea.WithOutput program options
// taking into account possible Emulated or Allocated PTYs.
func MakeOptions(sess ssh.Session) []tea.ProgramOption {
return makeOpts(sess)
}

type sshEnviron []string

var _ termenv.Environ = sshEnviron(nil)

// Environ implements termenv.Environ.
func (e sshEnviron) Environ() []string {
return e
}

// Getenv implements termenv.Environ.
func (e sshEnviron) Getenv(k string) string {
for _, v := range e {
if strings.HasPrefix(v, k+"=") {
return v[len(k)+1:]
}
}
return ""
}

func newDefaultProgramHandler(handler Handler) ProgramHandler {
return func(s ssh.Session) *tea.Program {
m, opts := handler(s)
Expand Down
18 changes: 9 additions & 9 deletions bubbletea/tea_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
package bubbletea

import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/ssh"
"github.com/muesli/termenv"
)

func makeOpts(s ssh.Session) []tea.ProgramOption {
pty, _, ok := s.Pty()
envs := s.Environ()
if ok {
envs = append(envs, "TERM="+pty.Term)
}
//nolint:godox
// TODO: Support Windows PTYs
return []tea.ProgramOption{
tea.WithInput(s),
tea.WithOutput(s),
tea.WithEnvironment(envs),
}
}

func newRenderer(s ssh.Session) *lipgloss.Renderer {
pty, _, _ := s.Pty()
env := sshEnviron(append(s.Environ(), "TERM="+pty.Term))
return lipgloss.NewRenderer(s, termenv.WithEnvironment(env), termenv.WithUnsafe(), termenv.WithColorCache(true))
}
92 changes: 26 additions & 66 deletions bubbletea/tea_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,85 +4,45 @@
package bubbletea

import (
"image/color"
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/colorprofile"
"github.com/charmbracelet/ssh"
"github.com/charmbracelet/x/ansi"
"github.com/charmbracelet/x/input"
"github.com/charmbracelet/x/term"
"github.com/lucasb-eyer/go-colorful"
"github.com/muesli/termenv"
)

func makeOpts(s ssh.Session) []tea.ProgramOption {
pty, _, ok := s.Pty()
if !ok || s.EmulatedPty() {
envs := s.Environ()

if !ok {
return []tea.ProgramOption{
tea.WithInput(s),
tea.WithOutput(s),
tea.WithEnvironment(envs),
}
}

// Make sure we have $TERM in the environment when we have a PTY session.
envs = append(envs, "TERM="+pty.Term)
if s.EmulatedPty() {
return []tea.ProgramOption{
tea.WithInput(s),
tea.WithOutput(s),
// Force color profile to be set based on environment variables. We
// do this because we don't have a real PTY attached, hence
// [ssh.Session.EmulatedPty]. This is sort of a hack, but it's the
// best we can do ;)
tea.WithColorProfile(colorprofile.Env(envs)),
tea.WithEnvironment(envs),
}
}

//nolint:godox
// TODO: Add $SSH_PTY and other environment variables to the environment
// when we have a real PTY attached.

return []tea.ProgramOption{
tea.WithInput(pty.Slave),
tea.WithOutput(pty.Slave),
tea.WithEnvironment(envs),
}
}

func newRenderer(s ssh.Session) *lipgloss.Renderer {
pty, _, ok := s.Pty()
if !ok || pty.Term == "" || pty.Term == "dumb" {
return lipgloss.NewRenderer(s, termenv.WithProfile(termenv.Ascii))
}
env := sshEnviron(append(s.Environ(), "TERM="+pty.Term))
var r *lipgloss.Renderer
var bg color.Color
if ok && pty.Slave != nil {
r = lipgloss.NewRenderer(
pty.Slave,
termenv.WithEnvironment(env),
termenv.WithColorCache(true),
)
state, err := term.MakeRaw(pty.Slave.Fd())
if err == nil {
bg, _ = queryBackgroundColor(pty.Slave, pty.Slave)
term.Restore(pty.Slave.Fd(), state)
}
} else {
r = lipgloss.NewRenderer(
s,
termenv.WithEnvironment(env),
termenv.WithUnsafe(),
termenv.WithColorCache(true),
)
bg = querySessionBackgroundColor(s)
}
if bg != nil {
c, ok := colorful.MakeColor(bg)
if ok {
_, _, l := c.Hsl()
r.SetHasDarkBackground(l < 0.5)
}
}
return r
}

// copied from x/[email protected].
func querySessionBackgroundColor(s ssh.Session) (bg color.Color) {
_ = queryTerminal(s, s, time.Second, func(events []input.Event) bool {
for _, e := range events {
switch e := e.(type) {
case input.BackgroundColorEvent:
bg = e.Color
continue // we need to consume the next DA1 event
case input.PrimaryDeviceAttributesEvent:
return false
}
}
return true
}, ansi.RequestBackgroundColor+ansi.RequestPrimaryDeviceAttributes)
return
}
2 changes: 1 addition & 1 deletion cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"io"
"os/exec"

tea "github.com/charmbracelet/bubbletea"
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/ssh"
)

Expand Down
Loading
Loading