Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
byebyebruce committed Jul 5, 2023
1 parent 9b9bbe8 commit 24dd1a0
Show file tree
Hide file tree
Showing 15 changed files with 685 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
.vscode
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# CLIPTY
> Transform your Command Line Interface (CLI) into a web browser-based terminal emulator.
## Preview
![](doc/preview.png)

## Usage
1.
```bash
go get github.com/byebyebruce/clipty
go get github.com/sorenisanerd/gotty/server
```
2.
```go
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
opt := &server.Options{}
err := clipty.RunServer(ctx, opt, nil, func(ctx context.Context, params map[string][]string, stdin *os.File, stdout *os.File, stderr *os.File) {
for i := 0; i < 10; i++ {
select {
case <-ctx.Done():
return
default:
}
time.Sleep(time.Second)
fmt.Fprintln(stdout, "sleep", i+1)
}
fmt.Fprintln(stdout, "Bye..")
})
if err != nil {
log.Fatal(err)
}
}
```
3.
```bash
go run .
```
4. open http://localhost:8081
## [Example](example)
84 changes: 84 additions & 0 deletions cli_wrapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package clipty

import (
"context"
"errors"
"fmt"
"os"

"github.com/creack/pty"
)

// MainFunc is CLI main loop function.
type MainFunc func(ctx context.Context, params map[string][]string, stdin *os.File, stdout *os.File, stderr *os.File)

type CliWrapper struct {
params map[string][]string
pty *os.File
tty *os.File
cancel context.CancelFunc
}

func New(params map[string][]string, mf MainFunc) (*CliWrapper, error) {
pty, tty, err := pty.Open()
if err != nil {
return nil, errors.Join(err, fmt.Errorf("pyt.Open error"))
}

ctx, cancel := context.WithCancel(context.Background())
c := &CliWrapper{
params: params,
pty: pty,
tty: tty,
cancel: cancel,
}

// When the process is closed by the user,
// close pty so that Read() on the pty breaks with an EOF.
go func() {
defer func() {
tty.Sync()
c.Close()
}()

mf(ctx, params, tty, tty, tty)
}()

return c, nil
}

func (c *CliWrapper) Read(p []byte) (n int, err error) {
return c.pty.Read(p)
}

func (c *CliWrapper) Write(p []byte) (n int, err error) {
return c.pty.Write(p)
}

func (c *CliWrapper) Close() error {
c.cancel()
c.tty.Close()
return c.pty.Close()
}

func (c *CliWrapper) WindowTitleVariables() map[string]interface{} {
return map[string]interface{}{
//"command": c.command,
"params": c.params,
}
}

func (c *CliWrapper) ResizeTerminal(width int, height int) error {
window := pty.Winsize{
Rows: uint16(height),
Cols: uint16(width),
X: 0,
Y: 0,
}
err := pty.Setsize(c.pty, &window)
if err != nil {
return err
} else {
return nil
}
}
Binary file added doc/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions example/chat/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"context"
"fmt"
"log"
"os"

"github.com/byebyebruce/clipty"
tea "github.com/charmbracelet/bubbletea"
"github.com/fatih/color"
"github.com/sorenisanerd/gotty/server"
)

func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
opt := &server.Options{
PermitArguments: true,
PermitWrite: true,
TitleVariables: map[string]interface{}{
"command": "my cli",
},
}
persistParams := map[string][]string{"who": {"bruce"}}
err := clipty.RunServer(ctx, opt, persistParams, mainFunc)
if err != nil {
log.Fatal(err)
}
}

func mainFunc(ctx context.Context, params map[string][]string, stdin *os.File, stdout *os.File, stderr *os.File) {
fmt.Fprintln(stdout, color.GreenString("Hello, "+params["who"][0]))

p := tea.NewProgram(initialModel(), tea.WithContext(ctx), tea.WithAltScreen(), tea.WithInput(stdin), tea.WithOutput(stdout))

if _, err := p.Run(); err != nil {
fmt.Fprintln(stdout, color.RedString("error: %v", err))
}
}
97 changes: 97 additions & 0 deletions example/chat/ui.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package main

import (
"fmt"
"strings"

"github.com/charmbracelet/bubbles/textarea"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)

type (
errMsg error
)

type model struct {
viewport viewport.Model
messages []string
textarea textarea.Model
senderStyle lipgloss.Style
err error
}

func initialModel() model {
ta := textarea.New()
ta.Placeholder = "Send a message..."
ta.Focus()

ta.Prompt = "┃ "
ta.CharLimit = 280

ta.SetWidth(30)
ta.SetHeight(3)

// Remove cursor line styling
ta.FocusedStyle.CursorLine = lipgloss.NewStyle()

ta.ShowLineNumbers = false

vp := viewport.New(30, 5)
vp.SetContent(`Welcome to the chat room!
Type a message and press Enter to send.`)

ta.KeyMap.InsertNewline.SetEnabled(false)

return model{
textarea: ta,
messages: []string{},
viewport: vp,
senderStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("5")),
err: nil,
}
}

func (m model) Init() tea.Cmd {
return textarea.Blink
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var (
tiCmd tea.Cmd
vpCmd tea.Cmd
)

m.textarea, tiCmd = m.textarea.Update(msg)
m.viewport, vpCmd = m.viewport.Update(msg)

switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyCtrlC, tea.KeyEsc:
fmt.Println(m.textarea.Value())
return m, tea.Quit
case tea.KeyEnter:
m.messages = append(m.messages, m.senderStyle.Render("You: ")+m.textarea.Value())
m.viewport.SetContent(strings.Join(m.messages, "\n"))
m.textarea.Reset()
m.viewport.GotoBottom()
}

// We handle errors just like any other message
case errMsg:
m.err = msg
return m, nil
}

return m, tea.Batch(tiCmd, vpCmd)
}

func (m model) View() string {
return fmt.Sprintf(
"%s\n\n%s",
m.viewport.View(),
m.textarea.View(),
) + "\n\n"
}
43 changes: 43 additions & 0 deletions example/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module github.com/byebyebruce/clipty/example

replace github.com/byebyebruce/clipty => ../

go 1.16

require (
github.com/byebyebruce/clipty v0.0.0-00010101000000-000000000000
github.com/charmbracelet/bubbles v0.16.1
github.com/charmbracelet/bubbletea v0.24.2
github.com/charmbracelet/lipgloss v0.7.1
github.com/fatih/color v1.15.0
github.com/manifoldco/promptui v0.9.0
github.com/olekukonko/tablewriter v0.0.5
github.com/schollz/progressbar/v3 v3.13.1
github.com/sorenisanerd/gotty v1.5.0
)

require (
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/creack/pty v1.1.18 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.3.8 // indirect
)
Loading

0 comments on commit 24dd1a0

Please sign in to comment.