Skip to content

feat!: v2 #392

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

Draft
wants to merge 14 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.

84 changes: 12 additions & 72 deletions bubbletea/tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@ package bubbletea

import (
"context"
"fmt"
"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 @@ -41,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 @@ -105,57 +87,15 @@ 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)

// We only force the color profile if the requested session is a PTY.
_, _, ok = sess.Pty()
if !ok {
return r
}

if r.ColorProfile() > cp {
_, _ = fmt.Fprintf(sess.Stderr(), "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 append(makeOpts(sess), tea.WithFilter(func(_ tea.Model, msg tea.Msg) tea.Msg {
if _, ok := msg.(tea.SuspendMsg); ok {
return tea.ResumeMsg{}
}
}
return ""
return msg
}))
}

func newDefaultProgramHandler(handler Handler) ProgramHandler {
Expand All @@ -164,6 +104,6 @@ func newDefaultProgramHandler(handler Handler) ProgramHandler {
if m == nil {
return nil
}
return tea.NewProgram(m, append(opts, makeOpts(s)...)...)
return tea.NewProgram(m, append(opts, MakeOptions(s)...)...)
}
}
19 changes: 10 additions & 9 deletions bubbletea/tea_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@
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),
tea.WithWindowSize(pty.Window.Width, pty.Window.Height),
}
}

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))
}
Loading
Loading