Przeglądaj źródła

d01 Проба bubbletea

SVI 3 lat temu
rodzic
commit
dc6aa9adf0
100 zmienionych plików z 11094 dodań i 1926 usunięć
  1. 5 25
      cmd/desktop/main.go
  2. 22 0
      vendor/github.com/atotto/clipboard/.travis.yml
  3. 27 0
      vendor/github.com/atotto/clipboard/LICENSE
  4. 48 0
      vendor/github.com/atotto/clipboard/README.md
  5. 20 0
      vendor/github.com/atotto/clipboard/clipboard.go
  6. 52 0
      vendor/github.com/atotto/clipboard/clipboard_darwin.go
  7. 42 0
      vendor/github.com/atotto/clipboard/clipboard_plan9.go
  8. 149 0
      vendor/github.com/atotto/clipboard/clipboard_unix.go
  9. 157 0
      vendor/github.com/atotto/clipboard/clipboard_windows.go
  10. 1 1
      vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE
  11. 83 0
      vendor/github.com/aymanbagabas/go-osc52/v2/README.md
  12. 305 0
      vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go
  13. 2 2
      vendor/github.com/charmbracelet/bubbles/LICENSE
  14. 207 0
      vendor/github.com/charmbracelet/bubbles/cursor/cursor.go
  15. 142 0
      vendor/github.com/charmbracelet/bubbles/key/key.go
  16. 102 0
      vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go
  17. 721 0
      vendor/github.com/charmbracelet/bubbles/textinput/textinput.go
  18. 22 0
      vendor/github.com/charmbracelet/bubbletea/.gitignore
  19. 47 0
      vendor/github.com/charmbracelet/bubbletea/.golangci-soft.yml
  20. 29 0
      vendor/github.com/charmbracelet/bubbletea/.golangci.yml
  21. 13 0
      vendor/github.com/charmbracelet/bubbletea/CONTRIBUTING.md
  22. 21 0
      vendor/github.com/charmbracelet/bubbletea/LICENSE
  23. 405 0
      vendor/github.com/charmbracelet/bubbletea/README.md
  24. 172 0
      vendor/github.com/charmbracelet/bubbletea/commands.go
  25. 129 0
      vendor/github.com/charmbracelet/bubbletea/exec.go
  26. 664 0
      vendor/github.com/charmbracelet/bubbletea/key.go
  27. 52 0
      vendor/github.com/charmbracelet/bubbletea/logging.go
  28. 149 0
      vendor/github.com/charmbracelet/bubbletea/mouse.go
  29. 19 0
      vendor/github.com/charmbracelet/bubbletea/nil_renderer.go
  30. 202 0
      vendor/github.com/charmbracelet/bubbletea/options.go
  31. 56 0
      vendor/github.com/charmbracelet/bubbletea/renderer.go
  32. 169 0
      vendor/github.com/charmbracelet/bubbletea/screen.go
  33. 33 0
      vendor/github.com/charmbracelet/bubbletea/signals_unix.go
  34. 10 0
      vendor/github.com/charmbracelet/bubbletea/signals_windows.go
  35. 657 0
      vendor/github.com/charmbracelet/bubbletea/standard_renderer.go
  36. 691 0
      vendor/github.com/charmbracelet/bubbletea/tea.go
  37. 131 0
      vendor/github.com/charmbracelet/bubbletea/tty.go
  38. 42 0
      vendor/github.com/charmbracelet/bubbletea/tty_unix.go
  39. 47 0
      vendor/github.com/charmbracelet/bubbletea/tty_windows.go
  40. 1 0
      vendor/github.com/charmbracelet/lipgloss/.gitignore
  41. 47 0
      vendor/github.com/charmbracelet/lipgloss/.golangci-soft.yml
  42. 29 0
      vendor/github.com/charmbracelet/lipgloss/.golangci.yml
  43. 21 0
      vendor/github.com/charmbracelet/lipgloss/LICENSE
  44. 458 0
      vendor/github.com/charmbracelet/lipgloss/README.md
  45. 82 0
      vendor/github.com/charmbracelet/lipgloss/align.go
  46. 7 0
      vendor/github.com/charmbracelet/lipgloss/ansi_unix.go
  47. 22 0
      vendor/github.com/charmbracelet/lipgloss/ansi_windows.go
  48. 412 0
      vendor/github.com/charmbracelet/lipgloss/borders.go
  49. 172 0
      vendor/github.com/charmbracelet/lipgloss/color.go
  50. 481 0
      vendor/github.com/charmbracelet/lipgloss/get.go
  51. 175 0
      vendor/github.com/charmbracelet/lipgloss/join.go
  52. 154 0
      vendor/github.com/charmbracelet/lipgloss/position.go
  53. 143 0
      vendor/github.com/charmbracelet/lipgloss/renderer.go
  54. 43 0
      vendor/github.com/charmbracelet/lipgloss/runes.go
  55. 634 0
      vendor/github.com/charmbracelet/lipgloss/set.go
  56. 41 0
      vendor/github.com/charmbracelet/lipgloss/size.go
  57. 497 0
      vendor/github.com/charmbracelet/lipgloss/style.go
  58. 306 0
      vendor/github.com/charmbracelet/lipgloss/unset.go
  59. 83 0
      vendor/github.com/charmbracelet/lipgloss/whitespace.go
  60. 20 0
      vendor/github.com/containerd/console/.golangci.yml
  61. 3 14
      vendor/github.com/containerd/console/LICENSE
  62. 29 0
      vendor/github.com/containerd/console/README.md
  63. 87 0
      vendor/github.com/containerd/console/console.go
  64. 281 0
      vendor/github.com/containerd/console/console_linux.go
  65. 157 0
      vendor/github.com/containerd/console/console_unix.go
  66. 222 0
      vendor/github.com/containerd/console/console_windows.go
  67. 46 0
      vendor/github.com/containerd/console/pty_freebsd_cgo.go
  68. 37 0
      vendor/github.com/containerd/console/pty_freebsd_nocgo.go
  69. 31 0
      vendor/github.com/containerd/console/pty_unix.go
  70. 43 0
      vendor/github.com/containerd/console/pty_zos.go
  71. 44 0
      vendor/github.com/containerd/console/tc_darwin.go
  72. 58 0
      vendor/github.com/containerd/console/tc_freebsd_cgo.go
  73. 56 0
      vendor/github.com/containerd/console/tc_freebsd_nocgo.go
  74. 51 0
      vendor/github.com/containerd/console/tc_linux.go
  75. 45 0
      vendor/github.com/containerd/console/tc_netbsd.go
  76. 52 0
      vendor/github.com/containerd/console/tc_openbsd_cgo.go
  77. 48 0
      vendor/github.com/containerd/console/tc_openbsd_nocgo.go
  78. 92 0
      vendor/github.com/containerd/console/tc_unix.go
  79. 39 0
      vendor/github.com/containerd/console/tc_zos.go
  80. 0 16
      vendor/github.com/epiclabs-io/winman/.travis.yml
  81. 0 80
      vendor/github.com/epiclabs-io/winman/README.md
  82. 0 20
      vendor/github.com/epiclabs-io/winman/button.go
  83. 0 83
      vendor/github.com/epiclabs-io/winman/clipregion.go
  84. 0 21
      vendor/github.com/epiclabs-io/winman/doc.go
  85. 0 374
      vendor/github.com/epiclabs-io/winman/manager.go
  86. 0 32
      vendor/github.com/epiclabs-io/winman/rect.go
  87. 0 99
      vendor/github.com/epiclabs-io/winman/stack.go
  88. 0 280
      vendor/github.com/epiclabs-io/winman/window.go
  89. 0 13
      vendor/github.com/gdamore/encoding/.appveyor.yml
  90. 0 7
      vendor/github.com/gdamore/encoding/.travis.yml
  91. 0 202
      vendor/github.com/gdamore/encoding/LICENSE
  92. 0 19
      vendor/github.com/gdamore/encoding/README.md
  93. 0 36
      vendor/github.com/gdamore/encoding/ascii.go
  94. 0 196
      vendor/github.com/gdamore/encoding/charmap.go
  95. 0 17
      vendor/github.com/gdamore/encoding/doc.go
  96. 0 273
      vendor/github.com/gdamore/encoding/ebcdic.go
  97. 0 33
      vendor/github.com/gdamore/encoding/latin1.go
  98. 0 35
      vendor/github.com/gdamore/encoding/latin5.go
  99. 0 35
      vendor/github.com/gdamore/encoding/utf8.go
  100. 0 13
      vendor/github.com/gdamore/tcell/v2/.appveyor.yml

+ 5 - 25
cmd/desktop/main.go

@@ -1,34 +1,14 @@
 package main
 
 import (
-	"github.com/epiclabs-io/winman"
-	"github.com/rivo/tview"
+	"log"
+	"wartank/desktop"
 )
 
 func main() {
 
-	app := tview.NewApplication()
-	wm := winman.NewWindowManager()
-
-	content := tview.NewTextView().
-		SetText("Привет, мир!").        // set content of the text view
-		SetTextAlign(tview.AlignCenter) // align text to the center of the text view
-
-	window := wm.NewWindow(). // create new window and add it to the window manager
-					Show().                   // make window visible
-					SetRoot(content).         // have the text view above be the content of the window
-					SetDraggable(true).       // make window draggable around the screen
-					SetResizable(true).       // make the window resizable
-					SetTitle("[ WarTank ]").  // set the window title
-					AddButton(&winman.Button{ // create a button with an X to close the application
-			Symbol:  'X',
-			OnClick: func() { app.Stop() }, // close the application
-		})
-
-	window.SetRect(5, 5, 30, 10) // place the window
-
-	// now, execute the application:
-	if err := app.SetRoot(wm, true).EnableMouse(true).Run(); err != nil {
-		panic(err)
+	app := desktop.NewDesktop()
+	if err := app.Run(); err != nil {
+		log.Printf("main(): in wor desktop, err=\n\t%v", err)
 	}
 }

+ 22 - 0
vendor/github.com/atotto/clipboard/.travis.yml

@@ -0,0 +1,22 @@
+language: go
+
+os:
+ - linux
+ - osx
+ - windows
+
+go:
+ - go1.13.x
+ - go1.x
+
+services:
+ - xvfb
+
+before_install:
+ - export DISPLAY=:99.0
+
+script:
+ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install xsel; fi
+ - go test -v .
+ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install xclip; fi
+ - go test -v .

+ 27 - 0
vendor/github.com/atotto/clipboard/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2013 Ato Araki. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of @atotto. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 48 - 0
vendor/github.com/atotto/clipboard/README.md

@@ -0,0 +1,48 @@
+[![Build Status](https://travis-ci.org/atotto/clipboard.svg?branch=master)](https://travis-ci.org/atotto/clipboard)
+
+[![GoDoc](https://godoc.org/github.com/atotto/clipboard?status.svg)](http://godoc.org/github.com/atotto/clipboard)
+
+# Clipboard for Go
+
+Provide copying and pasting to the Clipboard for Go.
+
+Build:
+
+    $ go get github.com/atotto/clipboard
+
+Platforms:
+
+* OSX
+* Windows 7 (probably work on other Windows)
+* Linux, Unix (requires 'xclip' or 'xsel' command to be installed)
+
+
+Document: 
+
+* http://godoc.org/github.com/atotto/clipboard
+
+Notes:
+
+* Text string only
+* UTF-8 text encoding only (no conversion)
+
+TODO:
+
+* Clipboard watcher(?)
+
+## Commands:
+
+paste shell command:
+
+    $ go get github.com/atotto/clipboard/cmd/gopaste
+    $ # example:
+    $ gopaste > document.txt
+
+copy shell command:
+
+    $ go get github.com/atotto/clipboard/cmd/gocopy
+    $ # example:
+    $ cat document.txt | gocopy
+
+
+

+ 20 - 0
vendor/github.com/atotto/clipboard/clipboard.go

@@ -0,0 +1,20 @@
+// Copyright 2013 @atotto. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package clipboard read/write on clipboard
+package clipboard
+
+// ReadAll read string from clipboard
+func ReadAll() (string, error) {
+	return readAll()
+}
+
+// WriteAll write string to clipboard
+func WriteAll(text string) error {
+	return writeAll(text)
+}
+
+// Unsupported might be set true during clipboard init, to help callers decide
+// whether or not to offer clipboard options.
+var Unsupported bool

+ 52 - 0
vendor/github.com/atotto/clipboard/clipboard_darwin.go

@@ -0,0 +1,52 @@
+// Copyright 2013 @atotto. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin
+
+package clipboard
+
+import (
+	"os/exec"
+)
+
+var (
+	pasteCmdArgs = "pbpaste"
+	copyCmdArgs  = "pbcopy"
+)
+
+func getPasteCommand() *exec.Cmd {
+	return exec.Command(pasteCmdArgs)
+}
+
+func getCopyCommand() *exec.Cmd {
+	return exec.Command(copyCmdArgs)
+}
+
+func readAll() (string, error) {
+	pasteCmd := getPasteCommand()
+	out, err := pasteCmd.Output()
+	if err != nil {
+		return "", err
+	}
+	return string(out), nil
+}
+
+func writeAll(text string) error {
+	copyCmd := getCopyCommand()
+	in, err := copyCmd.StdinPipe()
+	if err != nil {
+		return err
+	}
+
+	if err := copyCmd.Start(); err != nil {
+		return err
+	}
+	if _, err := in.Write([]byte(text)); err != nil {
+		return err
+	}
+	if err := in.Close(); err != nil {
+		return err
+	}
+	return copyCmd.Wait()
+}

+ 42 - 0
vendor/github.com/atotto/clipboard/clipboard_plan9.go

@@ -0,0 +1,42 @@
+// Copyright 2013 @atotto. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build plan9
+
+package clipboard
+
+import (
+	"os"
+	"io/ioutil"
+)
+
+func readAll() (string, error) {
+	f, err := os.Open("/dev/snarf")
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+
+	str, err := ioutil.ReadAll(f)
+	if err != nil {
+		return "", err
+	}
+	
+	return string(str), nil
+}
+
+func writeAll(text string) error {
+	f, err := os.OpenFile("/dev/snarf", os.O_WRONLY, 0666)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	
+	_, err = f.Write([]byte(text))
+	if err != nil {
+		return err
+	}
+	
+	return nil
+}

+ 149 - 0
vendor/github.com/atotto/clipboard/clipboard_unix.go

@@ -0,0 +1,149 @@
+// Copyright 2013 @atotto. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build freebsd linux netbsd openbsd solaris dragonfly
+
+package clipboard
+
+import (
+	"errors"
+	"os"
+	"os/exec"
+)
+
+const (
+	xsel               = "xsel"
+	xclip              = "xclip"
+	powershellExe      = "powershell.exe"
+	clipExe            = "clip.exe"
+	wlcopy             = "wl-copy"
+	wlpaste            = "wl-paste"
+	termuxClipboardGet = "termux-clipboard-get"
+	termuxClipboardSet = "termux-clipboard-set"
+)
+
+var (
+	Primary bool
+	trimDos bool
+
+	pasteCmdArgs []string
+	copyCmdArgs  []string
+
+	xselPasteArgs = []string{xsel, "--output", "--clipboard"}
+	xselCopyArgs  = []string{xsel, "--input", "--clipboard"}
+
+	xclipPasteArgs = []string{xclip, "-out", "-selection", "clipboard"}
+	xclipCopyArgs  = []string{xclip, "-in", "-selection", "clipboard"}
+
+	powershellExePasteArgs = []string{powershellExe, "Get-Clipboard"}
+	clipExeCopyArgs        = []string{clipExe}
+
+	wlpasteArgs = []string{wlpaste, "--no-newline"}
+	wlcopyArgs  = []string{wlcopy}
+
+	termuxPasteArgs = []string{termuxClipboardGet}
+	termuxCopyArgs  = []string{termuxClipboardSet}
+
+	missingCommands = errors.New("No clipboard utilities available. Please install xsel, xclip, wl-clipboard or Termux:API add-on for termux-clipboard-get/set.")
+)
+
+func init() {
+	if os.Getenv("WAYLAND_DISPLAY") != "" {
+		pasteCmdArgs = wlpasteArgs
+		copyCmdArgs = wlcopyArgs
+
+		if _, err := exec.LookPath(wlcopy); err == nil {
+			if _, err := exec.LookPath(wlpaste); err == nil {
+				return
+			}
+		}
+	}
+
+	pasteCmdArgs = xclipPasteArgs
+	copyCmdArgs = xclipCopyArgs
+
+	if _, err := exec.LookPath(xclip); err == nil {
+		return
+	}
+
+	pasteCmdArgs = xselPasteArgs
+	copyCmdArgs = xselCopyArgs
+
+	if _, err := exec.LookPath(xsel); err == nil {
+		return
+	}
+
+	pasteCmdArgs = termuxPasteArgs
+	copyCmdArgs = termuxCopyArgs
+
+	if _, err := exec.LookPath(termuxClipboardSet); err == nil {
+		if _, err := exec.LookPath(termuxClipboardGet); err == nil {
+			return
+		}
+	}
+
+	pasteCmdArgs = powershellExePasteArgs
+	copyCmdArgs = clipExeCopyArgs
+	trimDos = true
+
+	if _, err := exec.LookPath(clipExe); err == nil {
+		if _, err := exec.LookPath(powershellExe); err == nil {
+			return
+		}
+	}
+
+	Unsupported = true
+}
+
+func getPasteCommand() *exec.Cmd {
+	if Primary {
+		pasteCmdArgs = pasteCmdArgs[:1]
+	}
+	return exec.Command(pasteCmdArgs[0], pasteCmdArgs[1:]...)
+}
+
+func getCopyCommand() *exec.Cmd {
+	if Primary {
+		copyCmdArgs = copyCmdArgs[:1]
+	}
+	return exec.Command(copyCmdArgs[0], copyCmdArgs[1:]...)
+}
+
+func readAll() (string, error) {
+	if Unsupported {
+		return "", missingCommands
+	}
+	pasteCmd := getPasteCommand()
+	out, err := pasteCmd.Output()
+	if err != nil {
+		return "", err
+	}
+	result := string(out)
+	if trimDos && len(result) > 1 {
+		result = result[:len(result)-2]
+	}
+	return result, nil
+}
+
+func writeAll(text string) error {
+	if Unsupported {
+		return missingCommands
+	}
+	copyCmd := getCopyCommand()
+	in, err := copyCmd.StdinPipe()
+	if err != nil {
+		return err
+	}
+
+	if err := copyCmd.Start(); err != nil {
+		return err
+	}
+	if _, err := in.Write([]byte(text)); err != nil {
+		return err
+	}
+	if err := in.Close(); err != nil {
+		return err
+	}
+	return copyCmd.Wait()
+}

+ 157 - 0
vendor/github.com/atotto/clipboard/clipboard_windows.go

@@ -0,0 +1,157 @@
+// Copyright 2013 @atotto. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+package clipboard
+
+import (
+	"runtime"
+	"syscall"
+	"time"
+	"unsafe"
+)
+
+const (
+	cfUnicodetext = 13
+	gmemMoveable  = 0x0002
+)
+
+var (
+	user32                     = syscall.MustLoadDLL("user32")
+	isClipboardFormatAvailable = user32.MustFindProc("IsClipboardFormatAvailable")
+	openClipboard              = user32.MustFindProc("OpenClipboard")
+	closeClipboard             = user32.MustFindProc("CloseClipboard")
+	emptyClipboard             = user32.MustFindProc("EmptyClipboard")
+	getClipboardData           = user32.MustFindProc("GetClipboardData")
+	setClipboardData           = user32.MustFindProc("SetClipboardData")
+
+	kernel32     = syscall.NewLazyDLL("kernel32")
+	globalAlloc  = kernel32.NewProc("GlobalAlloc")
+	globalFree   = kernel32.NewProc("GlobalFree")
+	globalLock   = kernel32.NewProc("GlobalLock")
+	globalUnlock = kernel32.NewProc("GlobalUnlock")
+	lstrcpy      = kernel32.NewProc("lstrcpyW")
+)
+
+// waitOpenClipboard opens the clipboard, waiting for up to a second to do so.
+func waitOpenClipboard() error {
+	started := time.Now()
+	limit := started.Add(time.Second)
+	var r uintptr
+	var err error
+	for time.Now().Before(limit) {
+		r, _, err = openClipboard.Call(0)
+		if r != 0 {
+			return nil
+		}
+		time.Sleep(time.Millisecond)
+	}
+	return err
+}
+
+func readAll() (string, error) {
+	// LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution).
+	// Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock.
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+	if formatAvailable, _, err := isClipboardFormatAvailable.Call(cfUnicodetext); formatAvailable == 0 {
+		return "", err
+	}
+	err := waitOpenClipboard()
+	if err != nil {
+		return "", err
+	}
+
+	h, _, err := getClipboardData.Call(cfUnicodetext)
+	if h == 0 {
+		_, _, _ = closeClipboard.Call()
+		return "", err
+	}
+
+	l, _, err := globalLock.Call(h)
+	if l == 0 {
+		_, _, _ = closeClipboard.Call()
+		return "", err
+	}
+
+	text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:])
+
+	r, _, err := globalUnlock.Call(h)
+	if r == 0 {
+		_, _, _ = closeClipboard.Call()
+		return "", err
+	}
+
+	closed, _, err := closeClipboard.Call()
+	if closed == 0 {
+		return "", err
+	}
+	return text, nil
+}
+
+func writeAll(text string) error {
+	// LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution).
+	// Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock.
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	err := waitOpenClipboard()
+	if err != nil {
+		return err
+	}
+
+	r, _, err := emptyClipboard.Call(0)
+	if r == 0 {
+		_, _, _ = closeClipboard.Call()
+		return err
+	}
+
+	data := syscall.StringToUTF16(text)
+
+	// "If the hMem parameter identifies a memory object, the object must have
+	// been allocated using the function with the GMEM_MOVEABLE flag."
+	h, _, err := globalAlloc.Call(gmemMoveable, uintptr(len(data)*int(unsafe.Sizeof(data[0]))))
+	if h == 0 {
+		_, _, _ = closeClipboard.Call()
+		return err
+	}
+	defer func() {
+		if h != 0 {
+			globalFree.Call(h)
+		}
+	}()
+
+	l, _, err := globalLock.Call(h)
+	if l == 0 {
+		_, _, _ = closeClipboard.Call()
+		return err
+	}
+
+	r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0])))
+	if r == 0 {
+		_, _, _ = closeClipboard.Call()
+		return err
+	}
+
+	r, _, err = globalUnlock.Call(h)
+	if r == 0 {
+		if err.(syscall.Errno) != 0 {
+			_, _, _ = closeClipboard.Call()
+			return err
+		}
+	}
+
+	r, _, err = setClipboardData.Call(cfUnicodetext, h)
+	if r == 0 {
+		_, _, _ = closeClipboard.Call()
+		return err
+	}
+	h = 0 // suppress deferred cleanup
+	closed, _, err := closeClipboard.Call()
+	if closed == 0 {
+		return err
+	}
+	return nil
+}

+ 1 - 1
vendor/github.com/rivo/tview/LICENSE.txt → vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE

@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2018 Oliver Kuederle
+Copyright (c) 2022 Ayman Bagabas
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 83 - 0
vendor/github.com/aymanbagabas/go-osc52/v2/README.md

@@ -0,0 +1,83 @@
+
+# go-osc52
+
+<p>
+    <a href="https://github.com/aymanbagabas/go-osc52/releases"><img src="https://img.shields.io/github/release/aymanbagabas/go-osc52.svg" alt="Latest Release"></a>
+    <a href="https://pkg.go.dev/github.com/aymanbagabas/go-osc52/v2?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
+</p>
+
+A Go library to work with the [ANSI OSC52](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands) terminal sequence.
+
+## Usage
+
+You can use this small library to construct an ANSI OSC52 sequence suitable for
+your terminal.
+
+
+### Example
+
+```go
+import (
+  "os"
+  "fmt"
+
+  "github.com/aymanbagabas/go-osc52/v2"
+)
+
+func main() {
+  s := "Hello World!"
+
+  // Copy `s` to system clipboard
+  osc52.New(s).WriteTo(os.Stderr)
+
+  // Copy `s` to primary clipboard (X11)
+  osc52.New(s).Primary().WriteTo(os.Stderr)
+
+  // Query the clipboard
+  osc52.Query().WriteTo(os.Stderr)
+
+  // Clear system clipboard
+  osc52.Clear().WriteTo(os.Stderr)
+
+  // Use the fmt.Stringer interface to copy `s` to system clipboard
+  fmt.Fprint(os.Stderr, osc52.New(s))
+
+  // Or to primary clipboard
+  fmt.Fprint(os.Stderr, osc52.New(s).Primary())
+}
+```
+
+## SSH Example
+
+You can use this over SSH using [gliderlabs/ssh](https://github.com/gliderlabs/ssh) for instance:
+
+```go
+var sshSession ssh.Session
+seq := osc52.New("Hello awesome!")
+// Check if term is screen or tmux
+pty, _, _ := s.Pty()
+if pty.Term == "screen" {
+  seq = seq.Screen()
+} else if isTmux {
+  seq = seq.Tmux()
+}
+seq.WriteTo(sshSession.Stderr())
+```
+
+## Tmux
+
+Make sure you have `set-clipboard on` in your config, otherwise, tmux won't
+allow your application to access the clipboard [^1].
+
+Using the tmux option, `osc52.TmuxMode` or `osc52.New(...).Tmux()`, wraps the
+OSC52 sequence in a special tmux DCS sequence and pass it to the outer
+terminal. This requires `allow-passthrough on` in your config.
+`allow-passthrough` is no longer enabled by default
+[since tmux 3.3a](https://github.com/tmux/tmux/issues/3218#issuecomment-1153089282) [^2].
+
+[^1]: See [tmux clipboard](https://github.com/tmux/tmux/wiki/Clipboard)
+[^2]: [What is allow-passthrough](https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it)
+
+## Credits
+
+* [vim-oscyank](https://github.com/ojroques/vim-oscyank) this is heavily inspired by vim-oscyank.

+ 305 - 0
vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go

@@ -0,0 +1,305 @@
+// OSC52 is a terminal escape sequence that allows copying text to the clipboard.
+//
+// The sequence consists of the following:
+//
+//	OSC 52 ; Pc ; Pd BEL
+//
+// Pc is the clipboard choice:
+//
+//	c: clipboard
+//	p: primary
+//	q: secondary (not supported)
+//	s: select (not supported)
+//	0-7: cut-buffers (not supported)
+//
+// Pd is the data to copy to the clipboard. This string should be encoded in
+// base64 (RFC-4648).
+//
+// If Pd is "?", the terminal replies to the host with the current contents of
+// the clipboard.
+//
+// If Pd is neither a base64 string nor "?", the terminal clears the clipboard.
+//
+// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+// where Ps = 52 => Manipulate Selection Data.
+//
+// Examples:
+//
+//	// copy "hello world" to the system clipboard
+//	fmt.Fprint(os.Stderr, osc52.New("hello world"))
+//
+//	// copy "hello world" to the primary Clipboard
+//	fmt.Fprint(os.Stderr, osc52.New("hello world").Primary())
+//
+//	// limit the size of the string to copy 10 bytes
+//	fmt.Fprint(os.Stderr, osc52.New("0123456789").Limit(10))
+//
+//	// escape the OSC52 sequence for screen using DCS sequences
+//	fmt.Fprint(os.Stderr, osc52.New("hello world").Screen())
+//
+//	// escape the OSC52 sequence for Tmux
+//	fmt.Fprint(os.Stderr, osc52.New("hello world").Tmux())
+//
+//	// query the system Clipboard
+//	fmt.Fprint(os.Stderr, osc52.Query())
+//
+//	// query the primary clipboard
+//	fmt.Fprint(os.Stderr, osc52.Query().Primary())
+//
+//	// clear the system Clipboard
+//	fmt.Fprint(os.Stderr, osc52.Clear())
+//
+//	// clear the primary Clipboard
+//	fmt.Fprint(os.Stderr, osc52.Clear().Primary())
+package osc52
+
+import (
+	"encoding/base64"
+	"fmt"
+	"io"
+	"strings"
+)
+
+// Clipboard is the clipboard buffer to use.
+type Clipboard rune
+
+const (
+	// SystemClipboard is the system clipboard buffer.
+	SystemClipboard Clipboard = 'c'
+	// PrimaryClipboard is the primary clipboard buffer (X11).
+	PrimaryClipboard = 'p'
+)
+
+// Mode is the mode to use for the OSC52 sequence.
+type Mode uint
+
+const (
+	// DefaultMode is the default OSC52 sequence mode.
+	DefaultMode Mode = iota
+	// ScreenMode escapes the OSC52 sequence for screen using DCS sequences.
+	ScreenMode
+	// TmuxMode escapes the OSC52 sequence for tmux. Not needed if tmux
+	// clipboard is set to `set-clipboard on`
+	TmuxMode
+)
+
+// Operation is the OSC52 operation.
+type Operation uint
+
+const (
+	// SetOperation is the copy operation.
+	SetOperation Operation = iota
+	// QueryOperation is the query operation.
+	QueryOperation
+	// ClearOperation is the clear operation.
+	ClearOperation
+)
+
+// Sequence is the OSC52 sequence.
+type Sequence struct {
+	str       string
+	limit     int
+	op        Operation
+	mode      Mode
+	clipboard Clipboard
+}
+
+var _ fmt.Stringer = Sequence{}
+
+var _ io.WriterTo = Sequence{}
+
+// String returns the OSC52 sequence.
+func (s Sequence) String() string {
+	var seq strings.Builder
+	// mode escape sequences start
+	seq.WriteString(s.seqStart())
+	// actual OSC52 sequence start
+	seq.WriteString(fmt.Sprintf("\x1b]52;%c;", s.clipboard))
+	switch s.op {
+	case SetOperation:
+		str := s.str
+		if s.limit > 0 && len(str) > s.limit {
+			return ""
+		}
+		b64 := base64.StdEncoding.EncodeToString([]byte(str))
+		switch s.mode {
+		case ScreenMode:
+			// Screen doesn't support OSC52 but will pass the contents of a DCS
+			// sequence to the outer terminal unchanged.
+			//
+			// Here, we split the encoded string into 76 bytes chunks and then
+			// join the chunks with <end-dsc><start-dsc> sequences. Finally,
+			// wrap the whole thing in
+			// <start-dsc><start-osc52><joined-chunks><end-osc52><end-dsc>.
+			// s := strings.SplitN(b64, "", 76)
+			s := make([]string, 0, len(b64)/76+1)
+			for i := 0; i < len(b64); i += 76 {
+				end := i + 76
+				if end > len(b64) {
+					end = len(b64)
+				}
+				s = append(s, b64[i:end])
+			}
+			seq.WriteString(strings.Join(s, "\x1b\\\x1bP"))
+		default:
+			seq.WriteString(b64)
+		}
+	case QueryOperation:
+		// OSC52 queries the clipboard using "?"
+		seq.WriteString("?")
+	case ClearOperation:
+		// OSC52 clears the clipboard if the data is neither a base64 string nor "?"
+		// we're using "!" as a default
+		seq.WriteString("!")
+	}
+	// actual OSC52 sequence end
+	seq.WriteString("\x07")
+	// mode escape end
+	seq.WriteString(s.seqEnd())
+	return seq.String()
+}
+
+// WriteTo writes the OSC52 sequence to the writer.
+func (s Sequence) WriteTo(out io.Writer) (int64, error) {
+	n, err := out.Write([]byte(s.String()))
+	return int64(n), err
+}
+
+// Mode sets the mode for the OSC52 sequence.
+func (s Sequence) Mode(m Mode) Sequence {
+	s.mode = m
+	return s
+}
+
+// Tmux sets the mode to TmuxMode.
+// Used to escape the OSC52 sequence for `tmux`.
+//
+// Note: this is not needed if tmux clipboard is set to `set-clipboard on`. If
+// TmuxMode is used, tmux must have `allow-passthrough on` set.
+//
+// This is a syntactic sugar for s.Mode(TmuxMode).
+func (s Sequence) Tmux() Sequence {
+	return s.Mode(TmuxMode)
+}
+
+// Screen sets the mode to ScreenMode.
+// Used to escape the OSC52 sequence for `screen`.
+//
+// This is a syntactic sugar for s.Mode(ScreenMode).
+func (s Sequence) Screen() Sequence {
+	return s.Mode(ScreenMode)
+}
+
+// Clipboard sets the clipboard buffer for the OSC52 sequence.
+func (s Sequence) Clipboard(c Clipboard) Sequence {
+	s.clipboard = c
+	return s
+}
+
+// Primary sets the clipboard buffer to PrimaryClipboard.
+// This is the X11 primary clipboard.
+//
+// This is a syntactic sugar for s.Clipboard(PrimaryClipboard).
+func (s Sequence) Primary() Sequence {
+	return s.Clipboard(PrimaryClipboard)
+}
+
+// Limit sets the limit for the OSC52 sequence.
+// The default limit is 0 (no limit).
+//
+// Strings longer than the limit get ignored. Settting the limit to 0 or a
+// negative value disables the limit. Each terminal defines its own escapse
+// sequence limit.
+func (s Sequence) Limit(l int) Sequence {
+	if l < 0 {
+		s.limit = 0
+	} else {
+		s.limit = l
+	}
+	return s
+}
+
+// Operation sets the operation for the OSC52 sequence.
+// The default operation is SetOperation.
+func (s Sequence) Operation(o Operation) Sequence {
+	s.op = o
+	return s
+}
+
+// Clear sets the operation to ClearOperation.
+// This clears the clipboard.
+//
+// This is a syntactic sugar for s.Operation(ClearOperation).
+func (s Sequence) Clear() Sequence {
+	return s.Operation(ClearOperation)
+}
+
+// Query sets the operation to QueryOperation.
+// This queries the clipboard contents.
+//
+// This is a syntactic sugar for s.Operation(QueryOperation).
+func (s Sequence) Query() Sequence {
+	return s.Operation(QueryOperation)
+}
+
+// SetString sets the string for the OSC52 sequence. Strings are joined with a
+// space character.
+func (s Sequence) SetString(strs ...string) Sequence {
+	s.str = strings.Join(strs, " ")
+	return s
+}
+
+// New creates a new OSC52 sequence with the given string(s). Strings are
+// joined with a space character.
+func New(strs ...string) Sequence {
+	s := Sequence{
+		str:       strings.Join(strs, " "),
+		limit:     0,
+		mode:      DefaultMode,
+		clipboard: SystemClipboard,
+		op:        SetOperation,
+	}
+	return s
+}
+
+// Query creates a new OSC52 sequence with the QueryOperation.
+// This returns a new OSC52 sequence to query the clipboard contents.
+//
+// This is a syntactic sugar for New().Query().
+func Query() Sequence {
+	return New().Query()
+}
+
+// Clear creates a new OSC52 sequence with the ClearOperation.
+// This returns a new OSC52 sequence to clear the clipboard.
+//
+// This is a syntactic sugar for New().Clear().
+func Clear() Sequence {
+	return New().Clear()
+}
+
+func (s Sequence) seqStart() string {
+	switch s.mode {
+	case TmuxMode:
+		// Write the start of a tmux escape sequence.
+		return "\x1bPtmux;\x1b"
+	case ScreenMode:
+		// Write the start of a DCS sequence.
+		return "\x1bP"
+	default:
+		return ""
+	}
+}
+
+func (s Sequence) seqEnd() string {
+	switch s.mode {
+	case TmuxMode:
+		// Terminate the tmux escape sequence.
+		return "\x1b\\"
+	case ScreenMode:
+		// Write the end of a DCS sequence.
+		return "\x1b\x5c"
+	default:
+		return ""
+	}
+}

+ 2 - 2
vendor/github.com/epiclabs-io/winman/LICENSE.txt → vendor/github.com/charmbracelet/bubbles/LICENSE

@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2018 Javier Peletier - Epic Labs, S.L.
+Copyright (c) 2020 Charmbracelet, Inc
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+SOFTWARE.

+ 207 - 0
vendor/github.com/charmbracelet/bubbles/cursor/cursor.go

@@ -0,0 +1,207 @@
+package cursor
+
+import (
+	"context"
+	"time"
+
+	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/lipgloss"
+)
+
+const defaultBlinkSpeed = time.Millisecond * 530
+
+// initialBlinkMsg initializes cursor blinking.
+type initialBlinkMsg struct{}
+
+// BlinkMsg signals that the cursor should blink. It contains metadata that
+// allows us to tell if the blink message is the one we're expecting.
+type BlinkMsg struct {
+	id  int
+	tag int
+}
+
+// blinkCanceled is sent when a blink operation is canceled.
+type blinkCanceled struct{}
+
+// blinkCtx manages cursor blinking.
+type blinkCtx struct {
+	ctx    context.Context
+	cancel context.CancelFunc
+}
+
+// Mode describes the behavior of the cursor.
+type Mode int
+
+// Available cursor modes.
+const (
+	CursorBlink Mode = iota
+	CursorStatic
+	CursorHide
+)
+
+// String returns the cursor mode in a human-readable format. This method is
+// provisional and for informational purposes only.
+func (c Mode) String() string {
+	return [...]string{
+		"blink",
+		"static",
+		"hidden",
+	}[c]
+}
+
+// Model is the Bubble Tea model for this cursor element.
+type Model struct {
+	BlinkSpeed time.Duration
+	// Style for styling the cursor block.
+	Style lipgloss.Style
+	// TextStyle is the style used for the cursor when it is hidden (when blinking).
+	// I.e. displaying normal text.
+	TextStyle lipgloss.Style
+
+	// char is the character under the cursor
+	char string
+	// The ID of this Model as it relates to other cursors
+	id int
+	// focus indicates whether the containing input is focused
+	focus bool
+	// Cursor Blink state.
+	Blink bool
+	// Used to manage cursor blink
+	blinkCtx *blinkCtx
+	// The ID of the blink message we're expecting to receive.
+	blinkTag int
+	// mode determines the behavior of the cursor
+	mode Mode
+}
+
+// New creates a new model with default settings.
+func New() Model {
+	return Model{
+		BlinkSpeed: defaultBlinkSpeed,
+
+		Blink: true,
+		mode:  CursorBlink,
+
+		blinkCtx: &blinkCtx{
+			ctx: context.Background(),
+		},
+	}
+}
+
+// Update updates the cursor.
+func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
+	switch msg := msg.(type) {
+	case initialBlinkMsg:
+		// We accept all initialBlinkMsgs generated by the Blink command.
+
+		if m.mode != CursorBlink || !m.focus {
+			return m, nil
+		}
+
+		cmd := m.BlinkCmd()
+		return m, cmd
+
+	case BlinkMsg:
+		// We're choosy about whether to accept blinkMsgs so that our cursor
+		// only exactly when it should.
+
+		// Is this model blink-able?
+		if m.mode != CursorBlink || !m.focus {
+			return m, nil
+		}
+
+		// Were we expecting this blink message?
+		if msg.id != m.id || msg.tag != m.blinkTag {
+			return m, nil
+		}
+
+		var cmd tea.Cmd
+		if m.mode == CursorBlink {
+			m.Blink = !m.Blink
+			cmd = m.BlinkCmd()
+		}
+		return m, cmd
+
+	case blinkCanceled: // no-op
+		return m, nil
+	}
+	return m, nil
+}
+
+// Mode returns the model's cursor mode. For available cursor modes, see
+// type Mode.
+func (m Model) Mode() Mode {
+	return m.mode
+}
+
+// SetMode sets the model's cursor mode. This method returns a command.
+//
+// For available cursor modes, see type CursorMode.
+func (m *Model) SetMode(mode Mode) tea.Cmd {
+	m.mode = mode
+	m.Blink = m.mode == CursorHide || !m.focus
+	if mode == CursorBlink {
+		return Blink
+	}
+	return nil
+}
+
+// BlinkCmd is an command used to manage cursor blinking.
+func (m *Model) BlinkCmd() tea.Cmd {
+	if m.mode != CursorBlink {
+		return nil
+	}
+
+	if m.blinkCtx != nil && m.blinkCtx.cancel != nil {
+		m.blinkCtx.cancel()
+	}
+
+	ctx, cancel := context.WithTimeout(m.blinkCtx.ctx, m.BlinkSpeed)
+	m.blinkCtx.cancel = cancel
+
+	m.blinkTag++
+
+	return func() tea.Msg {
+		defer cancel()
+		<-ctx.Done()
+		if ctx.Err() == context.DeadlineExceeded {
+			return BlinkMsg{id: m.id, tag: m.blinkTag}
+		}
+		return blinkCanceled{}
+	}
+}
+
+// Blink is a command used to initialize cursor blinking.
+func Blink() tea.Msg {
+	return initialBlinkMsg{}
+}
+
+// Focus focuses the cursor to allow it to blink if desired.
+func (m *Model) Focus() tea.Cmd {
+	m.focus = true
+	m.Blink = m.mode == CursorHide // show the cursor unless we've explicitly hidden it
+
+	if m.mode == CursorBlink && m.focus {
+		return m.BlinkCmd()
+	}
+	return nil
+}
+
+// Blur blurs the cursor.
+func (m *Model) Blur() {
+	m.focus = false
+	m.Blink = true
+}
+
+// SetChar sets the character under the cursor.
+func (m *Model) SetChar(char string) {
+	m.char = char
+}
+
+// View displays the cursor.
+func (m Model) View() string {
+	if m.Blink {
+		return m.TextStyle.Inline(true).Render(m.char)
+	}
+	return m.Style.Inline(true).Reverse(true).Render(m.char)
+}

+ 142 - 0
vendor/github.com/charmbracelet/bubbles/key/key.go

@@ -0,0 +1,142 @@
+// Package key provides some types and functions for generating user-definable
+// keymappings useful in Bubble Tea components. There are a few different ways
+// you can define a keymapping with this package. Here's one example:
+//
+//	type KeyMap struct {
+//	    Up key.Binding
+//	    Down key.Binding
+//	}
+//
+//	var DefaultKeyMap = KeyMap{
+//	    Up: key.NewBinding(
+//	        key.WithKeys("k", "up"),        // actual keybindings
+//	        key.WithHelp("↑/k", "move up"), // corresponding help text
+//	    ),
+//	    Down: key.NewBinding(
+//	        key.WithKeys("j", "down"),
+//	        key.WithHelp("↓/j", "move down"),
+//	    ),
+//	}
+//
+//	func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+//	    switch msg := msg.(type) {
+//	    case tea.KeyMsg:
+//	        switch {
+//	        case key.Matches(msg, DefaultKeyMap.Up):
+//	            // The user pressed up
+//	        case key.Matches(msg, DefaultKeyMap.Down):
+//	            // The user pressed down
+//	        }
+//	    }
+//
+//	    // ...
+//	}
+//
+// The help information, which is not used in the example above, can be used
+// to render help text for keystrokes in your views.
+package key
+
+import (
+	tea "github.com/charmbracelet/bubbletea"
+)
+
+// Binding describes a set of keybindings and, optionally, their associated
+// help text.
+type Binding struct {
+	keys     []string
+	help     Help
+	disabled bool
+}
+
+// BindingOpt is an initialization option for a keybinding. It's used as an
+// argument to NewBinding.
+type BindingOpt func(*Binding)
+
+// NewBinding returns a new keybinding from a set of BindingOpt options.
+func NewBinding(opts ...BindingOpt) Binding {
+	b := &Binding{}
+	for _, opt := range opts {
+		opt(b)
+	}
+	return *b
+}
+
+// WithKeys initializes a keybinding with the given keystrokes.
+func WithKeys(keys ...string) BindingOpt {
+	return func(b *Binding) {
+		b.keys = keys
+	}
+}
+
+// WithHelp initializes a keybinding with the given help text.
+func WithHelp(key, desc string) BindingOpt {
+	return func(b *Binding) {
+		b.help = Help{Key: key, Desc: desc}
+	}
+}
+
+// WithDisabled initializes a disabled keybinding.
+func WithDisabled() BindingOpt {
+	return func(b *Binding) {
+		b.disabled = true
+	}
+}
+
+// SetKeys sets the keys for the keybinding.
+func (b *Binding) SetKeys(keys ...string) {
+	b.keys = keys
+}
+
+// Keys returns the keys for the keybinding.
+func (b Binding) Keys() []string {
+	return b.keys
+}
+
+// SetHelp sets the help text for the keybinding.
+func (b *Binding) SetHelp(key, desc string) {
+	b.help = Help{Key: key, Desc: desc}
+}
+
+// Help returns the Help information for the keybinding.
+func (b Binding) Help() Help {
+	return b.help
+}
+
+// Enabled returns whether or not the keybinding is enabled. Disabled
+// keybindings won't be activated and won't show up in help. Keybindings are
+// enabled by default.
+func (b Binding) Enabled() bool {
+	return !b.disabled && b.keys != nil
+}
+
+// SetEnabled enables or disables the keybinding.
+func (b *Binding) SetEnabled(v bool) {
+	b.disabled = !v
+}
+
+// Unbind removes the keys and help from this binding, effectively nullifying
+// it. This is a step beyond disabling it, since applications can enable
+// or disable key bindings based on application state.
+func (b *Binding) Unbind() {
+	b.keys = nil
+	b.help = Help{}
+}
+
+// Help is help information for a given keybinding.
+type Help struct {
+	Key  string
+	Desc string
+}
+
+// Matches checks if the given KeyMsg matches the given bindings.
+func Matches(k tea.KeyMsg, b ...Binding) bool {
+	keys := k.String()
+	for _, binding := range b {
+		for _, v := range binding.keys {
+			if keys == v && binding.Enabled() {
+				return true
+			}
+		}
+	}
+	return false
+}

+ 102 - 0
vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go

@@ -0,0 +1,102 @@
+// Package runeutil provides a utility function for use in Bubbles
+// that can process Key messages containing runes.
+package runeutil
+
+import (
+	"unicode"
+	"unicode/utf8"
+)
+
+// Sanitizer is a helper for bubble widgets that want to process
+// Runes from input key messages.
+type Sanitizer interface {
+	// Sanitize removes control characters from runes in a KeyRunes
+	// message, and optionally replaces newline/carriage return/tabs by a
+	// specified character.
+	//
+	// The rune array is modified in-place if possible. In that case, the
+	// returned slice is the original slice shortened after the control
+	// characters have been removed/translated.
+	Sanitize(runes []rune) []rune
+}
+
+// NewSanitizer constructs a rune sanitizer.
+func NewSanitizer(opts ...Option) Sanitizer {
+	s := sanitizer{
+		replaceNewLine: []rune("\n"),
+		replaceTab:     []rune("    "),
+	}
+	for _, o := range opts {
+		s = o(s)
+	}
+	return &s
+}
+
+// Option is the type of an option that can be passed to Sanitize().
+type Option func(sanitizer) sanitizer
+
+// ReplaceTabs replaces tabs by the specified string.
+func ReplaceTabs(tabRepl string) Option {
+	return func(s sanitizer) sanitizer {
+		s.replaceTab = []rune(tabRepl)
+		return s
+	}
+}
+
+// ReplaceNewlines replaces newline characters by the specified string.
+func ReplaceNewlines(nlRepl string) Option {
+	return func(s sanitizer) sanitizer {
+		s.replaceNewLine = []rune(nlRepl)
+		return s
+	}
+}
+
+func (s *sanitizer) Sanitize(runes []rune) []rune {
+	// dstrunes are where we are storing the result.
+	dstrunes := runes[:0:len(runes)]
+	// copied indicates whether dstrunes is an alias of runes
+	// or a copy. We need a copy when dst moves past src.
+	// We use this as an optimization to avoid allocating
+	// a new rune slice in the common case where the output
+	// is smaller or equal to the input.
+	copied := false
+
+	for src := 0; src < len(runes); src++ {
+		r := runes[src]
+		switch {
+		case r == utf8.RuneError:
+			// skip
+
+		case r == '\r' || r == '\n':
+			if len(dstrunes)+len(s.replaceNewLine) > src && !copied {
+				dst := len(dstrunes)
+				dstrunes = make([]rune, dst, len(runes)+len(s.replaceNewLine))
+				copy(dstrunes, runes[:dst])
+				copied = true
+			}
+			dstrunes = append(dstrunes, s.replaceNewLine...)
+
+		case r == '\t':
+			if len(dstrunes)+len(s.replaceTab) > src && !copied {
+				dst := len(dstrunes)
+				dstrunes = make([]rune, dst, len(runes)+len(s.replaceTab))
+				copy(dstrunes, runes[:dst])
+				copied = true
+			}
+			dstrunes = append(dstrunes, s.replaceTab...)
+
+		case unicode.IsControl(r):
+			// Other control characters: skip.
+
+		default:
+			// Keep the character.
+			dstrunes = append(dstrunes, runes[src])
+		}
+	}
+	return dstrunes
+}
+
+type sanitizer struct {
+	replaceNewLine []rune
+	replaceTab     []rune
+}

+ 721 - 0
vendor/github.com/charmbracelet/bubbles/textinput/textinput.go

@@ -0,0 +1,721 @@
+package textinput
+
+import (
+	"strings"
+	"time"
+	"unicode"
+
+	"github.com/atotto/clipboard"
+	"github.com/charmbracelet/bubbles/cursor"
+	"github.com/charmbracelet/bubbles/key"
+	"github.com/charmbracelet/bubbles/runeutil"
+	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/lipgloss"
+	rw "github.com/mattn/go-runewidth"
+)
+
+// Internal messages for clipboard operations.
+type pasteMsg string
+type pasteErrMsg struct{ error }
+
+// EchoMode sets the input behavior of the text input field.
+type EchoMode int
+
+const (
+	// EchoNormal displays text as is. This is the default behavior.
+	EchoNormal EchoMode = iota
+
+	// EchoPassword displays the EchoCharacter mask instead of actual
+	// characters.  This is commonly used for password fields.
+	EchoPassword
+
+	// EchoNone displays nothing as characters are entered. This is commonly
+	// seen for password fields on the command line.
+	EchoNone
+
+	// EchoOnEdit.
+)
+
+// ValidateFunc is a function that returns an error if the input is invalid.
+type ValidateFunc func(string) error
+
+// KeyMap is the key bindings for different actions within the textinput.
+type KeyMap struct {
+	CharacterForward        key.Binding
+	CharacterBackward       key.Binding
+	WordForward             key.Binding
+	WordBackward            key.Binding
+	DeleteWordBackward      key.Binding
+	DeleteWordForward       key.Binding
+	DeleteAfterCursor       key.Binding
+	DeleteBeforeCursor      key.Binding
+	DeleteCharacterBackward key.Binding
+	DeleteCharacterForward  key.Binding
+	LineStart               key.Binding
+	LineEnd                 key.Binding
+	Paste                   key.Binding
+}
+
+// DefaultKeyMap is the default set of key bindings for navigating and acting
+// upon the textinput.
+var DefaultKeyMap = KeyMap{
+	CharacterForward:        key.NewBinding(key.WithKeys("right", "ctrl+f")),
+	CharacterBackward:       key.NewBinding(key.WithKeys("left", "ctrl+b")),
+	WordForward:             key.NewBinding(key.WithKeys("alt+right", "alt+f")),
+	WordBackward:            key.NewBinding(key.WithKeys("alt+left", "alt+b")),
+	DeleteWordBackward:      key.NewBinding(key.WithKeys("alt+backspace", "ctrl+w")),
+	DeleteWordForward:       key.NewBinding(key.WithKeys("alte+delete", "alt+d")),
+	DeleteAfterCursor:       key.NewBinding(key.WithKeys("ctrl+k")),
+	DeleteBeforeCursor:      key.NewBinding(key.WithKeys("ctrl+u")),
+	DeleteCharacterBackward: key.NewBinding(key.WithKeys("backspace", "ctrl+h")),
+	DeleteCharacterForward:  key.NewBinding(key.WithKeys("delete", "ctrl+d")),
+	LineStart:               key.NewBinding(key.WithKeys("home", "ctrl+a")),
+	LineEnd:                 key.NewBinding(key.WithKeys("end", "ctrl+e")),
+	Paste:                   key.NewBinding(key.WithKeys("ctrl+v")),
+}
+
+// Model is the Bubble Tea model for this text input element.
+type Model struct {
+	Err error
+
+	// General settings.
+	Prompt        string
+	Placeholder   string
+	EchoMode      EchoMode
+	EchoCharacter rune
+	Cursor        cursor.Model
+
+	// Deprecated: use [cursor.BlinkSpeed] instead.
+	BlinkSpeed time.Duration
+
+	// Styles. These will be applied as inline styles.
+	//
+	// For an introduction to styling with Lip Gloss see:
+	// https://github.com/charmbracelet/lipgloss
+	PromptStyle      lipgloss.Style
+	TextStyle        lipgloss.Style
+	BackgroundStyle  lipgloss.Style
+	PlaceholderStyle lipgloss.Style
+	CursorStyle      lipgloss.Style
+
+	// CharLimit is the maximum amount of characters this input element will
+	// accept. If 0 or less, there's no limit.
+	CharLimit int
+
+	// Width is the maximum number of characters that can be displayed at once.
+	// It essentially treats the text field like a horizontally scrolling
+	// viewport. If 0 or less this setting is ignored.
+	Width int
+
+	// KeyMap encodes the keybindings recognized by the widget.
+	KeyMap KeyMap
+
+	// Underlying text value.
+	value []rune
+
+	// focus indicates whether user input focus should be on this input
+	// component. When false, ignore keyboard input and hide the cursor.
+	focus bool
+
+	// Cursor position.
+	pos int
+
+	// Used to emulate a viewport when width is set and the content is
+	// overflowing.
+	offset      int
+	offsetRight int
+
+	// Validate is a function that checks whether or not the text within the
+	// input is valid. If it is not valid, the `Err` field will be set to the
+	// error returned by the function. If the function is not defined, all
+	// input is considered valid.
+	Validate ValidateFunc
+
+	// rune sanitizer for input.
+	rsan runeutil.Sanitizer
+}
+
+// New creates a new model with default settings.
+func New() Model {
+	return Model{
+		Prompt:           "> ",
+		EchoCharacter:    '*',
+		CharLimit:        0,
+		PlaceholderStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("240")),
+		Cursor:           cursor.New(),
+		KeyMap:           DefaultKeyMap,
+
+		value: nil,
+		focus: false,
+		pos:   0,
+	}
+}
+
+// NewModel creates a new model with default settings.
+//
+// Deprecated: Use [New] instead.
+var NewModel = New
+
+// SetValue sets the value of the text input.
+func (m *Model) SetValue(s string) {
+	// Clean up any special characters in the input provided by the
+	// caller. This avoids bugs due to e.g. tab characters and whatnot.
+	runes := m.san().Sanitize([]rune(s))
+	m.setValueInternal(runes)
+}
+
+func (m *Model) setValueInternal(runes []rune) {
+	if m.Validate != nil {
+		if err := m.Validate(string(runes)); err != nil {
+			m.Err = err
+			return
+		}
+	}
+
+	empty := len(m.value) == 0
+	m.Err = nil
+
+	if m.CharLimit > 0 && len(runes) > m.CharLimit {
+		m.value = runes[:m.CharLimit]
+	} else {
+		m.value = runes
+	}
+	if (m.pos == 0 && empty) || m.pos > len(m.value) {
+		m.SetCursor(len(m.value))
+	}
+	m.handleOverflow()
+}
+
+// Value returns the value of the text input.
+func (m Model) Value() string {
+	return string(m.value)
+}
+
+// Position returns the cursor position.
+func (m Model) Position() int {
+	return m.pos
+}
+
+// SetCursor moves the cursor to the given position. If the position is
+// out of bounds the cursor will be moved to the start or end accordingly.
+func (m *Model) SetCursor(pos int) {
+	m.pos = clamp(pos, 0, len(m.value))
+	m.handleOverflow()
+}
+
+// CursorStart moves the cursor to the start of the input field.
+func (m *Model) CursorStart() {
+	m.SetCursor(0)
+}
+
+// CursorEnd moves the cursor to the end of the input field.
+func (m *Model) CursorEnd() {
+	m.SetCursor(len(m.value))
+}
+
+// Focused returns the focus state on the model.
+func (m Model) Focused() bool {
+	return m.focus
+}
+
+// Focus sets the focus state on the model. When the model is in focus it can
+// receive keyboard input and the cursor will be shown.
+func (m *Model) Focus() tea.Cmd {
+	m.focus = true
+	return m.Cursor.Focus()
+}
+
+// Blur removes the focus state on the model.  When the model is blurred it can
+// not receive keyboard input and the cursor will be hidden.
+func (m *Model) Blur() {
+	m.focus = false
+	m.Cursor.Blur()
+}
+
+// Reset sets the input to its default state with no input.
+func (m *Model) Reset() {
+	m.value = nil
+	m.SetCursor(0)
+}
+
+// rsan initializes or retrieves the rune sanitizer.
+func (m *Model) san() runeutil.Sanitizer {
+	if m.rsan == nil {
+		// Textinput has all its input on a single line so collapse
+		// newlines/tabs to single spaces.
+		m.rsan = runeutil.NewSanitizer(
+			runeutil.ReplaceTabs(" "), runeutil.ReplaceNewlines(" "))
+	}
+	return m.rsan
+}
+
+func (m *Model) insertRunesFromUserInput(v []rune) {
+	// Clean up any special characters in the input provided by the
+	// clipboard. This avoids bugs due to e.g. tab characters and
+	// whatnot.
+	paste := m.san().Sanitize(v)
+
+	var availSpace int
+	if m.CharLimit > 0 {
+		availSpace = m.CharLimit - len(m.value)
+
+		// If the char limit's been reached, cancel.
+		if availSpace <= 0 {
+			return
+		}
+
+		// If there's not enough space to paste the whole thing cut the pasted
+		// runes down so they'll fit.
+		if availSpace < len(paste) {
+			paste = paste[:len(paste)-availSpace]
+		}
+	}
+
+	// Stuff before and after the cursor
+	head := m.value[:m.pos]
+	tailSrc := m.value[m.pos:]
+	tail := make([]rune, len(tailSrc))
+	copy(tail, tailSrc)
+
+	oldPos := m.pos
+
+	// Insert pasted runes
+	for _, r := range paste {
+		head = append(head, r)
+		m.pos++
+		if m.CharLimit > 0 {
+			availSpace--
+			if availSpace <= 0 {
+				break
+			}
+		}
+	}
+
+	// Put it all back together
+	value := append(head, tail...)
+	m.setValueInternal(value)
+
+	if m.Err != nil {
+		m.pos = oldPos
+	}
+}
+
+// If a max width is defined, perform some logic to treat the visible area
+// as a horizontally scrolling viewport.
+func (m *Model) handleOverflow() {
+	if m.Width <= 0 || rw.StringWidth(string(m.value)) <= m.Width {
+		m.offset = 0
+		m.offsetRight = len(m.value)
+		return
+	}
+
+	// Correct right offset if we've deleted characters
+	m.offsetRight = min(m.offsetRight, len(m.value))
+
+	if m.pos < m.offset {
+		m.offset = m.pos
+
+		w := 0
+		i := 0
+		runes := m.value[m.offset:]
+
+		for i < len(runes) && w <= m.Width {
+			w += rw.RuneWidth(runes[i])
+			if w <= m.Width+1 {
+				i++
+			}
+		}
+
+		m.offsetRight = m.offset + i
+	} else if m.pos >= m.offsetRight {
+		m.offsetRight = m.pos
+
+		w := 0
+		runes := m.value[:m.offsetRight]
+		i := len(runes) - 1
+
+		for i > 0 && w < m.Width {
+			w += rw.RuneWidth(runes[i])
+			if w <= m.Width {
+				i--
+			}
+		}
+
+		m.offset = m.offsetRight - (len(runes) - 1 - i)
+	}
+}
+
+// deleteBeforeCursor deletes all text before the cursor.
+func (m *Model) deleteBeforeCursor() {
+	m.value = m.value[m.pos:]
+	m.offset = 0
+	m.SetCursor(0)
+}
+
+// deleteAfterCursor deletes all text after the cursor. If input is masked
+// delete everything after the cursor so as not to reveal word breaks in the
+// masked input.
+func (m *Model) deleteAfterCursor() {
+	m.value = m.value[:m.pos]
+	m.SetCursor(len(m.value))
+}
+
+// deleteWordBackward deletes the word left to the cursor.
+func (m *Model) deleteWordBackward() {
+	if m.pos == 0 || len(m.value) == 0 {
+		return
+	}
+
+	if m.EchoMode != EchoNormal {
+		m.deleteBeforeCursor()
+		return
+	}
+
+	// Linter note: it's critical that we acquire the initial cursor position
+	// here prior to altering it via SetCursor() below. As such, moving this
+	// call into the corresponding if clause does not apply here.
+	oldPos := m.pos //nolint:ifshort
+
+	m.SetCursor(m.pos - 1)
+	for unicode.IsSpace(m.value[m.pos]) {
+		if m.pos <= 0 {
+			break
+		}
+		// ignore series of whitespace before cursor
+		m.SetCursor(m.pos - 1)
+	}
+
+	for m.pos > 0 {
+		if !unicode.IsSpace(m.value[m.pos]) {
+			m.SetCursor(m.pos - 1)
+		} else {
+			if m.pos > 0 {
+				// keep the previous space
+				m.SetCursor(m.pos + 1)
+			}
+			break
+		}
+	}
+
+	if oldPos > len(m.value) {
+		m.value = m.value[:m.pos]
+	} else {
+		m.value = append(m.value[:m.pos], m.value[oldPos:]...)
+	}
+}
+
+// deleteWordForward deletes the word right to the cursor If input is masked
+// delete everything after the cursor so as not to reveal word breaks in the
+// masked input.
+func (m *Model) deleteWordForward() {
+	if m.pos >= len(m.value) || len(m.value) == 0 {
+		return
+	}
+
+	if m.EchoMode != EchoNormal {
+		m.deleteAfterCursor()
+		return
+	}
+
+	oldPos := m.pos
+	m.SetCursor(m.pos + 1)
+	for unicode.IsSpace(m.value[m.pos]) {
+		// ignore series of whitespace after cursor
+		m.SetCursor(m.pos + 1)
+
+		if m.pos >= len(m.value) {
+			break
+		}
+	}
+
+	for m.pos < len(m.value) {
+		if !unicode.IsSpace(m.value[m.pos]) {
+			m.SetCursor(m.pos + 1)
+		} else {
+			break
+		}
+	}
+
+	if m.pos > len(m.value) {
+		m.value = m.value[:oldPos]
+	} else {
+		m.value = append(m.value[:oldPos], m.value[m.pos:]...)
+	}
+
+	m.SetCursor(oldPos)
+}
+
+// wordBackward moves the cursor one word to the left. If input is masked, move
+// input to the start so as not to reveal word breaks in the masked input.
+func (m *Model) wordBackward() {
+	if m.pos == 0 || len(m.value) == 0 {
+		return
+	}
+
+	if m.EchoMode != EchoNormal {
+		m.CursorStart()
+		return
+	}
+
+	i := m.pos - 1
+	for i >= 0 {
+		if unicode.IsSpace(m.value[i]) {
+			m.SetCursor(m.pos - 1)
+			i--
+		} else {
+			break
+		}
+	}
+
+	for i >= 0 {
+		if !unicode.IsSpace(m.value[i]) {
+			m.SetCursor(m.pos - 1)
+			i--
+		} else {
+			break
+		}
+	}
+}
+
+// wordForward moves the cursor one word to the right. If the input is masked,
+// move input to the end so as not to reveal word breaks in the masked input.
+func (m *Model) wordForward() {
+	if m.pos >= len(m.value) || len(m.value) == 0 {
+		return
+	}
+
+	if m.EchoMode != EchoNormal {
+		m.CursorEnd()
+		return
+	}
+
+	i := m.pos
+	for i < len(m.value) {
+		if unicode.IsSpace(m.value[i]) {
+			m.SetCursor(m.pos + 1)
+			i++
+		} else {
+			break
+		}
+	}
+
+	for i < len(m.value) {
+		if !unicode.IsSpace(m.value[i]) {
+			m.SetCursor(m.pos + 1)
+			i++
+		} else {
+			break
+		}
+	}
+}
+
+func (m Model) echoTransform(v string) string {
+	switch m.EchoMode {
+	case EchoPassword:
+		return strings.Repeat(string(m.EchoCharacter), rw.StringWidth(v))
+	case EchoNone:
+		return ""
+
+	default:
+		return v
+	}
+}
+
+// Update is the Bubble Tea update loop.
+func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
+	if !m.focus {
+		return m, nil
+	}
+
+	// Let's remember where the position of the cursor currently is so that if
+	// the cursor position changes, we can reset the blink.
+	oldPos := m.pos //nolint
+
+	switch msg := msg.(type) {
+	case tea.KeyMsg:
+		switch {
+		case key.Matches(msg, m.KeyMap.DeleteWordBackward):
+			m.Err = nil
+			m.deleteWordBackward()
+		case key.Matches(msg, m.KeyMap.DeleteCharacterBackward):
+			m.Err = nil
+			if len(m.value) > 0 {
+				m.value = append(m.value[:max(0, m.pos-1)], m.value[m.pos:]...)
+				if m.pos > 0 {
+					m.SetCursor(m.pos - 1)
+				}
+			}
+		case key.Matches(msg, m.KeyMap.WordBackward):
+			m.wordBackward()
+		case key.Matches(msg, m.KeyMap.CharacterBackward):
+			if m.pos > 0 {
+				m.SetCursor(m.pos - 1)
+			}
+		case key.Matches(msg, m.KeyMap.WordForward):
+			m.wordForward()
+		case key.Matches(msg, m.KeyMap.CharacterForward):
+			if m.pos < len(m.value) {
+				m.SetCursor(m.pos + 1)
+			}
+		case key.Matches(msg, m.KeyMap.DeleteWordBackward):
+			m.deleteWordBackward()
+		case key.Matches(msg, m.KeyMap.LineStart):
+			m.CursorStart()
+		case key.Matches(msg, m.KeyMap.DeleteCharacterForward):
+			if len(m.value) > 0 && m.pos < len(m.value) {
+				m.value = append(m.value[:m.pos], m.value[m.pos+1:]...)
+			}
+		case key.Matches(msg, m.KeyMap.LineEnd):
+			m.CursorEnd()
+		case key.Matches(msg, m.KeyMap.DeleteAfterCursor):
+			m.deleteAfterCursor()
+		case key.Matches(msg, m.KeyMap.DeleteBeforeCursor):
+			m.deleteBeforeCursor()
+		case key.Matches(msg, m.KeyMap.Paste):
+			return m, Paste
+		case key.Matches(msg, m.KeyMap.DeleteWordForward):
+			m.deleteWordForward()
+		default:
+			// Input one or more regular characters.
+			m.insertRunesFromUserInput(msg.Runes)
+		}
+
+	case pasteMsg:
+		m.insertRunesFromUserInput([]rune(msg))
+
+	case pasteErrMsg:
+		m.Err = msg
+	}
+
+	var cmds []tea.Cmd
+	var cmd tea.Cmd
+
+	m.Cursor, cmd = m.Cursor.Update(msg)
+	cmds = append(cmds, cmd)
+
+	if oldPos != m.pos {
+		m.Cursor.Blink = false
+		cmds = append(cmds, m.Cursor.BlinkCmd())
+	}
+
+	m.handleOverflow()
+	return m, tea.Batch(cmds...)
+}
+
+// View renders the textinput in its current state.
+func (m Model) View() string {
+	// Placeholder text
+	if len(m.value) == 0 && m.Placeholder != "" {
+		return m.placeholderView()
+	}
+
+	styleText := m.TextStyle.Inline(true).Render
+
+	value := m.value[m.offset:m.offsetRight]
+	pos := max(0, m.pos-m.offset)
+	v := styleText(m.echoTransform(string(value[:pos])))
+
+	if pos < len(value) {
+		char := m.echoTransform(string(value[pos]))
+		m.Cursor.SetChar(char)
+		v += m.Cursor.View()                                   // cursor and text under it
+		v += styleText(m.echoTransform(string(value[pos+1:]))) // text after cursor
+	} else {
+		m.Cursor.SetChar(" ")
+		v += m.Cursor.View()
+	}
+
+	// If a max width and background color were set fill the empty spaces with
+	// the background color.
+	valWidth := rw.StringWidth(string(value))
+	if m.Width > 0 && valWidth <= m.Width {
+		padding := max(0, m.Width-valWidth)
+		if valWidth+padding <= m.Width && pos < len(value) {
+			padding++
+		}
+		v += styleText(strings.Repeat(" ", padding))
+	}
+
+	return m.PromptStyle.Render(m.Prompt) + v
+}
+
+// placeholderView returns the prompt and placeholder view, if any.
+func (m Model) placeholderView() string {
+	var (
+		v     string
+		p     = m.Placeholder
+		style = m.PlaceholderStyle.Inline(true).Render
+	)
+
+	m.Cursor.TextStyle = m.PlaceholderStyle
+	m.Cursor.SetChar(p[:1])
+	v += m.Cursor.View()
+
+	// The rest of the placeholder text
+	v += style(p[1:])
+
+	return m.PromptStyle.Render(m.Prompt) + v
+}
+
+// Blink is a command used to initialize cursor blinking.
+func Blink() tea.Msg {
+	return cursor.Blink()
+}
+
+// Paste is a command for pasting from the clipboard into the text input.
+func Paste() tea.Msg {
+	str, err := clipboard.ReadAll()
+	if err != nil {
+		return pasteErrMsg{err}
+	}
+	return pasteMsg(str)
+}
+
+func clamp(v, low, high int) int {
+	if high < low {
+		low, high = high, low
+	}
+	return min(high, max(low, v))
+}
+
+func min(a, b int) int {
+	if a < b {
+		return a
+	}
+	return b
+}
+
+func max(a, b int) int {
+	if a > b {
+		return a
+	}
+	return b
+}
+
+// Deprecated.
+
+// Deprecated: use cursor.Mode.
+type CursorMode int
+
+const (
+	// Deprecated: use cursor.CursorBlink.
+	CursorBlink = CursorMode(cursor.CursorBlink)
+	// Deprecated: use cursor.CursorStatic.
+	CursorStatic = CursorMode(cursor.CursorStatic)
+	// Deprecated: use cursor.CursorHide.
+	CursorHide = CursorMode(cursor.CursorHide)
+)
+
+func (c CursorMode) String() string {
+	return cursor.Mode(c).String()
+}
+
+// Deprecated: use cursor.Mode().
+func (m Model) CursorMode() CursorMode {
+	return CursorMode(m.Cursor.Mode())
+}
+
+// Deprecated: use cursor.SetMode().
+func (m *Model) SetCursorMode(mode CursorMode) tea.Cmd {
+	return m.Cursor.SetMode(cursor.Mode(mode))
+}

+ 22 - 0
vendor/github.com/charmbracelet/bubbletea/.gitignore

@@ -0,0 +1,22 @@
+.DS_Store
+.envrc
+
+examples/fullscreen/fullscreen
+examples/help/help
+examples/http/http
+examples/list-default/list-default
+examples/list-fancy/list-fancy
+examples/list-simple/list-simple
+examples/mouse/mouse
+examples/pager/pager
+examples/progress-download/color_vortex.blend
+examples/progress-download/progress-download
+examples/simple/simple
+examples/spinner/spinner
+examples/textinput/textinput
+examples/textinputs/textinputs
+examples/views/views
+tutorials/basics/basics
+tutorials/commands/commands
+.idea
+coverage.txt

+ 47 - 0
vendor/github.com/charmbracelet/bubbletea/.golangci-soft.yml

@@ -0,0 +1,47 @@
+run:
+  tests: false
+
+issues:
+  include:
+    - EXC0001
+    - EXC0005
+    - EXC0011
+    - EXC0012
+    - EXC0013
+
+  max-issues-per-linter: 0
+  max-same-issues: 0
+
+linters:
+  enable:
+    # - dupl
+    - exhaustive
+    # - exhaustivestruct
+    - goconst
+    - godot
+    - godox
+    - gomnd
+    - gomoddirectives
+    - goprintffuncname
+    - ifshort
+    # - lll
+    - misspell
+    - nakedret
+    - nestif
+    - noctx
+    - nolintlint
+    - prealloc
+    - wrapcheck
+
+  # disable default linters, they are already enabled in .golangci.yml
+  disable:
+    - deadcode
+    - errcheck
+    - gosimple
+    - govet
+    - ineffassign
+    - staticcheck
+    - structcheck
+    - typecheck
+    - unused
+    - varcheck

+ 29 - 0
vendor/github.com/charmbracelet/bubbletea/.golangci.yml

@@ -0,0 +1,29 @@
+run:
+  tests: false
+
+issues:
+  include:
+    - EXC0001
+    - EXC0005
+    - EXC0011
+    - EXC0012
+    - EXC0013
+
+  max-issues-per-linter: 0
+  max-same-issues: 0
+
+linters:
+  enable:
+    - bodyclose
+    - exportloopref
+    - goimports
+    - gosec
+    - nilerr
+    - predeclared
+    - revive
+    - rowserrcheck
+    - sqlclosecheck
+    - tparallel
+    - unconvert
+    - unparam
+    - whitespace

+ 13 - 0
vendor/github.com/charmbracelet/bubbletea/CONTRIBUTING.md

@@ -0,0 +1,13 @@
+# Contributing
+
+Pull requests are welcome for any changes.
+
+Consider opening an issue for larger changes to get feedback on the idea from the team.
+
+If your change touches parts of the Bubble Tea renderer or internals, make sure
+that all the examples in the `examples/` folder continue to run correctly.
+
+For commit messages, please use conventional commits[^1] to make it easier to
+generate release notes.
+
+[^1]: https://www.conventionalcommits.org/en/v1.0.0

+ 21 - 0
vendor/github.com/charmbracelet/bubbletea/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Charmbracelet, Inc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 405 - 0
vendor/github.com/charmbracelet/bubbletea/README.md

@@ -0,0 +1,405 @@
+# Bubble Tea
+
+<p>
+    <img src="https://stuff.charm.sh/bubbletea/bubbletea-github-header-simple.png" width="313" alt="Bubble Tea Title Treatment"><br>
+    <a href="https://github.com/charmbracelet/bubbletea/releases"><img src="https://img.shields.io/github/release/charmbracelet/bubbletea.svg" alt="Latest Release"></a>
+    <a href="https://pkg.go.dev/github.com/charmbracelet/bubbletea?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
+    <a href="https://github.com/charmbracelet/bubbletea/actions"><img src="https://github.com/charmbracelet/bubbletea/workflows/build/badge.svg" alt="Build Status"></a>
+</p>
+
+The fun, functional and stateful way to build terminal apps. A Go framework
+based on [The Elm Architecture][elm]. Bubble Tea is well-suited for simple and
+complex terminal applications, either inline, full-window, or a mix of both.
+
+<p>
+    <img src="https://stuff.charm.sh/bubbletea/bubbletea-example.gif" width="100%" alt="Bubble Tea Example">
+</p>
+
+Bubble Tea is in use in production and includes a number of features and
+performance optimizations we’ve added along the way. Among those is a standard
+framerate-based renderer, a renderer for high-performance scrollable
+regions which works alongside the main renderer, and mouse support.
+
+To get started, see the tutorial below, the [examples][examples], the
+[docs][docs], the [video tutorials][youtube] and some common [resources](#libraries-we-use-with-bubble-tea).
+
+[youtube]: https://charm.sh/yt
+
+## By the way
+
+Be sure to check out [Bubbles][bubbles], a library of common UI components for Bubble Tea.
+
+<p>
+    <a href="https://github.com/charmbracelet/bubbles"><img src="https://stuff.charm.sh/bubbles/bubbles-badge.png" width="174" alt="Bubbles Badge"></a>&nbsp;&nbsp;
+    <a href="https://github.com/charmbracelet/bubbles"><img src="https://stuff.charm.sh/bubbles-examples/textinput.gif" width="400" alt="Text Input Example from Bubbles"></a>
+</p>
+
+***
+
+## Tutorial
+
+Bubble Tea is based on the functional design paradigms of [The Elm
+Architecture][elm], which happens to work nicely with Go. It's a delightful way
+to build applications.
+
+This tutorial assumes you have a working knowledge of Go.
+
+By the way, the non-annotated source code for this program is available
+[on GitHub][tut-source].
+
+[elm]: https://guide.elm-lang.org/architecture/
+[tut-source]:https://github.com/charmbracelet/bubbletea/tree/master/tutorials/basics
+
+### Enough! Let's get to it.
+
+For this tutorial, we're making a shopping list.
+
+To start we'll define our package and import some libraries. Our only external
+import will be the Bubble Tea library, which we'll call `tea` for short.
+
+```go
+package main
+
+import (
+    "fmt"
+    "os"
+
+    tea "github.com/charmbracelet/bubbletea"
+)
+```
+
+Bubble Tea programs are comprised of a **model** that describes the application
+state and three simple methods on that model:
+
+* **Init**, a function that returns an initial command for the application to run.
+* **Update**, a function that handles incoming events and updates the model accordingly.
+* **View**, a function that renders the UI based on the data in the model.
+
+### The Model
+
+So let's start by defining our model which will store our application's state.
+It can be any type, but a `struct` usually makes the most sense.
+
+```go
+type model struct {
+    choices  []string           // items on the to-do list
+    cursor   int                // which to-do list item our cursor is pointing at
+    selected map[int]struct{}   // which to-do items are selected
+}
+```
+
+### Initialization
+
+Next, we’ll define our application’s initial state. In this case, we’re defining
+a function to return our initial model, however, we could just as easily define
+the initial model as a variable elsewhere, too.
+
+```go
+func initialModel() model {
+	return model{
+		// Our to-do list is a grocery list
+		choices:  []string{"Buy carrots", "Buy celery", "Buy kohlrabi"},
+
+		// A map which indicates which choices are selected. We're using
+		// the  map like a mathematical set. The keys refer to the indexes
+		// of the `choices` slice, above.
+		selected: make(map[int]struct{}),
+	}
+}
+```
+
+Next, we define the `Init` method. `Init` can return a `Cmd` that could perform
+some initial I/O. For now, we don't need to do any I/O, so for the command,
+we'll just return `nil`, which translates to "no command."
+
+```go
+func (m model) Init() tea.Cmd {
+    // Just return `nil`, which means "no I/O right now, please."
+    return nil
+}
+```
+
+### The Update Method
+
+Next up is the update method. The update function is called when ”things
+happen.” Its job is to look at what has happened and return an updated model in
+response. It can also return a `Cmd` to make more things happen, but for now
+don't worry about that part.
+
+In our case, when a user presses the down arrow, `Update`’s job is to notice
+that the down arrow was pressed and move the cursor accordingly (or not).
+
+The “something happened” comes in the form of a `Msg`, which can be any type.
+Messages are the result of some I/O that took place, such as a keypress, timer
+tick, or a response from a server.
+
+We usually figure out which type of `Msg` we received with a type switch, but
+you could also use a type assertion.
+
+For now, we'll just deal with `tea.KeyMsg` messages, which are automatically
+sent to the update function when keys are pressed.
+
+```go
+func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+    switch msg := msg.(type) {
+
+    // Is it a key press?
+    case tea.KeyMsg:
+
+        // Cool, what was the actual key pressed?
+        switch msg.String() {
+
+        // These keys should exit the program.
+        case "ctrl+c", "q":
+            return m, tea.Quit
+
+        // The "up" and "k" keys move the cursor up
+        case "up", "k":
+            if m.cursor > 0 {
+                m.cursor--
+            }
+
+        // The "down" and "j" keys move the cursor down
+        case "down", "j":
+            if m.cursor < len(m.choices)-1 {
+                m.cursor++
+            }
+
+        // The "enter" key and the spacebar (a literal space) toggle
+        // the selected state for the item that the cursor is pointing at.
+        case "enter", " ":
+            _, ok := m.selected[m.cursor]
+            if ok {
+                delete(m.selected, m.cursor)
+            } else {
+                m.selected[m.cursor] = struct{}{}
+            }
+        }
+    }
+
+    // Return the updated model to the Bubble Tea runtime for processing.
+    // Note that we're not returning a command.
+    return m, nil
+}
+```
+
+You may have noticed that <kbd>ctrl+c</kbd> and <kbd>q</kbd> above return
+a `tea.Quit` command with the model. That’s a special command which instructs
+the Bubble Tea runtime to quit, exiting the program.
+
+### The View Method
+
+At last, it’s time to render our UI. Of all the methods, the view is the
+simplest. We look at the model in its current state and use it to return
+a `string`. That string is our UI!
+
+Because the view describes the entire UI of your application, you don’t have to
+worry about redrawing logic and stuff like that. Bubble Tea takes care of it
+for you.
+
+```go
+func (m model) View() string {
+    // The header
+    s := "What should we buy at the market?\n\n"
+
+    // Iterate over our choices
+    for i, choice := range m.choices {
+
+        // Is the cursor pointing at this choice?
+        cursor := " " // no cursor
+        if m.cursor == i {
+            cursor = ">" // cursor!
+        }
+
+        // Is this choice selected?
+        checked := " " // not selected
+        if _, ok := m.selected[i]; ok {
+            checked = "x" // selected!
+        }
+
+        // Render the row
+        s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
+    }
+
+    // The footer
+    s += "\nPress q to quit.\n"
+
+    // Send the UI for rendering
+    return s
+}
+```
+
+### All Together Now
+
+The last step is to simply run our program. We pass our initial model to
+`tea.NewProgram` and let it rip:
+
+```go
+func main() {
+    p := tea.NewProgram(initialModel())
+    if _, err := p.Run(); err != nil {
+        fmt.Printf("Alas, there's been an error: %v", err)
+        os.Exit(1)
+    }
+}
+```
+
+## What’s Next?
+
+This tutorial covers the basics of building an interactive terminal UI, but
+in the real world you'll also need to perform I/O. To learn about that have a
+look at the [Command Tutorial][cmd]. It's pretty simple.
+
+There are also several [Bubble Tea examples][examples] available and, of course,
+there are [Go Docs][docs].
+
+[cmd]: http://github.com/charmbracelet/bubbletea/tree/master/tutorials/commands/
+[examples]: http://github.com/charmbracelet/bubbletea/tree/master/examples
+[docs]: https://pkg.go.dev/github.com/charmbracelet/bubbletea?tab=doc
+
+## Debugging
+
+### Debugging with Delve
+
+Since Bubble Tea apps assume control of stdin and stdout, you’ll need to run
+delve in headless mode and then connect to it:
+
+```bash
+# Start the debugger
+$ dlv debug --headless .
+API server listening at: 127.0.0.1:34241
+
+# Connect to it from another terminal
+$ dlv connect 127.0.0.1:34241
+```
+
+Note that the default port used will vary on your system and per run, so
+actually watch out what address the first `dlv` run tells you to connect to.
+
+### Logging Stuff
+
+You can’t really log to stdout with Bubble Tea because your TUI is busy
+occupying that! You can, however, log to a file by including something like
+the following prior to starting your Bubble Tea program:
+
+```go
+if len(os.Getenv("DEBUG")) > 0 {
+	f, err := tea.LogToFile("debug.log", "debug")
+	if err != nil {
+		fmt.Println("fatal:", err)
+		os.Exit(1)
+	}
+	defer f.Close()
+}
+```
+
+To see what’s being logged in real time, run `tail -f debug.log` while you run
+your program in another window.
+
+## Libraries we use with Bubble Tea
+
+* [Bubbles][bubbles]: Common Bubble Tea components such as text inputs, viewports, spinners and so on
+* [Lip Gloss][lipgloss]: Style, format and layout tools for terminal applications
+* [Harmonica][harmonica]: A spring animation library for smooth, natural motion
+* [BubbleZone][bubblezone]: Easy mouse event tracking for Bubble Tea components
+* [Termenv][termenv]: Advanced ANSI styling for terminal applications
+* [Reflow][reflow]: Advanced ANSI-aware methods for working with text
+
+[bubbles]: https://github.com/charmbracelet/bubbles
+[lipgloss]: https://github.com/charmbracelet/lipgloss
+[harmonica]: https://github.com/charmbracelet/harmonica
+[bubblezone]: https://github.com/lrstanley/bubblezone
+[termenv]: https://github.com/muesli/termenv
+[reflow]: https://github.com/muesli/reflow
+
+## Bubble Tea in the Wild
+
+For some Bubble Tea programs in production, see:
+
+* [AT CLI](https://github.com/daskycodes/at_cli): execute AT Commands via serial port connections
+* [Aztify](https://github.com/Azure/aztfy): bring Microsoft Azure resources under Terraform
+* [brows](https://github.com/rubysolo/brows): a GitHub release browser
+* [Canard](https://github.com/mrusme/canard): an RSS client
+* [charm](https://github.com/charmbracelet/charm): the official Charm user account manager
+* [chezmoi](https://github.com/twpayne/chezmoi): securely manage your dotfiles across multiple machines
+* [chtop](https://github.com/chhetripradeep/chtop): monitor your ClickHouse node without leaving terminal
+* [circumflex](https://github.com/bensadeh/circumflex): read Hacker News in the terminal
+* [clidle](https://github.com/ajeetdsouza/clidle): a Wordle clone
+* [cLive](https://github.com/koki-develop/clive): automate terminal operations and view them live in a browser
+* [container-canary](https://github.com/NVIDIA/container-canary): a container validator
+* [countdown](https://github.com/aldernero/countdown): a multi-event countdown timer
+* [dns53](https://github.com/purpleclay/dns53): dynamic DNS with Amazon Route53. Expose your EC2 quickly, securely and privately
+* [eks-node-viewer](https://github.com/awslabs/eks-node-viewer): a tool for visualizing dynamic node usage within an eks cluster
+* [enola](https://github.com/sherlock-project/enola): hunt down social media accounts by username across social networks
+* [flapioca](https://github.com/kbrgl/flapioca): Flappy Bird on the CLI!
+* [fm](https://github.com/knipferrc/fm): a terminal-based file manager
+* [fork-cleaner](https://github.com/caarlos0/fork-cleaner): clean up old and inactive forks in your GitHub account
+* [fztea](https://github.com/jon4hz/fztea): a Flipper Zero TUI
+* [gambit](https://github.com/maaslalani/gambit): chess in the terminal
+* [gembro](https://git.sr.ht/~rafael/gembro): a mouse-driven Gemini browser
+* [gh-b](https://github.com/joaom00/gh-b): a GitHub CLI extension for managing branches
+* [gh-dash](https://www.github.com/dlvhdr/gh-dash): a GitHub CLI extension for PRs and issues
+* [gitflow-toolkit](https://github.com/mritd/gitflow-toolkit): a GitFlow submission tool
+* [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser, and online markdown stash
+* [gocovsh](https://github.com/orlangure/gocovsh): explore Go coverage reports from the CLI
+* [got](https://github.com/fedeztk/got): a simple translator and text-to-speech app build on top of simplytranslate's APIs
+* [httpit](https://github.com/gonetx/httpit): a rapid http(s) benchmark tool
+* [IDNT](https://github.com/r-darwish/idnt): a batch software uninstaller
+* [kboard](https://github.com/CamiloGarciaLaRotta/kboard): a typing game
+* [mandelbrot-cli](https://github.com/MicheleFiladelfia/mandelbrot-cli): a multiplatform terminal mandelbrot set explorer
+* [mc](https://github.com/minio/mc): the official [MinIO](https://min.io) client
+* [mergestat](https://github.com/mergestat/mergestat): run SQL queries on git repositories
+* [Neon Modem Overdrive](https://github.com/mrusme/neonmodem): a BBS-style TUI client for Discourse, Lemmy, Lobste.rs and Hacker News
+* [Noted](https://github.com/torbratsberg/noted): a note viewer and manager
+* [pathos](https://github.com/chip/pathos): a PATH env variable editor
+* [portal](https://github.com/ZinoKader/portal): secure transfers between computers
+* [redis-viewer](https://github.com/SaltFishPr/redis-viewer): a Redis databases browser
+* [sku](https://github.com/fedeztk/sku): Sudoku on the CLI
+* [Slides](https://github.com/maaslalani/slides): a markdown-based presentation tool
+* [SlurmCommander](https://github.com/CLIP-HPC/SlurmCommander): a Slurm workload manager TUI
+* [Soft Serve](https://github.com/charmbracelet/soft-serve): a command-line-first Git server that runs a TUI over SSH
+* [solitaire-tui](https://github.com/brianstrauch/solitaire-tui): Klondike Solitaire for the terminal
+* [StormForge Optimize Controller](https://github.com/thestormforge/optimize-controller): a tool for experimenting with application configurations in Kubernetes
+* [STTG](https://github.com/wille1101/sttg): a teletext client for SVT, Sweden’s national public television station
+* [sttr](https://github.com/abhimanyu003/sttr): a general-purpose text transformer
+* [tasktimer](https://github.com/caarlos0/tasktimer): a dead-simple task timer
+* [termdbms](https://github.com/mathaou/termdbms): a keyboard and mouse driven database browser
+* [ticker](https://github.com/achannarasappa/ticker): a terminal stock viewer and stock position tracker
+* [tran](https://github.com/abdfnx/tran): securely transfer stuff between computers (based on [portal](https://github.com/ZinoKader/portal))
+* [Typer](https://github.com/maaslalani/typer): a typing test
+* [tz](https://github.com/oz/tz): an aid for scheduling across multiple time zones
+* [ugm](https://github.com/ariasmn/ugm): a unix user and group browser
+* [wander](https://github.com/robinovitch61/wander): a HashiCorp Nomad terminal client
+* [WG Commander](https://github.com/AndrianBdn/wg-cmd): a TUI for a simple WireGuard VPN setup 
+* [wishlist](https://github.com/charmbracelet/wishlist): an SSH directory
+
+## Feedback
+
+We'd love to hear your thoughts on this project. Feel free to drop us a note!
+
+* [Twitter](https://twitter.com/charmcli)
+* [The Fediverse](https://mastodon.social/@charmcli)
+* [Discord](https://charm.sh/chat)
+
+## Acknowledgments
+
+Bubble Tea is based on the paradigms of [The Elm Architecture][elm] by Evan
+Czaplicki et alia and the excellent [go-tea][gotea] by TJ Holowaychuk. It’s
+inspired by the many great [_Zeichenorientierte Benutzerschnittstellen_][zb]
+of days past.
+
+[elm]: https://guide.elm-lang.org/architecture/
+[gotea]: https://github.com/tj/go-tea
+[zb]: https://de.wikipedia.org/wiki/Zeichenorientierte_Benutzerschnittstelle
+
+## License
+
+[MIT](https://github.com/charmbracelet/bubbletea/raw/master/LICENSE)
+
+***
+
+Part of [Charm](https://charm.sh).
+
+<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>
+
+Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة

+ 172 - 0
vendor/github.com/charmbracelet/bubbletea/commands.go

@@ -0,0 +1,172 @@
+package tea
+
+import (
+	"time"
+)
+
+// Batch performs a bunch of commands concurrently with no ordering guarantees
+// about the results. Use a Batch to return several commands.
+//
+// Example:
+//
+//	    func (m model) Init() Cmd {
+//		       return tea.Batch(someCommand, someOtherCommand)
+//	    }
+func Batch(cmds ...Cmd) Cmd {
+	var validCmds []Cmd
+	for _, c := range cmds {
+		if c == nil {
+			continue
+		}
+		validCmds = append(validCmds, c)
+	}
+	if len(validCmds) == 0 {
+		return nil
+	}
+	return func() Msg {
+		return BatchMsg(validCmds)
+	}
+}
+
+// BatchMsg is a message used to perform a bunch of commands concurrently with
+// no ordering guarantees. You can send a BatchMsg with Batch.
+type BatchMsg []Cmd
+
+// Sequence runs the given commands one at a time, in order. Contrast this with
+// Batch, which runs commands concurrently.
+func Sequence(cmds ...Cmd) Cmd {
+	return func() Msg {
+		return sequenceMsg(cmds)
+	}
+}
+
+// sequenceMsg is used internally to run the given commands in order.
+type sequenceMsg []Cmd
+
+// Every is a command that ticks in sync with the system clock. So, if you
+// wanted to tick with the system clock every second, minute or hour you
+// could use this. It's also handy for having different things tick in sync.
+//
+// Because we're ticking with the system clock the tick will likely not run for
+// the entire specified duration. For example, if we're ticking for one minute
+// and the clock is at 12:34:20 then the next tick will happen at 12:35:00, 40
+// seconds later.
+//
+// To produce the command, pass a duration and a function which returns
+// a message containing the time at which the tick occurred.
+//
+//	type TickMsg time.Time
+//
+//	cmd := Every(time.Second, func(t time.Time) Msg {
+//	   return TickMsg(t)
+//	})
+//
+// Beginners' note: Every sends a single message and won't automatically
+// dispatch messages at an interval. To do that, you'll want to return another
+// Every command after receiving your tick message. For example:
+//
+//	type TickMsg time.Time
+//
+//	// Send a message every second.
+//	func tickEvery() Cmd {
+//	    return Every(time.Second, func(t time.Time) Msg {
+//	        return TickMsg(t)
+//	    })
+//	}
+//
+//	func (m model) Init() Cmd {
+//	    // Start ticking.
+//	    return tickEvery()
+//	}
+//
+//	func (m model) Update(msg Msg) (Model, Cmd) {
+//	    switch msg.(type) {
+//	    case TickMsg:
+//	        // Return your Every command again to loop.
+//	        return m, tickEvery()
+//	    }
+//	    return m, nil
+//	}
+//
+// Every is analogous to Tick in the Elm Architecture.
+func Every(duration time.Duration, fn func(time.Time) Msg) Cmd {
+	return func() Msg {
+		n := time.Now()
+		d := n.Truncate(duration).Add(duration).Sub(n)
+		t := time.NewTimer(d)
+		return fn(<-t.C)
+	}
+}
+
+// Tick produces a command at an interval independent of the system clock at
+// the given duration. That is, the timer begins precisely when invoked,
+// and runs for its entire duration.
+//
+// To produce the command, pass a duration and a function which returns
+// a message containing the time at which the tick occurred.
+//
+//	type TickMsg time.Time
+//
+//	cmd := Tick(time.Second, func(t time.Time) Msg {
+//	   return TickMsg(t)
+//	})
+//
+// Beginners' note: Tick sends a single message and won't automatically
+// dispatch messages at an interval. To do that, you'll want to return another
+// Tick command after receiving your tick message. For example:
+//
+//	type TickMsg time.Time
+//
+//	func doTick() Cmd {
+//	    return Tick(time.Second, func(t time.Time) Msg {
+//	        return TickMsg(t)
+//	    })
+//	}
+//
+//	func (m model) Init() Cmd {
+//	    // Start ticking.
+//	    return doTick()
+//	}
+//
+//	func (m model) Update(msg Msg) (Model, Cmd) {
+//	    switch msg.(type) {
+//	    case TickMsg:
+//	        // Return your Tick command again to loop.
+//	        return m, doTick()
+//	    }
+//	    return m, nil
+//	}
+func Tick(d time.Duration, fn func(time.Time) Msg) Cmd {
+	return func() Msg {
+		t := time.NewTimer(d)
+		return fn(<-t.C)
+	}
+}
+
+// Sequentially produces a command that sequentially executes the given
+// commands.
+// The Msg returned is the first non-nil message returned by a Cmd.
+//
+//	func saveStateCmd() Msg {
+//	   if err := save(); err != nil {
+//	       return errMsg{err}
+//	   }
+//	   return nil
+//	}
+//
+//	cmd := Sequentially(saveStateCmd, Quit)
+//
+// Deprecated: use Sequence instead.
+func Sequentially(cmds ...Cmd) Cmd {
+	return func() Msg {
+		for _, cmd := range cmds {
+			if cmd == nil {
+				continue
+			}
+			if msg := cmd(); msg != nil {
+				return msg
+			}
+		}
+		return nil
+	}
+}

+ 129 - 0
vendor/github.com/charmbracelet/bubbletea/exec.go

@@ -0,0 +1,129 @@
+package tea
+
+import (
+	"io"
+	"os"
+	"os/exec"
+)
+
+// execMsg is used internally to run an ExecCommand sent with Exec.
+type execMsg struct {
+	cmd ExecCommand
+	fn  ExecCallback
+}
+
+// Exec is used to perform arbitrary I/O in a blocking fashion, effectively
+// pausing the Program while execution is running and resuming it when
+// execution has completed.
+//
+// Most of the time you'll want to use ExecProcess, which runs an exec.Cmd.
+//
+// For non-interactive i/o you should use a Cmd (that is, a tea.Cmd).
+func Exec(c ExecCommand, fn ExecCallback) Cmd {
+	return func() Msg {
+		return execMsg{cmd: c, fn: fn}
+	}
+}
+
+// ExecProcess runs the given *exec.Cmd in a blocking fashion, effectively
+// pausing the Program while the command is running. After the *exec.Cmd exists
+// the Program resumes. It's useful for spawning other interactive applications
+// such as editors and shells from within a Program.
+//
+// To produce the command, pass an *exec.Cmd and a function which returns
+// a message containing the error which may have occurred when running the
+// ExecCommand.
+//
+//	type VimFinishedMsg struct { err error }
+//
+//	c := exec.Command("vim", "file.txt")
+//
+//	cmd := ExecProcess(c, func(err error) Msg {
+//	    return VimFinishedMsg{err: err}
+//	})
+//
+// Or, if you don't care about errors, you could simply:
+//
+//	cmd := ExecProcess(exec.Command("vim", "file.txt"), nil)
+//
+// For non-interactive i/o you should use a Cmd (that is, a tea.Cmd).
+func ExecProcess(c *exec.Cmd, fn ExecCallback) Cmd {
+	return Exec(wrapExecCommand(c), fn)
+}
+
+// ExecCallback is used when executing an *exec.Command to return a message
+// with an error, which may or may not be nil.
+type ExecCallback func(error) Msg
+
+// ExecCommand can be implemented to execute things in a blocking fashion in
+// the current terminal.
+type ExecCommand interface {
+	Run() error
+	SetStdin(io.Reader)
+	SetStdout(io.Writer)
+	SetStderr(io.Writer)
+}
+
+// wrapExecCommand wraps an exec.Cmd so that it satisfies the ExecCommand
+// interface so it can be used with Exec.
+func wrapExecCommand(c *exec.Cmd) ExecCommand {
+	return &osExecCommand{Cmd: c}
+}
+
+// osExecCommand is a layer over an exec.Cmd that satisfies the ExecCommand
+// interface.
+type osExecCommand struct{ *exec.Cmd }
+
+// SetStdin sets stdin on underlying exec.Cmd to the given io.Reader.
+func (c *osExecCommand) SetStdin(r io.Reader) {
+	// If unset, have the command use the same input as the terminal.
+	if c.Stdin == nil {
+		c.Stdin = r
+	}
+}
+
+// SetStdout sets stdout on underlying exec.Cmd to the given io.Writer.
+func (c *osExecCommand) SetStdout(w io.Writer) {
+	// If unset, have the command use the same output as the terminal.
+	if c.Stdout == nil {
+		c.Stdout = w
+	}
+}
+
+// SetStderr sets stderr on the underlying exec.Cmd to the given io.Writer.
+func (c *osExecCommand) SetStderr(w io.Writer) {
+	// If unset, use stderr for the command's stderr
+	if c.Stderr == nil {
+		c.Stderr = w
+	}
+}
+
+// exec runs an ExecCommand and delivers the results to the program as a Msg.
+func (p *Program) exec(c ExecCommand, fn ExecCallback) {
+	if err := p.ReleaseTerminal(); err != nil {
+		// If we can't release input, abort.
+		if fn != nil {
+			go p.Send(fn(err))
+		}
+		return
+	}
+
+	c.SetStdin(p.input)
+	c.SetStdout(p.output.TTY())
+	c.SetStderr(os.Stderr)
+
+	// Execute system command.
+	if err := c.Run(); err != nil {
+		_ = p.RestoreTerminal() // also try to restore the terminal.
+		if fn != nil {
+			go p.Send(fn(err))
+		}
+		return
+	}
+
+	// Have the program re-capture input.
+	err := p.RestoreTerminal()
+	if fn != nil {
+		go p.Send(fn(err))
+	}
+}

+ 664 - 0
vendor/github.com/charmbracelet/bubbletea/key.go

@@ -0,0 +1,664 @@
+package tea
+
+import (
+	"errors"
+	"io"
+	"unicode/utf8"
+
+	"github.com/mattn/go-localereader"
+)
+
+// KeyMsg contains information about a keypress. KeyMsgs are always sent to
+// the program's update function. There are a couple general patterns you could
+// use to check for keypresses:
+//
+//	// Switch on the string representation of the key (shorter)
+//	switch msg := msg.(type) {
+//	case KeyMsg:
+//	    switch msg.String() {
+//	    case "enter":
+//	        fmt.Println("you pressed enter!")
+//	    case "a":
+//	        fmt.Println("you pressed a!")
+//	    }
+//	}
+//
+//	// Switch on the key type (more foolproof)
+//	switch msg := msg.(type) {
+//	case KeyMsg:
+//	    switch msg.Type {
+//	    case KeyEnter:
+//	        fmt.Println("you pressed enter!")
+//	    case KeyRunes:
+//	        switch string(msg.Runes) {
+//	        case "a":
+//	            fmt.Println("you pressed a!")
+//	        }
+//	    }
+//	}
+//
+// Note that Key.Runes will always contain at least one character, so you can
+// always safely call Key.Runes[0]. In most cases Key.Runes will only contain
+// one character, though certain input method editors (most notably Chinese
+// IMEs) can input multiple runes at once.
+type KeyMsg Key
+
+// String returns a string representation for a key message. It's safe (and
+// encouraged) for use in key comparison.
+func (k KeyMsg) String() (str string) {
+	return Key(k).String()
+}
+
+// Key contains information about a keypress.
+type Key struct {
+	Type  KeyType
+	Runes []rune
+	Alt   bool
+}
+
+// String returns a friendly string representation for a key. It's safe (and
+// encouraged) for use in key comparison.
+//
+//	k := Key{Type: KeyEnter}
+//	fmt.Println(k)
+//	// Output: enter
+func (k Key) String() (str string) {
+	if k.Alt {
+		str += "alt+"
+	}
+	if k.Type == KeyRunes {
+		str += string(k.Runes)
+		return str
+	} else if s, ok := keyNames[k.Type]; ok {
+		str += s
+		return str
+	}
+	return ""
+}
+
+// KeyType indicates the key pressed, such as KeyEnter or KeyBreak or KeyCtrlC.
+// All other keys will be type KeyRunes. To get the rune value, check the Rune
+// method on a Key struct, or use the Key.String() method:
+//
+//	k := Key{Type: KeyRunes, Runes: []rune{'a'}, Alt: true}
+//	if k.Type == KeyRunes {
+//
+//	    fmt.Println(k.Runes)
+//	    // Output: a
+//
+//	    fmt.Println(k.String())
+//	    // Output: alt+a
+//
+//	}
+type KeyType int
+
+func (k KeyType) String() (str string) {
+	if s, ok := keyNames[k]; ok {
+		return s
+	}
+	return ""
+}
+
+// Control keys. We could do this with an iota, but the values are very
+// specific, so we set the values explicitly to avoid any confusion.
+//
+// See also:
+// https://en.wikipedia.org/wiki/C0_and_C1_control_codes
+const (
+	keyNUL KeyType = 0   // null, \0
+	keySOH KeyType = 1   // start of heading
+	keySTX KeyType = 2   // start of text
+	keyETX KeyType = 3   // break, ctrl+c
+	keyEOT KeyType = 4   // end of transmission
+	keyENQ KeyType = 5   // enquiry
+	keyACK KeyType = 6   // acknowledge
+	keyBEL KeyType = 7   // bell, \a
+	keyBS  KeyType = 8   // backspace
+	keyHT  KeyType = 9   // horizontal tabulation, \t
+	keyLF  KeyType = 10  // line feed, \n
+	keyVT  KeyType = 11  // vertical tabulation \v
+	keyFF  KeyType = 12  // form feed \f
+	keyCR  KeyType = 13  // carriage return, \r
+	keySO  KeyType = 14  // shift out
+	keySI  KeyType = 15  // shift in
+	keyDLE KeyType = 16  // data link escape
+	keyDC1 KeyType = 17  // device control one
+	keyDC2 KeyType = 18  // device control two
+	keyDC3 KeyType = 19  // device control three
+	keyDC4 KeyType = 20  // device control four
+	keyNAK KeyType = 21  // negative acknowledge
+	keySYN KeyType = 22  // synchronous idle
+	keyETB KeyType = 23  // end of transmission block
+	keyCAN KeyType = 24  // cancel
+	keyEM  KeyType = 25  // end of medium
+	keySUB KeyType = 26  // substitution
+	keyESC KeyType = 27  // escape, \e
+	keyFS  KeyType = 28  // file separator
+	keyGS  KeyType = 29  // group separator
+	keyRS  KeyType = 30  // record separator
+	keyUS  KeyType = 31  // unit separator
+	keyDEL KeyType = 127 // delete. on most systems this is mapped to backspace, I hear
+)
+
+// Control key aliases.
+const (
+	KeyNull      KeyType = keyNUL
+	KeyBreak     KeyType = keyETX
+	KeyEnter     KeyType = keyCR
+	KeyBackspace KeyType = keyDEL
+	KeyTab       KeyType = keyHT
+	KeyEsc       KeyType = keyESC
+	KeyEscape    KeyType = keyESC
+
+	KeyCtrlAt           KeyType = keyNUL // ctrl+@
+	KeyCtrlA            KeyType = keySOH
+	KeyCtrlB            KeyType = keySTX
+	KeyCtrlC            KeyType = keyETX
+	KeyCtrlD            KeyType = keyEOT
+	KeyCtrlE            KeyType = keyENQ
+	KeyCtrlF            KeyType = keyACK
+	KeyCtrlG            KeyType = keyBEL
+	KeyCtrlH            KeyType = keyBS
+	KeyCtrlI            KeyType = keyHT
+	KeyCtrlJ            KeyType = keyLF
+	KeyCtrlK            KeyType = keyVT
+	KeyCtrlL            KeyType = keyFF
+	KeyCtrlM            KeyType = keyCR
+	KeyCtrlN            KeyType = keySO
+	KeyCtrlO            KeyType = keySI
+	KeyCtrlP            KeyType = keyDLE
+	KeyCtrlQ            KeyType = keyDC1
+	KeyCtrlR            KeyType = keyDC2
+	KeyCtrlS            KeyType = keyDC3
+	KeyCtrlT            KeyType = keyDC4
+	KeyCtrlU            KeyType = keyNAK
+	KeyCtrlV            KeyType = keySYN
+	KeyCtrlW            KeyType = keyETB
+	KeyCtrlX            KeyType = keyCAN
+	KeyCtrlY            KeyType = keyEM
+	KeyCtrlZ            KeyType = keySUB
+	KeyCtrlOpenBracket  KeyType = keyESC // ctrl+[
+	KeyCtrlBackslash    KeyType = keyFS  // ctrl+\
+	KeyCtrlCloseBracket KeyType = keyGS  // ctrl+]
+	KeyCtrlCaret        KeyType = keyRS  // ctrl+^
+	KeyCtrlUnderscore   KeyType = keyUS  // ctrl+_
+	KeyCtrlQuestionMark KeyType = keyDEL // ctrl+?
+)
+
+// Other keys.
+const (
+	KeyRunes KeyType = -(iota + 1)
+	KeyUp
+	KeyDown
+	KeyRight
+	KeyLeft
+	KeyShiftTab
+	KeyHome
+	KeyEnd
+	KeyPgUp
+	KeyPgDown
+	KeyCtrlPgUp
+	KeyCtrlPgDown
+	KeyDelete
+	KeyInsert
+	KeySpace
+	KeyCtrlUp
+	KeyCtrlDown
+	KeyCtrlRight
+	KeyCtrlLeft
+	KeyCtrlHome
+	KeyCtrlEnd
+	KeyShiftUp
+	KeyShiftDown
+	KeyShiftRight
+	KeyShiftLeft
+	KeyShiftHome
+	KeyShiftEnd
+	KeyCtrlShiftUp
+	KeyCtrlShiftDown
+	KeyCtrlShiftLeft
+	KeyCtrlShiftRight
+	KeyCtrlShiftHome
+	KeyCtrlShiftEnd
+	KeyF1
+	KeyF2
+	KeyF3
+	KeyF4
+	KeyF5
+	KeyF6
+	KeyF7
+	KeyF8
+	KeyF9
+	KeyF10
+	KeyF11
+	KeyF12
+	KeyF13
+	KeyF14
+	KeyF15
+	KeyF16
+	KeyF17
+	KeyF18
+	KeyF19
+	KeyF20
+)
+
+// Mappings for control keys and other special keys to friendly consts.
+var keyNames = map[KeyType]string{
+	// Control keys.
+	keyNUL: "ctrl+@", // also ctrl+` (that's ctrl+backtick)
+	keySOH: "ctrl+a",
+	keySTX: "ctrl+b",
+	keyETX: "ctrl+c",
+	keyEOT: "ctrl+d",
+	keyENQ: "ctrl+e",
+	keyACK: "ctrl+f",
+	keyBEL: "ctrl+g",
+	keyBS:  "ctrl+h",
+	keyHT:  "tab", // also ctrl+i
+	keyLF:  "ctrl+j",
+	keyVT:  "ctrl+k",
+	keyFF:  "ctrl+l",
+	keyCR:  "enter",
+	keySO:  "ctrl+n",
+	keySI:  "ctrl+o",
+	keyDLE: "ctrl+p",
+	keyDC1: "ctrl+q",
+	keyDC2: "ctrl+r",
+	keyDC3: "ctrl+s",
+	keyDC4: "ctrl+t",
+	keyNAK: "ctrl+u",
+	keySYN: "ctrl+v",
+	keyETB: "ctrl+w",
+	keyCAN: "ctrl+x",
+	keyEM:  "ctrl+y",
+	keySUB: "ctrl+z",
+	keyESC: "esc",
+	keyFS:  "ctrl+\\",
+	keyGS:  "ctrl+]",
+	keyRS:  "ctrl+^",
+	keyUS:  "ctrl+_",
+	keyDEL: "backspace",
+
+	// Other keys.
+	KeyRunes:          "runes",
+	KeyUp:             "up",
+	KeyDown:           "down",
+	KeyRight:          "right",
+	KeySpace:          " ", // for backwards compatibility
+	KeyLeft:           "left",
+	KeyShiftTab:       "shift+tab",
+	KeyHome:           "home",
+	KeyEnd:            "end",
+	KeyCtrlHome:       "ctrl+home",
+	KeyCtrlEnd:        "ctrl+end",
+	KeyShiftHome:      "shift+home",
+	KeyShiftEnd:       "shift+end",
+	KeyCtrlShiftHome:  "ctrl+shift+home",
+	KeyCtrlShiftEnd:   "ctrl+shift+end",
+	KeyPgUp:           "pgup",
+	KeyPgDown:         "pgdown",
+	KeyCtrlPgUp:       "ctrl+pgup",
+	KeyCtrlPgDown:     "ctrl+pgdown",
+	KeyDelete:         "delete",
+	KeyInsert:         "insert",
+	KeyCtrlUp:         "ctrl+up",
+	KeyCtrlDown:       "ctrl+down",
+	KeyCtrlRight:      "ctrl+right",
+	KeyCtrlLeft:       "ctrl+left",
+	KeyShiftUp:        "shift+up",
+	KeyShiftDown:      "shift+down",
+	KeyShiftRight:     "shift+right",
+	KeyShiftLeft:      "shift+left",
+	KeyCtrlShiftUp:    "ctrl+shift+up",
+	KeyCtrlShiftDown:  "ctrl+shift+down",
+	KeyCtrlShiftLeft:  "ctrl+shift+left",
+	KeyCtrlShiftRight: "ctrl+shift+right",
+	KeyF1:             "f1",
+	KeyF2:             "f2",
+	KeyF3:             "f3",
+	KeyF4:             "f4",
+	KeyF5:             "f5",
+	KeyF6:             "f6",
+	KeyF7:             "f7",
+	KeyF8:             "f8",
+	KeyF9:             "f9",
+	KeyF10:            "f10",
+	KeyF11:            "f11",
+	KeyF12:            "f12",
+	KeyF13:            "f13",
+	KeyF14:            "f14",
+	KeyF15:            "f15",
+	KeyF16:            "f16",
+	KeyF17:            "f17",
+	KeyF18:            "f18",
+	KeyF19:            "f19",
+	KeyF20:            "f20",
+}
+
+// Sequence mappings.
+var sequences = map[string]Key{
+	// Arrow keys
+	"\x1b[A":     {Type: KeyUp},
+	"\x1b[B":     {Type: KeyDown},
+	"\x1b[C":     {Type: KeyRight},
+	"\x1b[D":     {Type: KeyLeft},
+	"\x1b[1;2A":  {Type: KeyShiftUp},
+	"\x1b[1;2B":  {Type: KeyShiftDown},
+	"\x1b[1;2C":  {Type: KeyShiftRight},
+	"\x1b[1;2D":  {Type: KeyShiftLeft},
+	"\x1b[OA":    {Type: KeyShiftUp},    // DECCKM
+	"\x1b[OB":    {Type: KeyShiftDown},  // DECCKM
+	"\x1b[OC":    {Type: KeyShiftRight}, // DECCKM
+	"\x1b[OD":    {Type: KeyShiftLeft},  // DECCKM
+	"\x1b[a":     {Type: KeyShiftUp},    // urxvt
+	"\x1b[b":     {Type: KeyShiftDown},  // urxvt
+	"\x1b[c":     {Type: KeyShiftRight}, // urxvt
+	"\x1b[d":     {Type: KeyShiftLeft},  // urxvt
+	"\x1b[1;3A":  {Type: KeyUp, Alt: true},
+	"\x1b[1;3B":  {Type: KeyDown, Alt: true},
+	"\x1b[1;3C":  {Type: KeyRight, Alt: true},
+	"\x1b[1;3D":  {Type: KeyLeft, Alt: true},
+	"\x1b\x1b[A": {Type: KeyUp, Alt: true},    // urxvt
+	"\x1b\x1b[B": {Type: KeyDown, Alt: true},  // urxvt
+	"\x1b\x1b[C": {Type: KeyRight, Alt: true}, // urxvt
+	"\x1b\x1b[D": {Type: KeyLeft, Alt: true},  // urxvt
+	"\x1b[1;4A":  {Type: KeyShiftUp, Alt: true},
+	"\x1b[1;4B":  {Type: KeyShiftDown, Alt: true},
+	"\x1b[1;4C":  {Type: KeyShiftRight, Alt: true},
+	"\x1b[1;4D":  {Type: KeyShiftLeft, Alt: true},
+	"\x1b\x1b[a": {Type: KeyShiftUp, Alt: true},    // urxvt
+	"\x1b\x1b[b": {Type: KeyShiftDown, Alt: true},  // urxvt
+	"\x1b\x1b[c": {Type: KeyShiftRight, Alt: true}, // urxvt
+	"\x1b\x1b[d": {Type: KeyShiftLeft, Alt: true},  // urxvt
+	"\x1b[1;5A":  {Type: KeyCtrlUp},
+	"\x1b[1;5B":  {Type: KeyCtrlDown},
+	"\x1b[1;5C":  {Type: KeyCtrlRight},
+	"\x1b[1;5D":  {Type: KeyCtrlLeft},
+	"\x1b[Oa":    {Type: KeyCtrlUp, Alt: true},    // urxvt
+	"\x1b[Ob":    {Type: KeyCtrlDown, Alt: true},  // urxvt
+	"\x1b[Oc":    {Type: KeyCtrlRight, Alt: true}, // urxvt
+	"\x1b[Od":    {Type: KeyCtrlLeft, Alt: true},  // urxvt
+	"\x1b[1;6A":  {Type: KeyCtrlShiftUp},
+	"\x1b[1;6B":  {Type: KeyCtrlShiftDown},
+	"\x1b[1;6C":  {Type: KeyCtrlShiftRight},
+	"\x1b[1;6D":  {Type: KeyCtrlShiftLeft},
+	"\x1b[1;7A":  {Type: KeyCtrlUp, Alt: true},
+	"\x1b[1;7B":  {Type: KeyCtrlDown, Alt: true},
+	"\x1b[1;7C":  {Type: KeyCtrlRight, Alt: true},
+	"\x1b[1;7D":  {Type: KeyCtrlLeft, Alt: true},
+	"\x1b[1;8A":  {Type: KeyCtrlShiftUp, Alt: true},
+	"\x1b[1;8B":  {Type: KeyCtrlShiftDown, Alt: true},
+	"\x1b[1;8C":  {Type: KeyCtrlShiftRight, Alt: true},
+	"\x1b[1;8D":  {Type: KeyCtrlShiftLeft, Alt: true},
+
+	// Miscellaneous keys
+	"\x1b[Z": {Type: KeyShiftTab},
+
+	"\x1b[2~":     {Type: KeyInsert},
+	"\x1b[3;2~":   {Type: KeyInsert, Alt: true},
+	"\x1b\x1b[2~": {Type: KeyInsert, Alt: true}, // urxvt
+
+	"\x1b[3~":     {Type: KeyDelete},
+	"\x1b[3;3~":   {Type: KeyDelete, Alt: true},
+	"\x1b\x1b[3~": {Type: KeyDelete, Alt: true}, // urxvt
+
+	"\x1b[5~":     {Type: KeyPgUp},
+	"\x1b[5;3~":   {Type: KeyPgUp, Alt: true},
+	"\x1b\x1b[5~": {Type: KeyPgUp, Alt: true}, // urxvt
+	"\x1b[5;5~":   {Type: KeyCtrlPgUp},
+	"\x1b[5^":     {Type: KeyCtrlPgUp}, // urxvt
+	"\x1b[5;7~":   {Type: KeyCtrlPgUp, Alt: true},
+	"\x1b\x1b[5^": {Type: KeyCtrlPgUp, Alt: true}, // urxvt
+
+	"\x1b[6~":     {Type: KeyPgDown},
+	"\x1b[6;3~":   {Type: KeyPgDown, Alt: true},
+	"\x1b\x1b[6~": {Type: KeyPgDown, Alt: true}, // urxvt
+	"\x1b[6;5~":   {Type: KeyCtrlPgDown},
+	"\x1b[6^":     {Type: KeyCtrlPgDown}, // urxvt
+	"\x1b[6;7~":   {Type: KeyCtrlPgDown, Alt: true},
+	"\x1b\x1b[6^": {Type: KeyCtrlPgDown, Alt: true}, // urxvt
+
+	"\x1b[1~":   {Type: KeyHome},
+	"\x1b[H":    {Type: KeyHome},                     // xterm, lxterm
+	"\x1b[1;3H": {Type: KeyHome, Alt: true},          // xterm, lxterm
+	"\x1b[1;5H": {Type: KeyCtrlHome},                 // xterm, lxterm
+	"\x1b[1;7H": {Type: KeyCtrlHome, Alt: true},      // xterm, lxterm
+	"\x1b[1;2H": {Type: KeyShiftHome},                // xterm, lxterm
+	"\x1b[1;4H": {Type: KeyShiftHome, Alt: true},     // xterm, lxterm
+	"\x1b[1;6H": {Type: KeyCtrlShiftHome},            // xterm, lxterm
+	"\x1b[1;8H": {Type: KeyCtrlShiftHome, Alt: true}, // xterm, lxterm
+
+	"\x1b[4~":   {Type: KeyEnd},
+	"\x1b[F":    {Type: KeyEnd},                     // xterm, lxterm
+	"\x1b[1;3F": {Type: KeyEnd, Alt: true},          // xterm, lxterm
+	"\x1b[1;5F": {Type: KeyCtrlEnd},                 // xterm, lxterm
+	"\x1b[1;7F": {Type: KeyCtrlEnd, Alt: true},      // xterm, lxterm
+	"\x1b[1;2F": {Type: KeyShiftEnd},                // xterm, lxterm
+	"\x1b[1;4F": {Type: KeyShiftEnd, Alt: true},     // xterm, lxterm
+	"\x1b[1;6F": {Type: KeyCtrlShiftEnd},            // xterm, lxterm
+	"\x1b[1;8F": {Type: KeyCtrlShiftEnd, Alt: true}, // xterm, lxterm
+
+	"\x1b[7~":     {Type: KeyHome},                     // urxvt
+	"\x1b\x1b[7~": {Type: KeyHome, Alt: true},          // urxvt
+	"\x1b[7^":     {Type: KeyCtrlHome},                 // urxvt
+	"\x1b\x1b[7^": {Type: KeyCtrlHome, Alt: true},      // urxvt
+	"\x1b[7$":     {Type: KeyShiftHome},                // urxvt
+	"\x1b\x1b[7$": {Type: KeyShiftHome, Alt: true},     // urxvt
+	"\x1b[7@":     {Type: KeyCtrlShiftHome},            // urxvt
+	"\x1b\x1b[7@": {Type: KeyCtrlShiftHome, Alt: true}, // urxvt
+
+	"\x1b[8~":     {Type: KeyEnd},                     // urxvt
+	"\x1b\x1b[8~": {Type: KeyEnd, Alt: true},          // urxvt
+	"\x1b[8^":     {Type: KeyCtrlEnd},                 // urxvt
+	"\x1b\x1b[8^": {Type: KeyCtrlEnd, Alt: true},      // urxvt
+	"\x1b[8$":     {Type: KeyShiftEnd},                // urxvt
+	"\x1b\x1b[8$": {Type: KeyShiftEnd, Alt: true},     // urxvt
+	"\x1b[8@":     {Type: KeyCtrlShiftEnd},            // urxvt
+	"\x1b\x1b[8@": {Type: KeyCtrlShiftEnd, Alt: true}, // urxvt
+
+	// Function keys, Linux console
+	"\x1b[[A": {Type: KeyF1}, // linux console
+	"\x1b[[B": {Type: KeyF2}, // linux console
+	"\x1b[[C": {Type: KeyF3}, // linux console
+	"\x1b[[D": {Type: KeyF4}, // linux console
+	"\x1b[[E": {Type: KeyF5}, // linux console
+
+	// Function keys, X11
+	"\x1bOP": {Type: KeyF1}, // vt100, xterm
+	"\x1bOQ": {Type: KeyF2}, // vt100, xterm
+	"\x1bOR": {Type: KeyF3}, // vt100, xterm
+	"\x1bOS": {Type: KeyF4}, // vt100, xterm
+
+	"\x1b[1;3P": {Type: KeyF1, Alt: true}, // vt100, xterm
+	"\x1b[1;3Q": {Type: KeyF2, Alt: true}, // vt100, xterm
+	"\x1b[1;3R": {Type: KeyF3, Alt: true}, // vt100, xterm
+	"\x1b[1;3S": {Type: KeyF4, Alt: true}, // vt100, xterm
+
+	"\x1b[11~": {Type: KeyF1}, // urxvt
+	"\x1b[12~": {Type: KeyF2}, // urxvt
+	"\x1b[13~": {Type: KeyF3}, // urxvt
+	"\x1b[14~": {Type: KeyF4}, // urxvt
+
+	"\x1b\x1b[11~": {Type: KeyF1, Alt: true}, // urxvt
+	"\x1b\x1b[12~": {Type: KeyF2, Alt: true}, // urxvt
+	"\x1b\x1b[13~": {Type: KeyF3, Alt: true}, // urxvt
+	"\x1b\x1b[14~": {Type: KeyF4, Alt: true}, // urxvt
+
+	"\x1b[15~": {Type: KeyF5}, // vt100, xterm, also urxvt
+
+	"\x1b[15;3~": {Type: KeyF5, Alt: true}, // vt100, xterm, also urxvt
+
+	"\x1b\x1b[15~": {Type: KeyF5, Alt: true}, // urxvt
+
+	"\x1b[17~": {Type: KeyF6},  // vt100, xterm, also urxvt
+	"\x1b[18~": {Type: KeyF7},  // vt100, xterm, also urxvt
+	"\x1b[19~": {Type: KeyF8},  // vt100, xterm, also urxvt
+	"\x1b[20~": {Type: KeyF9},  // vt100, xterm, also urxvt
+	"\x1b[21~": {Type: KeyF10}, // vt100, xterm, also urxvt
+
+	"\x1b\x1b[17~": {Type: KeyF6, Alt: true},  // urxvt
+	"\x1b\x1b[18~": {Type: KeyF7, Alt: true},  // urxvt
+	"\x1b\x1b[19~": {Type: KeyF8, Alt: true},  // urxvt
+	"\x1b\x1b[20~": {Type: KeyF9, Alt: true},  // urxvt
+	"\x1b\x1b[21~": {Type: KeyF10, Alt: true}, // urxvt
+
+	"\x1b[17;3~": {Type: KeyF6, Alt: true},  // vt100, xterm
+	"\x1b[18;3~": {Type: KeyF7, Alt: true},  // vt100, xterm
+	"\x1b[19;3~": {Type: KeyF8, Alt: true},  // vt100, xterm
+	"\x1b[20;3~": {Type: KeyF9, Alt: true},  // vt100, xterm
+	"\x1b[21;3~": {Type: KeyF10, Alt: true}, // vt100, xterm
+
+	"\x1b[23~": {Type: KeyF11}, // vt100, xterm, also urxvt
+	"\x1b[24~": {Type: KeyF12}, // vt100, xterm, also urxvt
+
+	"\x1b[23;3~": {Type: KeyF11, Alt: true}, // vt100, xterm
+	"\x1b[24;3~": {Type: KeyF12, Alt: true}, // vt100, xterm
+
+	"\x1b\x1b[23~": {Type: KeyF11, Alt: true}, // urxvt
+	"\x1b\x1b[24~": {Type: KeyF12, Alt: true}, // urxvt
+
+	"\x1b[1;2P": {Type: KeyF13},
+	"\x1b[1;2Q": {Type: KeyF14},
+
+	"\x1b[25~": {Type: KeyF13}, // vt100, xterm, also urxvt
+	"\x1b[26~": {Type: KeyF14}, // vt100, xterm, also urxvt
+
+	"\x1b[25;3~": {Type: KeyF13, Alt: true}, // vt100, xterm
+	"\x1b[26;3~": {Type: KeyF14, Alt: true}, // vt100, xterm
+
+	"\x1b\x1b[25~": {Type: KeyF13, Alt: true}, // urxvt
+	"\x1b\x1b[26~": {Type: KeyF14, Alt: true}, // urxvt
+
+	"\x1b[1;2R": {Type: KeyF15},
+	"\x1b[1;2S": {Type: KeyF16},
+
+	"\x1b[28~": {Type: KeyF15}, // vt100, xterm, also urxvt
+	"\x1b[29~": {Type: KeyF16}, // vt100, xterm, also urxvt
+
+	"\x1b[28;3~": {Type: KeyF15, Alt: true}, // vt100, xterm
+	"\x1b[29;3~": {Type: KeyF16, Alt: true}, // vt100, xterm
+
+	"\x1b\x1b[28~": {Type: KeyF15, Alt: true}, // urxvt
+	"\x1b\x1b[29~": {Type: KeyF16, Alt: true}, // urxvt
+
+	"\x1b[15;2~": {Type: KeyF17},
+	"\x1b[17;2~": {Type: KeyF18},
+	"\x1b[18;2~": {Type: KeyF19},
+	"\x1b[19;2~": {Type: KeyF20},
+
+	"\x1b[31~": {Type: KeyF17},
+	"\x1b[32~": {Type: KeyF18},
+	"\x1b[33~": {Type: KeyF19},
+	"\x1b[34~": {Type: KeyF20},
+
+	"\x1b\x1b[31~": {Type: KeyF17, Alt: true}, // urxvt
+	"\x1b\x1b[32~": {Type: KeyF18, Alt: true}, // urxvt
+	"\x1b\x1b[33~": {Type: KeyF19, Alt: true}, // urxvt
+	"\x1b\x1b[34~": {Type: KeyF20, Alt: true}, // urxvt
+
+	// Powershell sequences.
+	"\x1bOA": {Type: KeyUp, Alt: false},
+	"\x1bOB": {Type: KeyDown, Alt: false},
+	"\x1bOC": {Type: KeyRight, Alt: false},
+	"\x1bOD": {Type: KeyLeft, Alt: false},
+}
+
+// readInputs reads keypress and mouse inputs from a TTY and returns messages
+// containing information about the key or mouse events accordingly.
+func readInputs(input io.Reader) ([]Msg, error) {
+	var buf [256]byte
+
+	// Read and block
+	numBytes, err := input.Read(buf[:])
+	if err != nil {
+		return nil, err
+	}
+	b := buf[:numBytes]
+	b, err = localereader.UTF8(b)
+	if err != nil {
+		return nil, err
+	}
+
+	// Check if it's a mouse event. For now we're parsing X10-type mouse events
+	// only.
+	mouseEvent, err := parseX10MouseEvents(b)
+	if err == nil {
+		var m []Msg
+		for _, v := range mouseEvent {
+			m = append(m, MouseMsg(v))
+		}
+		return m, nil
+	}
+
+	var runeSets [][]rune
+	var runes []rune
+
+	// Translate input into runes. In most cases we'll receive exactly one
+	// rune, but there are cases, particularly when an input method editor is
+	// used, where we can receive multiple runes at once.
+	for i, w := 0, 0; i < len(b); i += w {
+		r, width := utf8.DecodeRune(b[i:])
+		if r == utf8.RuneError {
+			return nil, errors.New("could not decode rune")
+		}
+
+		if r == '\x1b' && len(runes) > 1 {
+			// a new key sequence has started
+			runeSets = append(runeSets, runes)
+			runes = []rune{}
+		}
+
+		runes = append(runes, r)
+		w = width
+	}
+	// add the final set of runes we decoded
+	runeSets = append(runeSets, runes)
+
+	if len(runeSets) == 0 {
+		return nil, errors.New("received 0 runes from input")
+	}
+
+	var msgs []Msg
+	for _, runes := range runeSets {
+		// Is it a sequence, like an arrow key?
+		if k, ok := sequences[string(runes)]; ok {
+			msgs = append(msgs, KeyMsg(k))
+			continue
+		}
+
+		// Is this an unrecognized CSI sequence? If so, ignore it.
+		if len(runes) > 2 && runes[0] == 0x1b && (runes[1] == '[' ||
+			(len(runes) > 3 && runes[1] == 0x1b && runes[2] == '[')) {
+			continue
+		}
+
+		// Is the alt key pressed? If so, the buffer will be prefixed with an
+		// escape.
+		alt := false
+		if len(runes) > 1 && runes[0] == 0x1b {
+			alt = true
+			runes = runes[1:]
+		}
+
+		for _, v := range runes {
+			// Is the first rune a control character?
+			r := KeyType(v)
+			if r <= keyUS || r == keyDEL {
+				msgs = append(msgs, KeyMsg(Key{Type: r, Alt: alt}))
+				continue
+			}
+
+			// If it's a space, override the type with KeySpace (but still include
+			// the rune).
+			if r == ' ' {
+				msgs = append(msgs, KeyMsg(Key{Type: KeySpace, Runes: []rune{v}, Alt: alt}))
+				continue
+			}
+
+			// Welp, just regular, ol' runes.
+			msgs = append(msgs, KeyMsg(Key{Type: KeyRunes, Runes: []rune{v}, Alt: alt}))
+		}
+	}
+
+	return msgs, nil
+}

+ 52 - 0
vendor/github.com/charmbracelet/bubbletea/logging.go

@@ -0,0 +1,52 @@
+package tea
+
+import (
+	"io"
+	"log"
+	"os"
+	"unicode"
+)
+
+// LogToFile sets up default logging to log to a file. This is helpful as we
+// can't print to the terminal since our TUI is occupying it. If the file
+// doesn't exist it will be created.
+//
+// Don't forget to close the file when you're done with it.
+//
+//	  f, err := LogToFile("debug.log", "debug")
+//	  if err != nil {
+//			fmt.Println("fatal:", err)
+//			os.Exit(1)
+//	  }
+//	  defer f.Close()
+func LogToFile(path string, prefix string) (*os.File, error) {
+	return LogToFileWith(path, prefix, log.Default())
+}
+
+// LogOptionsSetter is an interface implemented by stdlib's log and charm's log
+// libraries.
+type LogOptionsSetter interface {
+	SetOutput(io.Writer)
+	SetPrefix(string)
+}
+
+// LogToFileWith does allows to call LogToFile with a custom LogOptionsSetter.
+func LogToFileWith(path string, prefix string, log LogOptionsSetter) (*os.File, error) {
+	f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644)
+	if err != nil {
+		return nil, err
+	}
+	log.SetOutput(f)
+
+	// Add a space after the prefix if a prefix is being specified and it
+	// doesn't already have a trailing space.
+	if len(prefix) > 0 {
+		finalChar := prefix[len(prefix)-1]
+		if !unicode.IsSpace(rune(finalChar)) {
+			prefix += " "
+		}
+	}
+	log.SetPrefix(prefix)
+
+	return f, nil
+}

+ 149 - 0
vendor/github.com/charmbracelet/bubbletea/mouse.go

@@ -0,0 +1,149 @@
+package tea
+
+import (
+	"bytes"
+	"errors"
+)
+
+// MouseMsg contains information about a mouse event and is sent to a program's
+// update function when mouse activity occurs. Note that the mouse must first
+// be enabled in order for the mouse events to be received.
+type MouseMsg MouseEvent
+
+// MouseEvent represents a mouse event, which could be a click, a scroll wheel
+// movement, a cursor movement, or a combination.
+type MouseEvent struct {
+	X    int
+	Y    int
+	Type MouseEventType
+	Alt  bool
+	Ctrl bool
+}
+
+// String returns a string representation of a mouse event.
+func (m MouseEvent) String() (s string) {
+	if m.Ctrl {
+		s += "ctrl+"
+	}
+	if m.Alt {
+		s += "alt+"
+	}
+	s += mouseEventTypes[m.Type]
+	return s
+}
+
+// MouseEventType indicates the type of mouse event occurring.
+type MouseEventType int
+
+// Mouse event types.
+const (
+	MouseUnknown MouseEventType = iota
+	MouseLeft
+	MouseRight
+	MouseMiddle
+	MouseRelease
+	MouseWheelUp
+	MouseWheelDown
+	MouseMotion
+)
+
+var mouseEventTypes = map[MouseEventType]string{
+	MouseUnknown:   "unknown",
+	MouseLeft:      "left",
+	MouseRight:     "right",
+	MouseMiddle:    "middle",
+	MouseRelease:   "release",
+	MouseWheelUp:   "wheel up",
+	MouseWheelDown: "wheel down",
+	MouseMotion:    "motion",
+}
+
+// Parse X10-encoded mouse events; the simplest kind. The last release of X10
+// was December 1986, by the way.
+//
+// X10 mouse events look like:
+//
+//	ESC [M Cb Cx Cy
+//
+// See: http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
+func parseX10MouseEvents(buf []byte) ([]MouseEvent, error) {
+	var r []MouseEvent
+
+	seq := []byte("\x1b[M")
+	if !bytes.Contains(buf, seq) {
+		return r, errors.New("not an X10 mouse event")
+	}
+
+	for _, v := range bytes.Split(buf, seq) {
+		if len(v) == 0 {
+			continue
+		}
+		if len(v) != 3 {
+			return r, errors.New("not an X10 mouse event")
+		}
+
+		var m MouseEvent
+		const byteOffset = 32
+		e := v[0] - byteOffset
+
+		const (
+			bitShift  = 0b0000_0100
+			bitAlt    = 0b0000_1000
+			bitCtrl   = 0b0001_0000
+			bitMotion = 0b0010_0000
+			bitWheel  = 0b0100_0000
+
+			bitsMask = 0b0000_0011
+
+			bitsLeft    = 0b0000_0000
+			bitsMiddle  = 0b0000_0001
+			bitsRight   = 0b0000_0010
+			bitsRelease = 0b0000_0011
+
+			bitsWheelUp   = 0b0000_0000
+			bitsWheelDown = 0b0000_0001
+		)
+
+		if e&bitWheel != 0 {
+			// Check the low two bits.
+			switch e & bitsMask {
+			case bitsWheelUp:
+				m.Type = MouseWheelUp
+			case bitsWheelDown:
+				m.Type = MouseWheelDown
+			}
+		} else {
+			// Check the low two bits.
+			// We do not separate clicking and dragging.
+			switch e & bitsMask {
+			case bitsLeft:
+				m.Type = MouseLeft
+			case bitsMiddle:
+				m.Type = MouseMiddle
+			case bitsRight:
+				m.Type = MouseRight
+			case bitsRelease:
+				if e&bitMotion != 0 {
+					m.Type = MouseMotion
+				} else {
+					m.Type = MouseRelease
+				}
+			}
+		}
+
+		if e&bitAlt != 0 {
+			m.Alt = true
+		}
+		if e&bitCtrl != 0 {
+			m.Ctrl = true
+		}
+
+		// (1,1) is the upper left. We subtract 1 to normalize it to (0,0).
+		m.X = int(v[1]) - byteOffset - 1
+		m.Y = int(v[2]) - byteOffset - 1
+
+		r = append(r, m)
+	}
+
+	return r, nil
+}

+ 19 - 0
vendor/github.com/charmbracelet/bubbletea/nil_renderer.go

@@ -0,0 +1,19 @@
+package tea
+
+type nilRenderer struct{}
+
+func (n nilRenderer) start()                  {}
+func (n nilRenderer) stop()                   {}
+func (n nilRenderer) kill()                   {}
+func (n nilRenderer) write(_ string)          {}
+func (n nilRenderer) repaint()                {}
+func (n nilRenderer) clearScreen()            {}
+func (n nilRenderer) altScreen() bool         { return false }
+func (n nilRenderer) enterAltScreen()         {}
+func (n nilRenderer) exitAltScreen()          {}
+func (n nilRenderer) showCursor()             {}
+func (n nilRenderer) hideCursor()             {}
+func (n nilRenderer) enableMouseCellMotion()  {}
+func (n nilRenderer) disableMouseCellMotion() {}
+func (n nilRenderer) enableMouseAllMotion()   {}
+func (n nilRenderer) disableMouseAllMotion()  {}

+ 202 - 0
vendor/github.com/charmbracelet/bubbletea/options.go

@@ -0,0 +1,202 @@
+package tea
+
+import (
+	"context"
+	"io"
+
+	"github.com/muesli/termenv"
+)
+
+// ProgramOption is used to set options when initializing a Program. Program can
+// accept a variable number of options.
+//
+// Example usage:
+//
+//	p := NewProgram(model, WithInput(someInput), WithOutput(someOutput))
+type ProgramOption func(*Program)
+
+// WithContext lets you specify a context in which to run the Program. This is
+// useful if you want to cancel the execution from outside. When a Program gets
+// cancelled it will exit with an error ErrProgramKilled.
+func WithContext(ctx context.Context) ProgramOption {
+	return func(p *Program) {
+		p.ctx = ctx
+	}
+}
+
+// WithOutput sets the output which, by default, is stdout. In most cases you
+// won't need to use this.
+func WithOutput(output io.Writer) ProgramOption {
+	return func(p *Program) {
+		if o, ok := output.(*termenv.Output); ok {
+			p.output = o
+		} else {
+			p.output = termenv.NewOutput(output, termenv.WithColorCache(true))
+		}
+	}
+}
+
+// WithInput sets the input which, by default, is stdin. In most cases you
+// won't need to use this. To disable input entirely pass nil.
+//
+//	p := NewProgram(model, WithInput(nil))
+func WithInput(input io.Reader) ProgramOption {
+	return func(p *Program) {
+		p.input = input
+		p.inputType = customInput
+	}
+}
+
+// WithInputTTY opens a new TTY for input (or console input device on Windows).
+func WithInputTTY() ProgramOption {
+	return func(p *Program) {
+		p.inputType = ttyInput
+	}
+}
+
+// WithoutSignalHandler disables the signal handler that Bubble Tea sets up for
+// Programs. This is useful if you want to handle signals yourself.
+func WithoutSignalHandler() ProgramOption {
+	return func(p *Program) {
+		p.startupOptions |= withoutSignalHandler
+	}
+}
+
+// WithoutCatchPanics disables the panic catching that Bubble Tea does by
+// default. If panic catching is disabled the terminal will be in a fairly
+// unusable state after a panic because Bubble Tea will not perform its usual
+// cleanup on exit.
+func WithoutCatchPanics() ProgramOption {
+	return func(p *Program) {
+		p.startupOptions |= withoutCatchPanics
+	}
+}
+
+// WithoutSignals will ignore OS signals.
+// This is mainly useful for testing.
+func WithoutSignals() ProgramOption {
+	return func(p *Program) {
+		p.ignoreSignals = true
+	}
+}
+
+// WithAltScreen starts the program with the alternate screen buffer enabled
+// (i.e. the program starts in full window mode). Note that the altscreen will
+// be automatically exited when the program quits.
+//
+// Example:
+//
+//	p := tea.NewProgram(Model{}, tea.WithAltScreen())
+//	if _, err := p.Run(); err != nil {
+//	    fmt.Println("Error running program:", err)
+//	    os.Exit(1)
+//	}
+//
+// To enter the altscreen once the program has already started running use the
+// EnterAltScreen command.
+func WithAltScreen() ProgramOption {
+	return func(p *Program) {
+		p.startupOptions |= withAltScreen
+	}
+}
+
+// WithMouseCellMotion starts the program with the mouse enabled in "cell
+// motion" mode.
+//
+// Cell motion mode enables mouse click, release, and wheel events. Mouse
+// movement events are also captured if a mouse button is pressed (i.e., drag
+// events). Cell motion mode is better supported than all motion mode.
+//
+// To enable mouse cell motion once the program has already started running use
+// the EnableMouseCellMotion command. To disable the mouse when the program is
+// running use the DisableMouse command.
+//
+// The mouse will be automatically disabled when the program exits.
+func WithMouseCellMotion() ProgramOption {
+	return func(p *Program) {
+		p.startupOptions |= withMouseCellMotion // set
+		p.startupOptions &^= withMouseAllMotion // clear
+	}
+}
+
+// WithMouseAllMotion starts the program with the mouse enabled in "all motion"
+// mode.
+//
+// EnableMouseAllMotion is a special command that enables mouse click, release,
+// wheel, and motion events, which are delivered regardless of whether a mouse
+// button is pressed, effectively enabling support for hover interactions.
+//
+// Many modern terminals support this, but not all. If in doubt, use
+// EnableMouseCellMotion instead.
+//
+// To enable the mouse once the program has already started running use the
+// EnableMouseAllMotion command. To disable the mouse when the program is
+// running use the DisableMouse command.
+//
+// The mouse will be automatically disabled when the program exits.
+func WithMouseAllMotion() ProgramOption {
+	return func(p *Program) {
+		p.startupOptions |= withMouseAllMotion   // set
+		p.startupOptions &^= withMouseCellMotion // clear
+	}
+}
+
+// WithoutRenderer disables the renderer. When this is set output and log
+// statements will be plainly sent to stdout (or another output if one is set)
+// without any rendering and redrawing logic. In other words, printing and
+// logging will behave the same way it would in a non-TUI commandline tool.
+// This can be useful if you want to use the Bubble Tea framework for a non-TUI
+// application, or to provide an additional non-TUI mode to your Bubble Tea
+// programs. For example, your program could behave like a daemon if output is
+// not a TTY.
+func WithoutRenderer() ProgramOption {
+	return func(p *Program) {
+		p.renderer = &nilRenderer{}
+	}
+}
+
+// WithANSICompressor removes redundant ANSI sequences to produce potentially
+// smaller output, at the cost of some processing overhead.
+//
+// This feature is provisional, and may be changed or removed in a future version
+// of this package.
+func WithANSICompressor() ProgramOption {
+	return func(p *Program) {
+		p.startupOptions |= withANSICompressor
+	}
+}
+
+// WithFilter supplies an event filter that will be invoked before Bubble Tea
+// processes a tea.Msg. The event filter can return any tea.Msg which will then
+// get handled by Bubble Tea instead of the original event. If the event filter
+// returns nil, the event will be ignored and Bubble Tea will not process it.
+//
+// As an example, this could be used to prevent a program from shutting down if
+// there are unsaved changes.
+//
+// Example:
+//
+//	func filter(m tea.Model, msg tea.Msg) tea.Msg {
+//		if _, ok := msg.(tea.QuitMsg); !ok {
+//			return msg
+//		}
+//
+//		model := m.(myModel)
+//		if model.hasChanges {
+//			return nil
+//		}
+//
+//		return msg
+//	}
+//
+//	p := tea.NewProgram(Model{}, tea.WithFilter(filter));
+//
+//	if _,err := p.Run(); err != nil {
+//		fmt.Println("Error running program:", err)
+//		os.Exit(1)
+//	}
+func WithFilter(filter func(Model, Msg) Msg) ProgramOption {
+	return func(p *Program) {
+		p.filter = filter
+	}
+}

+ 56 - 0
vendor/github.com/charmbracelet/bubbletea/renderer.go

@@ -0,0 +1,56 @@
+package tea
+
+// renderer is the interface for Bubble Tea renderers.
+type renderer interface {
+	// Start the renderer.
+	start()
+
+	// Stop the renderer, but render the final frame in the buffer, if any.
+	stop()
+
+	// Stop the renderer without doing any final rendering.
+	kill()
+
+	// Write a frame to the renderer. The renderer can write this data to
+	// output at its discretion.
+	write(string)
+
+	// Request a full re-render. Note that this will not trigger a render
+	// immediately. Rather, this method causes the next render to be a full
+	// repaint. Because of this, it's safe to call this method multiple times
+	// in succession.
+	repaint()
+
+	// Clears the terminal.
+	clearScreen()
+
+	// Whether or not the alternate screen buffer is enabled.
+	altScreen() bool
+	// Enable the alternate screen buffer.
+	enterAltScreen()
+	// Disable the alternate screen buffer.
+	exitAltScreen()
+
+	// Show the cursor.
+	showCursor()
+	// Hide the cursor.
+	hideCursor()
+
+	// enableMouseCellMotion enables mouse click, release, wheel and motion
+	// events if a mouse button is pressed (i.e., drag events).
+	enableMouseCellMotion()
+
+	// DisableMouseCellMotion disables Mouse Cell Motion tracking.
+	disableMouseCellMotion()
+
+	// EnableMouseAllMotion enables mouse click, release, wheel and motion
+	// events, regardless of whether a mouse button is pressed. Many modern
+	// terminals support this, but not all.
+	enableMouseAllMotion()
+
+	// DisableMouseAllMotion disables All Motion mouse tracking.
+	disableMouseAllMotion()
+}
+
+// repaintMsg forces a full repaint.
+type repaintMsg struct{}

+ 169 - 0
vendor/github.com/charmbracelet/bubbletea/screen.go

@@ -0,0 +1,169 @@
+package tea
+
+// WindowSizeMsg is used to report the terminal size. It's sent to Update once
+// initially and then on every terminal resize. Note that Windows does not
+// have support for reporting when resizes occur as it does not support the
+// SIGWINCH signal.
+type WindowSizeMsg struct {
+	Width  int
+	Height int
+}
+
+// ClearScreen is a special command that tells the program to clear the screen
+// before the next update. This can be used to move the cursor to the top left
+// of the screen and clear visual clutter when the alt screen is not in use.
+//
+// Note that it should never be necessary to call ClearScreen() for regular
+// redraws.
+func ClearScreen() Msg {
+	return clearScreenMsg{}
+}
+
+// clearScreenMsg is an internal message that signals to clear the screen.
+// You can send a clearScreenMsg with ClearScreen.
+type clearScreenMsg struct{}
+
+// EnterAltScreen is a special command that tells the Bubble Tea program to
+// enter the alternate screen buffer.
+//
+// Because commands run asynchronously, this command should not be used in your
+// model's Init function. To initialize your program with the altscreen enabled
+// use the WithAltScreen ProgramOption instead.
+func EnterAltScreen() Msg {
+	return enterAltScreenMsg{}
+}
+
+// enterAltScreenMsg in an internal message signals that the program should
+// enter alternate screen buffer. You can send a enterAltScreenMsg with
+// EnterAltScreen.
+type enterAltScreenMsg struct{}
+
+// ExitAltScreen is a special command that tells the Bubble Tea program to exit
+// the alternate screen buffer. This command should be used to exit the
+// alternate screen buffer while the program is running.
+//
+// Note that the alternate screen buffer will be automatically exited when the
+// program quits.
+func ExitAltScreen() Msg {
+	return exitAltScreenMsg{}
+}
+
+// exitAltScreenMsg in an internal message signals that the program should exit
+// alternate screen buffer. You can send a exitAltScreenMsg with ExitAltScreen.
+type exitAltScreenMsg struct{}
+
+// EnableMouseCellMotion is a special command that enables mouse click,
+// release, and wheel events. Mouse movement events are also captured if
+// a mouse button is pressed (i.e., drag events).
+//
+// Because commands run asynchronously, this command should not be used in your
+// model's Init function. Use the WithMouseCellMotion ProgramOption instead.
+func EnableMouseCellMotion() Msg {
+	return enableMouseCellMotionMsg{}
+}
+
+// enableMouseCellMotionMsg is a special command that signals to start
+// listening for "cell motion" type mouse events (ESC[?1002l). To send an
+// enableMouseCellMotionMsg, use the EnableMouseCellMotion command.
+type enableMouseCellMotionMsg struct{}
+
+// EnableMouseAllMotion is a special command that enables mouse click, release,
+// wheel, and motion events, which are delivered regardless of whether a mouse
+// button is pressed, effectively enabling support for hover interactions.
+//
+// Many modern terminals support this, but not all. If in doubt, use
+// EnableMouseCellMotion instead.
+//
+// Because commands run asynchronously, this command should not be used in your
+// model's Init function. Use the WithMouseAllMotion ProgramOption instead.
+func EnableMouseAllMotion() Msg {
+	return enableMouseAllMotionMsg{}
+}
+
+// enableMouseAllMotionMsg is a special command that signals to start listening
+// for "all motion" type mouse events (ESC[?1003l). To send an
+// enableMouseAllMotionMsg, use the EnableMouseAllMotion command.
+type enableMouseAllMotionMsg struct{}
+
+// DisableMouse is a special command that stops listening for mouse events.
+func DisableMouse() Msg {
+	return disableMouseMsg{}
+}
+
+// disableMouseMsg is an internal message that signals to stop listening
+// for mouse events. To send a disableMouseMsg, use the DisableMouse command.
+type disableMouseMsg struct{}
+
+// HideCursor is a special command for manually instructing Bubble Tea to hide
+// the cursor. In some rare cases, certain operations will cause the terminal
+// to show the cursor, which is normally hidden for the duration of a Bubble
+// Tea program's lifetime. You will most likely not need to use this command.
+func HideCursor() Msg {
+	return hideCursorMsg{}
+}
+
+// hideCursorMsg is an internal command used to hide the cursor. You can send
+// this message with HideCursor.
+type hideCursorMsg struct{}
+
+// ShowCursor is a special command for manually instructing Bubble Tea to show
+// the cursor.
+func ShowCursor() Msg {
+	return showCursorMsg{}
+}
+
+// showCursorMsg is an internal command used to show the cursor. You can send
+// this message with ShowCursor.
+type showCursorMsg struct{}
+
+// EnterAltScreen enters the alternate screen buffer, which consumes the entire
+// terminal window. ExitAltScreen will return the terminal to its former state.
+//
+// Deprecated: Use the WithAltScreen ProgramOption instead.
+func (p *Program) EnterAltScreen() {
+	if p.renderer != nil {
+		p.renderer.enterAltScreen()
+	}
+}
+
+// ExitAltScreen exits the alternate screen buffer.
+//
+// Deprecated: The altscreen will exited automatically when the program exits.
+func (p *Program) ExitAltScreen() {
+	if p.renderer != nil {
+		p.renderer.exitAltScreen()
+	}
+}
+
+// EnableMouseCellMotion enables mouse click, release, wheel and motion events
+// if a mouse button is pressed (i.e., drag events).
+//
+// Deprecated: Use the WithMouseCellMotion ProgramOption instead.
+func (p *Program) EnableMouseCellMotion() {
+	p.renderer.enableMouseCellMotion()
+}
+
+// DisableMouseCellMotion disables Mouse Cell Motion tracking. This will be
+// called automatically when exiting a Bubble Tea program.
+//
+// Deprecated: The mouse will automatically be disabled when the program exits.
+func (p *Program) DisableMouseCellMotion() {
+	p.renderer.disableMouseCellMotion()
+}
+
+// EnableMouseAllMotion enables mouse click, release, wheel and motion events,
+// regardless of whether a mouse button is pressed. Many modern terminals
+// support this, but not all.
+//
+// Deprecated: Use the WithMouseAllMotion ProgramOption instead.
+func (p *Program) EnableMouseAllMotion() {
+	p.renderer.enableMouseAllMotion()
+}
+
+// DisableMouseAllMotion disables All Motion mouse tracking. This will be
+// called automatically when exiting a Bubble Tea program.
+//
+// Deprecated: The mouse will automatically be disabled when the program exits.
+func (p *Program) DisableMouseAllMotion() {
+	p.renderer.disableMouseAllMotion()
+}

+ 33 - 0
vendor/github.com/charmbracelet/bubbletea/signals_unix.go

@@ -0,0 +1,33 @@
+//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || aix
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris aix
+
+package tea
+
+import (
+	"os"
+	"os/signal"
+	"syscall"
+)
+
+// listenForResize sends messages (or errors) when the terminal resizes.
+// Argument output should be the file descriptor for the terminal; usually
+// os.Stdout.
+func (p *Program) listenForResize(done chan struct{}) {
+	sig := make(chan os.Signal, 1)
+	signal.Notify(sig, syscall.SIGWINCH)
+
+	defer func() {
+		signal.Stop(sig)
+		close(done)
+	}()
+
+	for {
+		select {
+		case <-p.ctx.Done():
+			return
+		case <-sig:
+		}
+
+		p.checkResize()
+	}
+}

+ 10 - 0
vendor/github.com/charmbracelet/bubbletea/signals_windows.go

@@ -0,0 +1,10 @@
+//go:build windows
+// +build windows
+
+package tea
+
+// listenForResize is not available on windows because windows does not
+// implement syscall.SIGWINCH.
+func (p *Program) listenForResize(done chan struct{}) {
+	close(done)
+}

+ 657 - 0
vendor/github.com/charmbracelet/bubbletea/standard_renderer.go

@@ -0,0 +1,657 @@
+package tea
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/muesli/ansi/compressor"
+	"github.com/muesli/reflow/truncate"
+	"github.com/muesli/termenv"
+)
+
+const (
+	// defaultFramerate specifies the maximum interval at which we should
+	// update the view.
+	defaultFramerate = time.Second / 60
+)
+
+// standardRenderer is a framerate-based terminal renderer, updating the view
+// at a given framerate to avoid overloading the terminal emulator.
+//
+// In cases where very high performance is needed the renderer can be told
+// to exclude ranges of lines, allowing them to be written to directly.
+type standardRenderer struct {
+	mtx *sync.Mutex
+	out *termenv.Output
+
+	buf                bytes.Buffer
+	queuedMessageLines []string
+	framerate          time.Duration
+	ticker             *time.Ticker
+	done               chan struct{}
+	lastRender         string
+	linesRendered      int
+	useANSICompressor  bool
+	once               sync.Once
+
+	// cursor visibility state
+	cursorHidden bool
+
+	// essentially whether or not we're using the full size of the terminal
+	altScreenActive bool
+
+	// renderer dimensions; usually the size of the window
+	width  int
+	height int
+
+	// lines explicitly set not to render
+	ignoreLines map[int]struct{}
+}
+
+// newRenderer creates a new renderer. Normally you'll want to initialize it
+// with os.Stdout as the first argument.
+func newRenderer(out *termenv.Output, useANSICompressor bool) renderer {
+	r := &standardRenderer{
+		out:                out,
+		mtx:                &sync.Mutex{},
+		done:               make(chan struct{}),
+		framerate:          defaultFramerate,
+		useANSICompressor:  useANSICompressor,
+		queuedMessageLines: []string{},
+	}
+	if r.useANSICompressor {
+		r.out = termenv.NewOutput(&compressor.Writer{Forward: out})
+	}
+	return r
+}
+
+// start starts the renderer.
+func (r *standardRenderer) start() {
+	if r.ticker == nil {
+		r.ticker = time.NewTicker(r.framerate)
+	} else {
+		// If the ticker already exists, it has been stopped and we need to
+		// reset it.
+		r.ticker.Reset(r.framerate)
+	}
+
+	// Since the renderer can be restarted after a stop, we need to reset
+	// the done channel and its corresponding sync.Once.
+	r.once = sync.Once{}
+
+	go r.listen()
+}
+
+// stop permanently halts the renderer, rendering the final frame.
+func (r *standardRenderer) stop() {
+	// flush locks the mutex
+	r.flush()
+
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	r.out.ClearLine()
+	r.once.Do(func() {
+		r.done <- struct{}{}
+	})
+
+	if r.useANSICompressor {
+		if w, ok := r.out.TTY().(io.WriteCloser); ok {
+			_ = w.Close()
+		}
+	}
+}
+
+// kill halts the renderer. The final frame will not be rendered.
+func (r *standardRenderer) kill() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	r.out.ClearLine()
+	r.once.Do(func() {
+		r.done <- struct{}{}
+	})
+}
+
+// listen waits for ticks on the ticker, or a signal to stop the renderer.
+func (r *standardRenderer) listen() {
+	for {
+		select {
+		case <-r.done:
+			r.ticker.Stop()
+			return
+
+		case <-r.ticker.C:
+			r.flush()
+		}
+	}
+}
+
+// flush renders the buffer.
+func (r *standardRenderer) flush() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	if r.buf.Len() == 0 || r.buf.String() == r.lastRender {
+		// Nothing to do
+		return
+	}
+
+	// Output buffer
+	buf := &bytes.Buffer{}
+	out := termenv.NewOutput(buf)
+
+	newLines := strings.Split(r.buf.String(), "\n")
+
+	// If we know the output's height, we can use it to determine how many
+	// lines we can render. We drop lines from the top of the render buffer if
+	// necessary, as we can't navigate the cursor into the terminal's scrollback
+	// buffer.
+	if r.height > 0 && len(newLines) > r.height {
+		newLines = newLines[len(newLines)-r.height:]
+	}
+
+	numLinesThisFlush := len(newLines)
+	oldLines := strings.Split(r.lastRender, "\n")
+	skipLines := make(map[int]struct{})
+	flushQueuedMessages := len(r.queuedMessageLines) > 0 && !r.altScreenActive
+
+	// Add any queued messages to this render
+	if flushQueuedMessages {
+		newLines = append(r.queuedMessageLines, newLines...)
+		r.queuedMessageLines = []string{}
+	}
+
+	// Clear any lines we painted in the last render.
+	if r.linesRendered > 0 {
+		for i := r.linesRendered - 1; i > 0; i-- {
+			// If the number of lines we want to render hasn't increased and
+			// new line is the same as the old line we can skip rendering for
+			// this line as a performance optimization.
+			if (len(newLines) <= len(oldLines)) && (len(newLines) > i && len(oldLines) > i) && (newLines[i] == oldLines[i]) {
+				skipLines[i] = struct{}{}
+			} else if _, exists := r.ignoreLines[i]; !exists {
+				out.ClearLine()
+			}
+
+			out.CursorUp(1)
+		}
+
+		if _, exists := r.ignoreLines[0]; !exists {
+			// We need to return to the start of the line here to properly
+			// erase it. Going back the entire width of the terminal will
+			// usually be farther than we need to go, but terminal emulators
+			// will stop the cursor at the start of the line as a rule.
+			//
+			// We use this sequence in particular because it's part of the ANSI
+			// standard (whereas others are proprietary to, say, VT100/VT52).
+			// If cursor previous line (ESC[ + <n> + F) were better supported
+			// we could use that above to eliminate this step.
+			out.CursorBack(r.width)
+			out.ClearLine()
+		}
+	}
+
+	// Merge the set of lines we're skipping as a rendering optimization with
+	// the set of lines we've explicitly asked the renderer to ignore.
+	if r.ignoreLines != nil {
+		for k, v := range r.ignoreLines {
+			skipLines[k] = v
+		}
+	}
+
+	// Paint new lines
+	for i := 0; i < len(newLines); i++ {
+		if _, skip := skipLines[i]; skip {
+			// Unless this is the last line, move the cursor down.
+			if i < len(newLines)-1 {
+				out.CursorDown(1)
+			}
+		} else {
+			line := newLines[i]
+
+			// Truncate lines wider than the width of the window to avoid
+			// wrapping, which will mess up rendering. If we don't have the
+			// width of the window this will be ignored.
+			//
+			// Note that on Windows we only get the width of the window on
+			// program initialization, so after a resize this won't perform
+			// correctly (signal SIGWINCH is not supported on Windows).
+			if r.width > 0 {
+				line = truncate.String(line, uint(r.width))
+			}
+
+			_, _ = out.WriteString(line)
+
+			if i < len(newLines)-1 {
+				_, _ = out.WriteString("\r\n")
+			}
+		}
+	}
+	r.linesRendered = numLinesThisFlush
+
+	// Make sure the cursor is at the start of the last line to keep rendering
+	// behavior consistent.
+	if r.altScreenActive {
+		// This case fixes a bug in macOS terminal. In other terminals the
+		// other case seems to do the job regardless of whether or not we're
+		// using the full terminal window.
+		out.MoveCursor(r.linesRendered, 0)
+	} else {
+		out.CursorBack(r.width)
+	}
+
+	_, _ = r.out.Write(buf.Bytes())
+	r.lastRender = r.buf.String()
+	r.buf.Reset()
+}
+
+// write writes to the internal buffer. The buffer will be outputted via the
+// ticker which calls flush().
+func (r *standardRenderer) write(s string) {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+	r.buf.Reset()
+
+	// If an empty string was passed we should clear existing output and
+	// rendering nothing. Rather than introduce additional state to manage
+	// this, we render a single space as a simple (albeit less correct)
+	// solution.
+	if s == "" {
+		s = " "
+	}
+
+	_, _ = r.buf.WriteString(s)
+}
+
+func (r *standardRenderer) repaint() {
+	r.lastRender = ""
+}
+
+func (r *standardRenderer) clearScreen() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	r.out.ClearScreen()
+	r.out.MoveCursor(1, 1)
+
+	r.repaint()
+}
+
+func (r *standardRenderer) altScreen() bool {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	return r.altScreenActive
+}
+
+func (r *standardRenderer) enterAltScreen() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	if r.altScreenActive {
+		return
+	}
+
+	r.altScreenActive = true
+	r.out.AltScreen()
+
+	// Ensure that the terminal is cleared, even when it doesn't support
+	// alt screen (or alt screen support is disabled, like GNU screen by
+	// default).
+	//
+	// Note: we can't use r.clearScreen() here because the mutex is already
+	// locked.
+	r.out.ClearScreen()
+	r.out.MoveCursor(1, 1)
+
+	// cmd.exe and other terminals keep separate cursor states for the AltScreen
+	// and the main buffer. We have to explicitly reset the cursor visibility
+	// whenever we enter AltScreen.
+	if r.cursorHidden {
+		r.out.HideCursor()
+	} else {
+		r.out.ShowCursor()
+	}
+
+	r.repaint()
+}
+
+func (r *standardRenderer) exitAltScreen() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	if !r.altScreenActive {
+		return
+	}
+
+	r.altScreenActive = false
+	r.out.ExitAltScreen()
+
+	// cmd.exe and other terminals keep separate cursor states for the AltScreen
+	// and the main buffer. We have to explicitly reset the cursor visibility
+	// whenever we exit AltScreen.
+	if r.cursorHidden {
+		r.out.HideCursor()
+	} else {
+		r.out.ShowCursor()
+	}
+
+	r.repaint()
+}
+
+func (r *standardRenderer) showCursor() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	r.cursorHidden = false
+	r.out.ShowCursor()
+}
+
+func (r *standardRenderer) hideCursor() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	r.cursorHidden = true
+	r.out.HideCursor()
+}
+
+func (r *standardRenderer) enableMouseCellMotion() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	r.out.EnableMouseCellMotion()
+}
+
+func (r *standardRenderer) disableMouseCellMotion() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	r.out.DisableMouseCellMotion()
+}
+
+func (r *standardRenderer) enableMouseAllMotion() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	r.out.EnableMouseAllMotion()
+}
+
+func (r *standardRenderer) disableMouseAllMotion() {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	r.out.DisableMouseAllMotion()
+}
+
+// setIgnoredLines specifies lines not to be touched by the standard Bubble Tea
+// renderer.
+func (r *standardRenderer) setIgnoredLines(from int, to int) {
+	// Lock if we're going to be clearing some lines since we don't want
+	// anything jacking our cursor.
+	if r.linesRendered > 0 {
+		r.mtx.Lock()
+		defer r.mtx.Unlock()
+	}
+
+	if r.ignoreLines == nil {
+		r.ignoreLines = make(map[int]struct{})
+	}
+	for i := from; i < to; i++ {
+		r.ignoreLines[i] = struct{}{}
+	}
+
+	// Erase ignored lines
+	if r.linesRendered > 0 {
+		buf := &bytes.Buffer{}
+		out := termenv.NewOutput(buf)
+
+		for i := r.linesRendered - 1; i >= 0; i-- {
+			if _, exists := r.ignoreLines[i]; exists {
+				out.ClearLine()
+			}
+			out.CursorUp(1)
+		}
+		out.MoveCursor(r.linesRendered, 0) // put cursor back
+		_, _ = r.out.Write(buf.Bytes())
+	}
+}
+
+// clearIgnoredLines returns control of any ignored lines to the standard
+// Bubble Tea renderer. That is, any lines previously set to be ignored can be
+// rendered to again.
+func (r *standardRenderer) clearIgnoredLines() {
+	r.ignoreLines = nil
+}
+
+// insertTop effectively scrolls up. It inserts lines at the top of a given
+// area designated to be a scrollable region, pushing everything else down.
+// This is roughly how ncurses does it.
+//
+// To call this function use command ScrollUp().
+//
+// For this to work renderer.ignoreLines must be set to ignore the scrollable
+// region since we are bypassing the normal Bubble Tea renderer here.
+//
+// Because this method relies on the terminal dimensions, it's only valid for
+// full-window applications (generally those that use the alternate screen
+// buffer).
+//
+// This method bypasses the normal rendering buffer and is philosophically
+// different than the normal way we approach rendering in Bubble Tea. It's for
+// use in high-performance rendering, such as a pager that could potentially
+// be rendering very complicated ansi. In cases where the content is simpler
+// standard Bubble Tea rendering should suffice.
+func (r *standardRenderer) insertTop(lines []string, topBoundary, bottomBoundary int) {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	buf := &bytes.Buffer{}
+	out := termenv.NewOutput(buf)
+
+	out.ChangeScrollingRegion(topBoundary, bottomBoundary)
+	out.MoveCursor(topBoundary, 0)
+	out.InsertLines(len(lines))
+	_, _ = out.WriteString(strings.Join(lines, "\r\n"))
+	out.ChangeScrollingRegion(0, r.height)
+
+	// Move cursor back to where the main rendering routine expects it to be
+	out.MoveCursor(r.linesRendered, 0)
+
+	_, _ = r.out.Write(buf.Bytes())
+}
+
+// insertBottom effectively scrolls down. It inserts lines at the bottom of
+// a given area designated to be a scrollable region, pushing everything else
+// up. This is roughly how ncurses does it.
+//
+// To call this function use the command ScrollDown().
+//
+// See note in insertTop() for caveats, how this function only makes sense for
+// full-window applications, and how it differs from the normal way we do
+// rendering in Bubble Tea.
+func (r *standardRenderer) insertBottom(lines []string, topBoundary, bottomBoundary int) {
+	r.mtx.Lock()
+	defer r.mtx.Unlock()
+
+	buf := &bytes.Buffer{}
+	out := termenv.NewOutput(buf)
+
+	out.ChangeScrollingRegion(topBoundary, bottomBoundary)
+	out.MoveCursor(bottomBoundary, 0)
+	_, _ = out.WriteString("\r\n" + strings.Join(lines, "\r\n"))
+	out.ChangeScrollingRegion(0, r.height)
+
+	// Move cursor back to where the main rendering routine expects it to be
+	out.MoveCursor(r.linesRendered, 0)
+
+	_, _ = r.out.Write(buf.Bytes())
+}
+
+// handleMessages handles internal messages for the renderer.
+func (r *standardRenderer) handleMessages(msg Msg) {
+	switch msg := msg.(type) {
+	case repaintMsg:
+		// Force a repaint by clearing the render cache as we slide into a
+		// render.
+		r.mtx.Lock()
+		r.repaint()
+		r.mtx.Unlock()
+
+	case WindowSizeMsg:
+		r.mtx.Lock()
+		r.width = msg.Width
+		r.height = msg.Height
+		r.repaint()
+		r.mtx.Unlock()
+
+	case clearScrollAreaMsg:
+		r.clearIgnoredLines()
+
+		// Force a repaint on the area where the scrollable stuff was in this
+		// update cycle
+		r.mtx.Lock()
+		r.repaint()
+		r.mtx.Unlock()
+
+	case syncScrollAreaMsg:
+		// Re-render scrolling area
+		r.clearIgnoredLines()
+		r.setIgnoredLines(msg.topBoundary, msg.bottomBoundary)
+		r.insertTop(msg.lines, msg.topBoundary, msg.bottomBoundary)
+
+		// Force non-scrolling stuff to repaint in this update cycle
+		r.mtx.Lock()
+		r.repaint()
+		r.mtx.Unlock()
+
+	case scrollUpMsg:
+		r.insertTop(msg.lines, msg.topBoundary, msg.bottomBoundary)
+
+	case scrollDownMsg:
+		r.insertBottom(msg.lines, msg.topBoundary, msg.bottomBoundary)
+
+	case printLineMessage:
+		if !r.altScreenActive {
+			lines := strings.Split(msg.messageBody, "\n")
+			r.mtx.Lock()
+			r.queuedMessageLines = append(r.queuedMessageLines, lines...)
+			r.repaint()
+			r.mtx.Unlock()
+		}
+	}
+}
+
+// HIGH-PERFORMANCE RENDERING STUFF
+
+type syncScrollAreaMsg struct {
+	lines          []string
+	topBoundary    int
+	bottomBoundary int
+}
+
+// SyncScrollArea performs a paint of the entire region designated to be the
+// scrollable area. This is required to initialize the scrollable region and
+// should also be called on resize (WindowSizeMsg).
+//
+// For high-performance, scroll-based rendering only.
+func SyncScrollArea(lines []string, topBoundary int, bottomBoundary int) Cmd {
+	return func() Msg {
+		return syncScrollAreaMsg{
+			lines:          lines,
+			topBoundary:    topBoundary,
+			bottomBoundary: bottomBoundary,
+		}
+	}
+}
+
+type clearScrollAreaMsg struct{}
+
+// ClearScrollArea deallocates the scrollable region and returns the control of
+// those lines to the main rendering routine.
+//
+// For high-performance, scroll-based rendering only.
+func ClearScrollArea() Msg {
+	return clearScrollAreaMsg{}
+}
+
+type scrollUpMsg struct {
+	lines          []string
+	topBoundary    int
+	bottomBoundary int
+}
+
+// ScrollUp adds lines to the top of the scrollable region, pushing existing
+// lines below down. Lines that are pushed out the scrollable region disappear
+// from view.
+//
+// For high-performance, scroll-based rendering only.
+func ScrollUp(newLines []string, topBoundary, bottomBoundary int) Cmd {
+	return func() Msg {
+		return scrollUpMsg{
+			lines:          newLines,
+			topBoundary:    topBoundary,
+			bottomBoundary: bottomBoundary,
+		}
+	}
+}
+
+type scrollDownMsg struct {
+	lines          []string
+	topBoundary    int
+	bottomBoundary int
+}
+
+// ScrollDown adds lines to the bottom of the scrollable region, pushing
+// existing lines above up. Lines that are pushed out of the scrollable region
+// disappear from view.
+//
+// For high-performance, scroll-based rendering only.
+func ScrollDown(newLines []string, topBoundary, bottomBoundary int) Cmd {
+	return func() Msg {
+		return scrollDownMsg{
+			lines:          newLines,
+			topBoundary:    topBoundary,
+			bottomBoundary: bottomBoundary,
+		}
+	}
+}
+
+type printLineMessage struct {
+	messageBody string
+}
+
+// Println prints above the Program. This output is unmanaged by the program and
+// will persist across renders by the Program.
+//
+// Unlike fmt.Println (but similar to log.Println) the message will be print on
+// its own line.
+//
+// If the altscreen is active no output will be printed.
+func Println(args ...interface{}) Cmd {
+	return func() Msg {
+		return printLineMessage{
+			messageBody: fmt.Sprint(args...),
+		}
+	}
+}
+
+// Printf prints above the Program. It takes a format template followed by
+// values similar to fmt.Printf. This output is unmanaged by the program and
+// will persist across renders by the Program.
+//
+// Unlike fmt.Printf (but similar to log.Printf) the message will be print on
+// its own line.
+//
+// If the altscreen is active no output will be printed.
+func Printf(template string, args ...interface{}) Cmd {
+	return func() Msg {
+		return printLineMessage{
+			messageBody: fmt.Sprintf(template, args...),
+		}
+	}
+}

+ 691 - 0
vendor/github.com/charmbracelet/bubbletea/tea.go

@@ -0,0 +1,691 @@
+// Package tea provides a framework for building rich terminal user interfaces
+// based on the paradigms of The Elm Architecture. It's well-suited for simple
+// and complex terminal applications, either inline, full-window, or a mix of
+// both. It's been battle-tested in several large projects and is
+// production-ready.
+//
+// A tutorial is available at https://github.com/charmbracelet/bubbletea/tree/master/tutorials
+//
+// Example programs can be found at https://github.com/charmbracelet/bubbletea/tree/master/examples
+package tea
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"os/signal"
+	"runtime/debug"
+	"sync"
+	"syscall"
+
+	"github.com/containerd/console"
+	isatty "github.com/mattn/go-isatty"
+	"github.com/muesli/cancelreader"
+	"github.com/muesli/termenv"
+	"golang.org/x/sync/errgroup"
+)
+
+// ErrProgramKilled is returned by [Program.Run] when the program got killed.
+var ErrProgramKilled = errors.New("program was killed")
+
+// Msg contain data from the result of a IO operation. Msgs trigger the update
+// function and, henceforth, the UI.
+type Msg interface{}
+
+// Model contains the program's state as well as its core functions.
+type Model interface {
+	// Init is the first function that will be called. It returns an optional
+	// initial command. To not perform an initial command return nil.
+	Init() Cmd
+
+	// Update is called when a message is received. Use it to inspect messages
+	// and, in response, update the model and/or send a command.
+	Update(Msg) (Model, Cmd)
+
+	// View renders the program's UI, which is just a string. The view is
+	// rendered after every Update.
+	View() string
+}
+
+// Cmd is an IO operation that returns a message when it's complete. If it's
+// nil it's considered a no-op. Use it for things like HTTP requests, timers,
+// saving and loading from disk, and so on.
+//
+// Note that there's almost never a reason to use a command to send a message
+// to another part of your program. That can almost always be done in the
+// update function.
+type Cmd func() Msg
+
+type handlers []chan struct{}
+
+type inputType int
+
+const (
+	defaultInput inputType = iota
+	ttyInput
+	customInput
+)
+
+// String implements the stringer interface for [inputType]. It is inteded to
+// be used in testing.
+func (i inputType) String() string {
+	return [...]string{
+		"default input",
+		"tty input",
+		"custom input",
+	}[i]
+}
+
+// Options to customize the program during its initialization. These are
+// generally set with ProgramOptions.
+//
+// The options here are treated as bits.
+type startupOptions byte
+
+func (s startupOptions) has(option startupOptions) bool {
+	return s&option != 0
+}
+
+const (
+	withAltScreen startupOptions = 1 << iota
+	withMouseCellMotion
+	withMouseAllMotion
+	withANSICompressor
+	withoutSignalHandler
+
+	// Catching panics is incredibly useful for restoring the terminal to a
+	// usable state after a panic occurs. When this is set, Bubble Tea will
+	// recover from panics, print the stack trace, and disable raw mode. This
+	// feature is on by default.
+	withoutCatchPanics
+)
+
+// Program is a terminal user interface.
+type Program struct {
+	initialModel Model
+
+	// Configuration options that will set as the program is initializing,
+	// treated as bits. These options can be set via various ProgramOptions.
+	startupOptions startupOptions
+
+	inputType inputType
+
+	ctx    context.Context
+	cancel context.CancelFunc
+
+	msgs     chan Msg
+	errs     chan error
+	finished chan struct{}
+
+	// where to send output, this will usually be os.Stdout.
+	output        *termenv.Output
+	restoreOutput func() error
+	renderer      renderer
+
+	// where to read inputs from, this will usually be os.Stdin.
+	input        io.Reader
+	cancelReader cancelreader.CancelReader
+	readLoopDone chan struct{}
+	console      console.Console
+
+	// was the altscreen active before releasing the terminal?
+	altScreenWasActive bool
+	ignoreSignals      bool
+
+	// Stores the original reference to stdin for cases where input is not a
+	// TTY on windows and we've automatically opened CONIN$ to receive input.
+	// When the program exits this will be restored.
+	//
+	// Lint ignore note: the linter will find false positive on unix systems
+	// as this value only comes into play on Windows, hence the ignore comment
+	// below.
+	windowsStdin *os.File //nolint:golint,structcheck,unused
+
+	filter func(Model, Msg) Msg
+}
+
+// Quit is a special command that tells the Bubble Tea program to exit.
+func Quit() Msg {
+	return QuitMsg{}
+}
+
+// QuitMsg signals that the program should quit. You can send a QuitMsg with
+// Quit.
+type QuitMsg struct{}
+
+// NewProgram creates a new Program.
+func NewProgram(model Model, opts ...ProgramOption) *Program {
+	p := &Program{
+		initialModel: model,
+		msgs:         make(chan Msg),
+	}
+
+	// Apply all options to the program.
+	for _, opt := range opts {
+		opt(p)
+	}
+
+	// A context can be provided with a ProgramOption, but if none was provided
+	// we'll use the default background context.
+	if p.ctx == nil {
+		p.ctx = context.Background()
+	}
+	// Initialize context and teardown channel.
+	p.ctx, p.cancel = context.WithCancel(p.ctx)
+
+	// if no output was set, set it to stdout
+	if p.output == nil {
+		p.output = termenv.DefaultOutput()
+
+		// cache detected color values
+		termenv.WithColorCache(true)(p.output)
+	}
+
+	p.restoreOutput, _ = termenv.EnableVirtualTerminalProcessing(p.output)
+
+	return p
+}
+
+func (p *Program) handleSignals() chan struct{} {
+	ch := make(chan struct{})
+
+	// Listen for SIGINT and SIGTERM.
+	//
+	// In most cases ^C will not send an interrupt because the terminal will be
+	// in raw mode and ^C will be captured as a keystroke and sent along to
+	// Program.Update as a KeyMsg. When input is not a TTY, however, ^C will be
+	// caught here.
+	//
+	// SIGTERM is sent by unix utilities (like kill) to terminate a process.
+	go func() {
+		sig := make(chan os.Signal, 1)
+		signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
+		defer func() {
+			signal.Stop(sig)
+			close(ch)
+		}()
+
+		for {
+			select {
+			case <-p.ctx.Done():
+				return
+
+			case <-sig:
+				if !p.ignoreSignals {
+					p.msgs <- QuitMsg{}
+					return
+				}
+			}
+		}
+	}()
+
+	return ch
+}
+
+// handleResize handles terminal resize events.
+func (p *Program) handleResize() chan struct{} {
+	ch := make(chan struct{})
+
+	if f, ok := p.output.TTY().(*os.File); ok && isatty.IsTerminal(f.Fd()) {
+		// Get the initial terminal size and send it to the program.
+		go p.checkResize()
+
+		// Listen for window resizes.
+		go p.listenForResize(ch)
+	} else {
+		close(ch)
+	}
+
+	return ch
+}
+
+// handleCommands runs commands in a goroutine and sends the result to the
+// program's message channel.
+func (p *Program) handleCommands(cmds chan Cmd) chan struct{} {
+	ch := make(chan struct{})
+
+	go func() {
+		defer close(ch)
+
+		for {
+			select {
+			case <-p.ctx.Done():
+				return
+
+			case cmd := <-cmds:
+				if cmd == nil {
+					continue
+				}
+
+				// Don't wait on these goroutines, otherwise the shutdown
+				// latency would get too large as a Cmd can run for some time
+				// (e.g. tick commands that sleep for half a second). It's not
+				// possible to cancel them so we'll have to leak the goroutine
+				// until Cmd returns.
+				go func() {
+					msg := cmd() // this can be long.
+					p.Send(msg)
+				}()
+			}
+		}
+	}()
+
+	return ch
+}
+
+// eventLoop is the central message loop. It receives and handles the default
+// Bubble Tea messages, update the model and triggers redraws.
+func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
+	for {
+		select {
+		case <-p.ctx.Done():
+			return model, nil
+
+		case err := <-p.errs:
+			return model, err
+
+		case msg := <-p.msgs:
+			// Filter messages.
+			if p.filter != nil {
+				msg = p.filter(model, msg)
+			}
+			if msg == nil {
+				continue
+			}
+
+			// Handle special internal messages.
+			switch msg := msg.(type) {
+			case QuitMsg:
+				return model, nil
+
+			case clearScreenMsg:
+				p.renderer.clearScreen()
+
+			case enterAltScreenMsg:
+				p.renderer.enterAltScreen()
+
+			case exitAltScreenMsg:
+				p.renderer.exitAltScreen()
+
+			case enableMouseCellMotionMsg:
+				p.renderer.enableMouseCellMotion()
+
+			case enableMouseAllMotionMsg:
+				p.renderer.enableMouseAllMotion()
+
+			case disableMouseMsg:
+				p.renderer.disableMouseCellMotion()
+				p.renderer.disableMouseAllMotion()
+
+			case showCursorMsg:
+				p.renderer.showCursor()
+
+			case hideCursorMsg:
+				p.renderer.hideCursor()
+
+			case execMsg:
+				// NB: this blocks.
+				p.exec(msg.cmd, msg.fn)
+
+			case BatchMsg:
+				for _, cmd := range msg {
+					cmds <- cmd
+				}
+				continue
+
+			case sequenceMsg:
+				go func() {
+					// Execute commands one at a time, in order.
+					for _, cmd := range msg {
+						if cmd == nil {
+							continue
+						}
+
+						msg := cmd()
+						if batchMsg, ok := msg.(BatchMsg); ok {
+							g, _ := errgroup.WithContext(p.ctx)
+							for _, cmd := range batchMsg {
+								cmd := cmd
+								g.Go(func() error {
+									p.Send(cmd())
+									return nil
+								})
+							}
+
+							//nolint:errcheck
+							g.Wait() // wait for all commands from batch msg to finish
+							continue
+						}
+
+						p.Send(msg)
+					}
+				}()
+			}
+
+			// Process internal messages for the renderer.
+			if r, ok := p.renderer.(*standardRenderer); ok {
+				r.handleMessages(msg)
+			}
+
+			var cmd Cmd
+			model, cmd = model.Update(msg) // run update
+			cmds <- cmd                    // process command (if any)
+			p.renderer.write(model.View()) // send view to renderer
+		}
+	}
+}
+
+// Run initializes the program and runs its event loops, blocking until it gets
+// terminated by either [Program.Quit], [Program.Kill], or its signal handler.
+// Returns the final model.
+func (p *Program) Run() (Model, error) {
+	handlers := handlers{}
+	cmds := make(chan Cmd)
+	p.errs = make(chan error)
+	p.finished = make(chan struct{}, 1)
+
+	defer p.cancel()
+
+	switch p.inputType {
+	case defaultInput:
+		p.input = os.Stdin
+
+	case ttyInput:
+		// Open a new TTY, by request
+		f, err := openInputTTY()
+		if err != nil {
+			return p.initialModel, err
+		}
+		defer f.Close() //nolint:errcheck
+		p.input = f
+
+	case customInput:
+		// If the user hasn't set a custom input, and input's not a terminal,
+		// open a TTY so we can capture input as normal. This will allow things
+		// to "just work" in cases where data was piped or redirected into this
+		// application.
+		f, isFile := p.input.(*os.File)
+		if !isFile {
+			break
+		}
+		if isatty.IsTerminal(f.Fd()) {
+			break
+		}
+
+		f, err := openInputTTY()
+		if err != nil {
+			return p.initialModel, err
+		}
+		defer f.Close() //nolint:errcheck
+		p.input = f
+	}
+
+	// Handle signals.
+	if !p.startupOptions.has(withoutSignalHandler) {
+		handlers.add(p.handleSignals())
+	}
+
+	// Recover from panics.
+	if !p.startupOptions.has(withoutCatchPanics) {
+		defer func() {
+			if r := recover(); r != nil {
+				p.shutdown(true)
+				fmt.Printf("Caught panic:\n\n%s\n\nRestoring terminal...\n\n", r)
+				debug.PrintStack()
+				return
+			}
+		}()
+	}
+
+	// If no renderer is set use the standard one.
+	if p.renderer == nil {
+		p.renderer = newRenderer(p.output, p.startupOptions.has(withANSICompressor))
+	}
+
+	// Check if output is a TTY before entering raw mode, hiding the cursor and
+	// so on.
+	if err := p.initTerminal(); err != nil {
+		return p.initialModel, err
+	}
+
+	// Honor program startup options.
+	if p.startupOptions&withAltScreen != 0 {
+		p.renderer.enterAltScreen()
+	}
+	if p.startupOptions&withMouseCellMotion != 0 {
+		p.renderer.enableMouseCellMotion()
+	} else if p.startupOptions&withMouseAllMotion != 0 {
+		p.renderer.enableMouseAllMotion()
+	}
+
+	// Initialize the program.
+	model := p.initialModel
+	if initCmd := model.Init(); initCmd != nil {
+		ch := make(chan struct{})
+		handlers.add(ch)
+
+		go func() {
+			defer close(ch)
+
+			select {
+			case cmds <- initCmd:
+			case <-p.ctx.Done():
+			}
+		}()
+	}
+
+	// Start the renderer.
+	p.renderer.start()
+
+	// Render the initial view.
+	p.renderer.write(model.View())
+
+	// Subscribe to user input.
+	if p.input != nil {
+		if err := p.initCancelReader(); err != nil {
+			return model, err
+		}
+	}
+
+	// Handle resize events.
+	handlers.add(p.handleResize())
+
+	// Process commands.
+	handlers.add(p.handleCommands(cmds))
+
+	// Run event loop, handle updates and draw.
+	model, err := p.eventLoop(model, cmds)
+	killed := p.ctx.Err() != nil
+	if killed {
+		err = ErrProgramKilled
+	} else {
+		// Ensure we rendered the final state of the model.
+		p.renderer.write(model.View())
+	}
+
+	// Tear down.
+	p.cancel()
+
+	// Check if the cancel reader has been setup before waiting and closing.
+	if p.cancelReader != nil {
+		// Wait for input loop to finish.
+		if p.cancelReader.Cancel() {
+			p.waitForReadLoop()
+		}
+		_ = p.cancelReader.Close()
+	}
+
+	// Wait for all handlers to finish.
+	handlers.shutdown()
+
+	// Restore terminal state.
+	p.shutdown(killed)
+
+	return model, err
+}
+
+// StartReturningModel initializes the program and runs its event loops,
+// blocking until it gets terminated by either [Program.Quit], [Program.Kill],
+// or its signal handler. Returns the final model.
+//
+// Deprecated: please use [Program.Run] instead.
+func (p *Program) StartReturningModel() (Model, error) {
+	return p.Run()
+}
+
+// Start initializes the program and runs its event loops, blocking until it
+// gets terminated by either [Program.Quit], [Program.Kill], or its signal
+// handler.
+//
+// Deprecated: please use [Program.Run] instead.
+func (p *Program) Start() error {
+	_, err := p.Run()
+	return err
+}
+
+// Send sends a message to the main update function, effectively allowing
+// messages to be injected from outside the program for interoperability
+// purposes.
+//
+// If the program hasn't started yet this will be a blocking operation.
+// If the program has already been terminated this will be a no-op, so it's safe
+// to send messages after the program has exited.
+func (p *Program) Send(msg Msg) {
+	select {
+	case <-p.ctx.Done():
+	case p.msgs <- msg:
+	}
+}
+
+// Quit is a convenience function for quitting Bubble Tea programs. Use it
+// when you need to shut down a Bubble Tea program from the outside.
+//
+// If you wish to quit from within a Bubble Tea program use the Quit command.
+//
+// If the program is not running this will be a no-op, so it's safe to call
+// if the program is unstarted or has already exited.
+func (p *Program) Quit() {
+	p.Send(Quit())
+}
+
+// Kill stops the program immediately and restores the former terminal state.
+// The final render that you would normally see when quitting will be skipped.
+// [program.Run] returns a [ErrProgramKilled] error.
+func (p *Program) Kill() {
+	p.cancel()
+}
+
+// Wait waits/blocks until the underlying Program finished shutting down.
+func (p *Program) Wait() {
+	<-p.finished
+}
+
+// shutdown performs operations to free up resources and restore the terminal
+// to its original state.
+func (p *Program) shutdown(kill bool) {
+	if p.renderer != nil {
+		if kill {
+			p.renderer.kill()
+		} else {
+			p.renderer.stop()
+		}
+	}
+
+	_ = p.restoreTerminalState()
+	if p.restoreOutput != nil {
+		_ = p.restoreOutput()
+	}
+	p.finished <- struct{}{}
+}
+
+// ReleaseTerminal restores the original terminal state and cancels the input
+// reader. You can return control to the Program with RestoreTerminal.
+func (p *Program) ReleaseTerminal() error {
+	p.ignoreSignals = true
+	p.cancelReader.Cancel()
+	p.waitForReadLoop()
+
+	if p.renderer != nil {
+		p.renderer.stop()
+	}
+
+	p.altScreenWasActive = p.renderer.altScreen()
+	return p.restoreTerminalState()
+}
+
+// RestoreTerminal reinitializes the Program's input reader, restores the
+// terminal to the former state when the program was running, and repaints.
+// Use it to reinitialize a Program after running ReleaseTerminal.
+func (p *Program) RestoreTerminal() error {
+	p.ignoreSignals = false
+
+	if err := p.initTerminal(); err != nil {
+		return err
+	}
+	if err := p.initCancelReader(); err != nil {
+		return err
+	}
+
+	if p.altScreenWasActive {
+		p.renderer.enterAltScreen()
+	} else {
+		// entering alt screen already causes a repaint.
+		go p.Send(repaintMsg{})
+	}
+	if p.renderer != nil {
+		p.renderer.start()
+	}
+
+	// If the output is a terminal, it may have been resized while another
+	// process was at the foreground, in which case we may not have received
+	// SIGWINCH. Detect any size change now and propagate the new size as
+	// needed.
+	go p.checkResize()
+
+	return nil
+}
+
+// Println prints above the Program. This output is unmanaged by the program
+// and will persist across renders by the Program.
+//
+// If the altscreen is active no output will be printed.
+func (p *Program) Println(args ...interface{}) {
+	p.msgs <- printLineMessage{
+		messageBody: fmt.Sprint(args...),
+	}
+}
+
+// Printf prints above the Program. It takes a format template followed by
+// values similar to fmt.Printf. This output is unmanaged by the program and
+// will persist across renders by the Program.
+//
+// Unlike fmt.Printf (but similar to log.Printf) the message will be print on
+// its own line.
+//
+// If the altscreen is active no output will be printed.
+func (p *Program) Printf(template string, args ...interface{}) {
+	p.msgs <- printLineMessage{
+		messageBody: fmt.Sprintf(template, args...),
+	}
+}
+
+// Adds a handler to the list of handlers. We wait for all handlers to terminate
+// gracefully on shutdown.
+func (h *handlers) add(ch chan struct{}) {
+	*h = append(*h, ch)
+}
+
+// Shutdown waits for all handlers to terminate.
+func (h handlers) shutdown() {
+	var wg sync.WaitGroup
+	for _, ch := range h {
+		wg.Add(1)
+		go func(ch chan struct{}) {
+			<-ch
+			wg.Done()
+		}(ch)
+	}
+	wg.Wait()
+}

+ 131 - 0
vendor/github.com/charmbracelet/bubbletea/tty.go

@@ -0,0 +1,131 @@
+package tea
+
+import (
+	"errors"
+	"io"
+	"os"
+	"time"
+
+	isatty "github.com/mattn/go-isatty"
+	"github.com/muesli/cancelreader"
+	"golang.org/x/term"
+)
+
+func (p *Program) initTerminal() error {
+	err := p.initInput()
+	if err != nil {
+		return err
+	}
+
+	if p.console != nil {
+		err = p.console.SetRaw()
+		if err != nil {
+			return err
+		}
+	}
+
+	p.renderer.hideCursor()
+	return nil
+}
+
+// restoreTerminalState restores the terminal to the state prior to running the
+// Bubble Tea program.
+func (p *Program) restoreTerminalState() error {
+	if p.renderer != nil {
+		p.renderer.showCursor()
+		p.renderer.disableMouseCellMotion()
+		p.renderer.disableMouseAllMotion()
+
+		if p.renderer.altScreen() {
+			p.renderer.exitAltScreen()
+
+			// give the terminal a moment to catch up
+			time.Sleep(time.Millisecond * 10)
+		}
+	}
+
+	if p.console != nil {
+		err := p.console.Reset()
+		if err != nil {
+			return err
+		}
+	}
+
+	return p.restoreInput()
+}
+
+// initCancelReader (re)commences reading inputs.
+func (p *Program) initCancelReader() error {
+	var err error
+	p.cancelReader, err = cancelreader.NewReader(p.input)
+	if err != nil {
+		return err
+	}
+
+	p.readLoopDone = make(chan struct{})
+	go p.readLoop()
+
+	return nil
+}
+
+func (p *Program) readLoop() {
+	defer close(p.readLoopDone)
+
+	for {
+		if p.ctx.Err() != nil {
+			return
+		}
+
+		msgs, err := readInputs(p.cancelReader)
+		if err != nil {
+			if !errors.Is(err, io.EOF) && !errors.Is(err, cancelreader.ErrCanceled) {
+				select {
+				case <-p.ctx.Done():
+				case p.errs <- err:
+				}
+			}
+
+			return
+		}
+
+		for _, msg := range msgs {
+			p.msgs <- msg
+		}
+	}
+}
+
+// waitForReadLoop waits for the cancelReader to finish its read loop.
+func (p *Program) waitForReadLoop() {
+	select {
+	case <-p.readLoopDone:
+	case <-time.After(500 * time.Millisecond):
+		// The read loop hangs, which means the input
+		// cancelReader's cancel function has returned true even
+		// though it was not able to cancel the read.
+	}
+}
+
+// checkResize detects the current size of the output and informs the program
+// via a WindowSizeMsg.
+func (p *Program) checkResize() {
+	f, ok := p.output.TTY().(*os.File)
+	if !ok || !isatty.IsTerminal(f.Fd()) {
+		// can't query window size
+		return
+	}
+
+	w, h, err := term.GetSize(int(f.Fd()))
+	if err != nil {
+		select {
+		case <-p.ctx.Done():
+		case p.errs <- err:
+		}
+
+		return
+	}
+
+	p.Send(WindowSizeMsg{
+		Width:  w,
+		Height: h,
+	})
+}

+ 42 - 0
vendor/github.com/charmbracelet/bubbletea/tty_unix.go

@@ -0,0 +1,42 @@
+//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || aix
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris aix
+
+package tea
+
+import (
+	"os"
+
+	"github.com/containerd/console"
+)
+
+func (p *Program) initInput() error {
+	// If input's a file, use console to manage it
+	if f, ok := p.input.(*os.File); ok {
+		c, err := console.ConsoleFromFile(f)
+		if err != nil {
+			return nil //nolint:nilerr // ignore error, this was just a test
+		}
+		p.console = c
+	}
+
+	return nil
+}
+
+// On unix systems, RestoreInput closes any TTYs we opened for input. Note that
+// we don't do this on Windows as it causes the prompt to not be drawn until
+// the terminal receives a keypress rather than appearing promptly after the
+// program exits.
+func (p *Program) restoreInput() error {
+	if p.console != nil {
+		return p.console.Reset()
+	}
+	return nil
+}
+
+func openInputTTY() (*os.File, error) {
+	f, err := os.Open("/dev/tty")
+	if err != nil {
+		return nil, err
+	}
+	return f, nil
+}

+ 47 - 0
vendor/github.com/charmbracelet/bubbletea/tty_windows.go

@@ -0,0 +1,47 @@
+//go:build windows
+// +build windows
+
+package tea
+
+import (
+	"os"
+
+	"github.com/containerd/console"
+)
+
+func (p *Program) initInput() error {
+	// If input's a file, use console to manage it
+	if f, ok := p.input.(*os.File); ok {
+		// Save a reference to the current stdin then replace stdin with our
+		// input. We do this so we can hand input off to containerd/console to
+		// set raw mode, and do it in this fashion because the method
+		// console.ConsoleFromFile isn't supported on Windows.
+		p.windowsStdin = os.Stdin
+		os.Stdin = f
+
+		// Note: this will panic if it fails.
+		c := console.Current()
+		p.console = c
+	}
+
+	return nil
+}
+
+// restoreInput restores stdout in the event that we placed it aside to handle
+// input with CONIN$, above.
+func (p *Program) restoreInput() error {
+	if p.windowsStdin != nil {
+		os.Stdin = p.windowsStdin
+	}
+
+	return nil
+}
+
+// Open the Windows equivalent of a TTY.
+func openInputTTY() (*os.File, error) {
+	f, err := os.OpenFile("CONIN$", os.O_RDWR, 0644)
+	if err != nil {
+		return nil, err
+	}
+	return f, nil
+}

+ 1 - 0
vendor/github.com/charmbracelet/lipgloss/.gitignore

@@ -0,0 +1 @@
+ssh_example_ed25519*

+ 47 - 0
vendor/github.com/charmbracelet/lipgloss/.golangci-soft.yml

@@ -0,0 +1,47 @@
+run:
+  tests: false
+
+issues:
+  include:
+    - EXC0001
+    - EXC0005
+    - EXC0011
+    - EXC0012
+    - EXC0013
+
+  max-issues-per-linter: 0
+  max-same-issues: 0
+
+linters:
+  enable:
+    # - dupl
+    - exhaustive
+    # - exhaustivestruct
+    - goconst
+    - godot
+    - godox
+    - gomnd
+    - gomoddirectives
+    - goprintffuncname
+    - ifshort
+    # - lll
+    - misspell
+    - nakedret
+    - nestif
+    - noctx
+    - nolintlint
+    - prealloc
+    - wrapcheck
+
+  # disable default linters, they are already enabled in .golangci.yml
+  disable:
+    - deadcode
+    - errcheck
+    - gosimple
+    - govet
+    - ineffassign
+    - staticcheck
+    - structcheck
+    - typecheck
+    - unused
+    - varcheck

+ 29 - 0
vendor/github.com/charmbracelet/lipgloss/.golangci.yml

@@ -0,0 +1,29 @@
+run:
+  tests: false
+
+issues:
+  include:
+    - EXC0001
+    - EXC0005
+    - EXC0011
+    - EXC0012
+    - EXC0013
+
+  max-issues-per-linter: 0
+  max-same-issues: 0
+
+linters:
+  enable:
+    - bodyclose
+    - exportloopref
+    - goimports
+    - gosec
+    - nilerr
+    - predeclared
+    - revive
+    - rowserrcheck
+    - sqlclosecheck
+    - tparallel
+    - unconvert
+    - unparam
+    - whitespace

+ 21 - 0
vendor/github.com/charmbracelet/lipgloss/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Charmbracelet, Inc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 458 - 0
vendor/github.com/charmbracelet/lipgloss/README.md

@@ -0,0 +1,458 @@
+Lip Gloss
+=========
+
+<p>
+    <img src="https://stuff.charm.sh/lipgloss/lipgloss-header-github.png" width="340" alt="Lip Gloss Title Treatment"><br>
+    <a href="https://github.com/charmbracelet/lipgloss/releases"><img src="https://img.shields.io/github/release/charmbracelet/lipgloss.svg" alt="Latest Release"></a>
+    <a href="https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
+    <a href="https://github.com/charmbracelet/lipgloss/actions"><img src="https://github.com/charmbracelet/lipgloss/workflows/build/badge.svg" alt="Build Status"></a>
+</p>
+
+Style definitions for nice terminal layouts. Built with TUIs in mind.
+
+![Lip Gloss example](https://stuff.charm.sh/lipgloss/lipgloss-example.png)
+
+Lip Gloss takes an expressive, declarative approach to terminal rendering.
+Users familiar with CSS will feel at home with Lip Gloss.
+
+```go
+
+import "github.com/charmbracelet/lipgloss"
+
+var style = lipgloss.NewStyle().
+    Bold(true).
+    Foreground(lipgloss.Color("#FAFAFA")).
+    Background(lipgloss.Color("#7D56F4")).
+    PaddingTop(2).
+    PaddingLeft(4).
+    Width(22)
+
+fmt.Println(style.Render("Hello, kitty"))
+```
+
+## Colors
+
+Lip Gloss supports the following color profiles:
+
+### ANSI 16 colors (4-bit)
+
+```go
+lipgloss.Color("5")  // magenta
+lipgloss.Color("9")  // red
+lipgloss.Color("12") // light blue
+```
+
+### ANSI 256 Colors (8-bit)
+
+```go
+lipgloss.Color("86")  // aqua
+lipgloss.Color("201") // hot pink
+lipgloss.Color("202") // orange
+```
+
+### True Color (16,777,216 colors; 24-bit)
+
+```go
+lipgloss.Color("#0000FF") // good ol' 100% blue
+lipgloss.Color("#04B575") // a green
+lipgloss.Color("#3C3C3C") // a dark gray
+```
+
+...as well as a 1-bit ASCII profile, which is black and white only.
+
+The terminal's color profile will be automatically detected, and colors outside
+the gamut of the current palette will be automatically coerced to their closest
+available value.
+
+
+### Adaptive Colors
+
+You can also specify color options for light and dark backgrounds:
+
+```go
+lipgloss.AdaptiveColor{Light: "236", Dark: "248"}
+```
+
+The terminal's background color will automatically be detected and the
+appropriate color will be chosen at runtime.
+
+### Complete Colors
+
+CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
+profiles.
+
+```go
+lipgloss.CompleteColor{True: "#0000FF", ANSI256: "86", ANSI: "5"}
+```
+
+Automatic color degradation will not be performed in this case and it will be
+based on the color specified.
+
+### Complete Adaptive Colors
+
+You can use CompleteColor with AdaptiveColor to specify the exact values for
+light and dark backgrounds without automatic color degradation.
+
+```go
+lipgloss.CompleteAdaptiveColor{
+    Light: CompleteColor{TrueColor: "#d7ffae", ANSI256: "193", ANSI: "11"},
+    Dark:  CompleteColor{TrueColor: "#d75fee", ANSI256: "163", ANSI: "5"},
+}
+```
+
+## Inline Formatting
+
+Lip Gloss supports the usual ANSI text formatting options:
+
+```go
+var style = lipgloss.NewStyle().
+    Bold(true).
+    Italic(true).
+    Faint(true).
+    Blink(true).
+    Strikethrough(true).
+    Underline(true).
+    Reverse(true)
+```
+
+
+## Block-Level Formatting
+
+Lip Gloss also supports rules for block-level formatting:
+
+```go
+// Padding
+var style = lipgloss.NewStyle().
+    PaddingTop(2).
+    PaddingRight(4).
+    PaddingBottom(2).
+    PaddingLeft(4)
+
+// Margins
+var style = lipgloss.NewStyle().
+    MarginTop(2).
+    MarginRight(4).
+    MarginBottom(2).
+    MarginLeft(4)
+```
+
+There is also shorthand syntax for margins and padding, which follows the same
+format as CSS:
+
+```go
+// 2 cells on all sides
+lipgloss.NewStyle().Padding(2)
+
+// 2 cells on the top and bottom, 4 cells on the left and right
+lipgloss.NewStyle().Margin(2, 4)
+
+// 1 cell on the top, 4 cells on the sides, 2 cells on the bottom
+lipgloss.NewStyle().Padding(1, 4, 2)
+
+// Clockwise, starting from the top: 2 cells on the top, 4 on the right, 3 on
+// the bottom, and 1 on the left
+lipgloss.NewStyle().Margin(2, 4, 3, 1)
+```
+
+
+## Aligning Text
+
+You can align paragraphs of text to the left, right, or center.
+
+```go
+var style = lipgloss.NewStyle().
+    Width(24).
+    Align(lipgloss.Left).  // align it left
+    Align(lipgloss.Right). // no wait, align it right
+    Align(lipgloss.Center) // just kidding, align it in the center
+```
+
+
+## Width and Height
+
+Setting a minimum width and height is simple and straightforward.
+
+```go
+var style = lipgloss.NewStyle().
+    SetString("What’s for lunch?").
+    Width(24).
+    Height(32).
+    Foreground(lipgloss.Color("63"))
+```
+
+
+## Borders
+
+Adding borders is easy:
+
+```go
+// Add a purple, rectangular border
+var style = lipgloss.NewStyle().
+    BorderStyle(lipgloss.NormalBorder()).
+    BorderForeground(lipgloss.Color("63"))
+
+// Set a rounded, yellow-on-purple border to the top and left
+var anotherStyle = lipgloss.NewStyle().
+    BorderStyle(lipgloss.RoundedBorder()).
+    BorderForeground(lipgloss.Color("228")).
+    BorderBackground(lipgloss.Color("63")).
+    BorderTop(true).
+    BorderLeft(true)
+
+// Make your own border
+var myCuteBorder = lipgloss.Border{
+    Top:         "._.:*:",
+    Bottom:      "._.:*:",
+    Left:        "|*",
+    Right:       "|*",
+    TopLeft:     "*",
+    TopRight:    "*",
+    BottomLeft:  "*",
+    BottomRight: "*",
+}
+```
+
+There are also shorthand functions for defining borders, which follow a similar
+pattern to the margin and padding shorthand functions.
+
+```go
+// Add a thick border to the top and bottom
+lipgloss.NewStyle().
+    Border(lipgloss.ThickBorder(), true, false)
+
+// Add a thick border to the right and bottom sides. Rules are set clockwise
+// from top.
+lipgloss.NewStyle().
+    Border(lipgloss.DoubleBorder(), true, false, false, true)
+```
+
+For more on borders see [the docs][docs].
+
+
+## Copying Styles
+
+Just use `Copy()`:
+
+```go
+var style = lipgloss.NewStyle().Foreground(lipgloss.Color("219"))
+
+var wildStyle = style.Copy().Blink(true)
+```
+
+`Copy()` performs a copy on the underlying data structure ensuring that you get
+a true, dereferenced copy of a style. Without copying, it's possible to mutate
+styles.
+
+
+## Inheritance
+
+Styles can inherit rules from other styles. When inheriting, only unset rules
+on the receiver are inherited.
+
+```go
+var styleA = lipgloss.NewStyle().
+    Foreground(lipgloss.Color("229")).
+    Background(lipgloss.Color("63"))
+
+// Only the background color will be inherited here, because the foreground
+// color will have been already set:
+var styleB = lipgloss.NewStyle().
+    Foreground(lipgloss.Color("201")).
+    Inherit(styleA)
+```
+
+
+## Unsetting Rules
+
+All rules can be unset:
+
+```go
+var style = lipgloss.NewStyle().
+    Bold(true).                        // make it bold
+    UnsetBold().                       // jk don't make it bold
+    Background(lipgloss.Color("227")). // yellow background
+    UnsetBackground()                  // never mind
+```
+
+When a rule is unset, it won't be inherited or copied.
+
+
+## Enforcing Rules
+
+Sometimes, such as when developing a component, you want to make sure style
+definitions respect their intended purpose in the UI. This is where `Inline`
+and `MaxWidth`, and `MaxHeight` come in:
+
+```go
+// Force rendering onto a single line, ignoring margins, padding, and borders.
+someStyle.Inline(true).Render("yadda yadda")
+
+// Also limit rendering to five cells
+someStyle.Inline(true).MaxWidth(5).Render("yadda yadda")
+
+// Limit rendering to a 5x5 cell block
+someStyle.MaxWidth(5).MaxHeight(5).Render("yadda yadda")
+```
+
+## Rendering
+
+Generally, you just call the `Render(string...)` method on a `lipgloss.Style`:
+
+```go
+style := lipgloss.NewStyle().Bold(true).SetString("Hello,")
+fmt.Println(style.Render("kitty.")) // Hello, kitty.
+fmt.Println(style.Render("puppy.")) // Hello, puppy.
+```
+
+But you could also use the Stringer interface:
+
+```go
+var style = lipgloss.NewStyle().SetString("你好,猫咪。").Bold(true)
+fmt.Println(style) // 你好,猫咪。
+```
+
+### Custom Renderers
+
+Custom renderers allow you to render to a specific outputs. This is
+particularly important when you want to render to different outputs and
+correctly detect the color profile and dark background status for each, such as
+in a server-client situation.
+
+```go
+func myLittleHandler(sess ssh.Session) {
+    // Create a renderer for the client.
+    renderer := lipgloss.NewRenderer(sess)
+
+    // Create a new style on the renderer.
+    style := renderer.NewStyle().Background(lipgloss.AdaptiveColor{Light: "63", Dark: "228"})
+
+    // Render. The color profile and dark background state will be correctly detected.
+    io.WriteString(sess, style.Render("Heyyyyyyy"))
+}
+```
+
+For an example on using a custom renderer over SSH with [Wish][wish] see the
+[SSH example][ssh-example].
+
+## Utilities
+
+In addition to pure styling, Lip Gloss also ships with some utilities to help
+assemble your layouts.
+
+
+### Joining Paragraphs
+
+Horizontally and vertically joining paragraphs is a cinch.
+
+```go
+// Horizontally join three paragraphs along their bottom edges
+lipgloss.JoinHorizontal(lipgloss.Bottom, paragraphA, paragraphB, paragraphC)
+
+// Vertically join two paragraphs along their center axes
+lipgloss.JoinVertical(lipgloss.Center, paragraphA, paragraphB)
+
+// Horizontally join three paragraphs, with the shorter ones aligning 20%
+// from the top of the tallest
+lipgloss.JoinHorizontal(0.2, paragraphA, paragraphB, paragraphC)
+```
+
+
+### Measuring Width and Height
+
+Sometimes you’ll want to know the width and height of text blocks when building
+your layouts.
+
+```go
+// Render a block of text.
+var style = lipgloss.NewStyle().
+    Width(40).
+    Padding(2)
+var block string = style.Render(someLongString)
+
+// Get the actual, physical dimensions of the text block.
+width := lipgloss.Width(block)
+height := lipgloss.Height(block)
+
+// Here's a shorthand function.
+w, h := lipgloss.Size(block)
+```
+
+
+### Placing Text in Whitespace
+
+Sometimes you’ll simply want to place a block of text in whitespace.
+
+```go
+// Center a paragraph horizontally in a space 80 cells wide. The height of
+// the block returned will be as tall as the input paragraph.
+block := lipgloss.PlaceHorizontal(80, lipgloss.Center, fancyStyledParagraph)
+
+// Place a paragraph at the bottom of a space 30 cells tall. The width of
+// the text block returned will be as wide as the input paragraph.
+block := lipgloss.PlaceVertical(30, lipgloss.Bottom, fancyStyledParagraph)
+
+// Place a paragraph in the bottom right corner of a 30x80 cell space.
+block := lipgloss.Place(30, 80, lipgloss.Right, lipgloss.Bottom, fancyStyledParagraph)
+```
+
+You can also style the whitespace. For details, see [the docs][docs].
+
+
+***
+
+
+## What about [Bubble Tea][tea]?
+
+Lip Gloss doesn’t replace Bubble Tea. Rather, it is an excellent Bubble Tea
+companion. It was designed to make assembling terminal user interface views as
+simple and fun as possible so that you can focus on building your application
+instead of concerning yourself with low-level layout details.
+
+In simple terms, you can use Lip Gloss to help build your Bubble Tea views.
+
+[tea]: https://github.com/charmbracelet/tea
+
+
+## Under the Hood
+
+Lip Gloss is built on the excellent [Termenv][termenv] and [Reflow][reflow]
+libraries which deal with color and ANSI-aware text operations, respectively.
+For many use cases Termenv and Reflow will be sufficient for your needs.
+
+[termenv]: https://github.com/muesli/termenv
+[reflow]: https://github.com/muesli/reflow
+
+
+## Rendering Markdown
+
+For a more document-centric rendering solution with support for things like
+lists, tables, and syntax-highlighted code have a look at [Glamour][glamour],
+the stylesheet-based Markdown renderer.
+
+[glamour]: https://github.com/charmbracelet/glamour
+
+
+## Feedback
+
+We’d love to hear your thoughts on this project. Feel free to drop us a note!
+
+* [Twitter](https://twitter.com/charmcli)
+* [The Fediverse](https://mastodon.social/@charmcli)
+* [Discord](https://charm.sh/chat)
+
+## License
+
+[MIT](https://github.com/charmbracelet/lipgloss/raw/master/LICENSE)
+
+***
+
+Part of [Charm](https://charm.sh).
+
+<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>
+
+Charm热爱开源 • Charm loves open source
+
+
+[docs]: https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc
+[wish]: https://github.com/charmbracelet/wish
+[ssh-example]: examples/ssh

+ 82 - 0
vendor/github.com/charmbracelet/lipgloss/align.go

@@ -0,0 +1,82 @@
+package lipgloss
+
+import (
+	"strings"
+
+	"github.com/muesli/reflow/ansi"
+	"github.com/muesli/termenv"
+)
+
+// Perform text alignment. If the string is multi-lined, we also make all lines
+// the same width by padding them with spaces. If a termenv style is passed,
+// use that to style the spaces added.
+func alignTextHorizontal(str string, pos Position, width int, style *termenv.Style) string {
+	lines, widestLine := getLines(str)
+	var b strings.Builder
+
+	for i, l := range lines {
+		lineWidth := ansi.PrintableRuneWidth(l)
+
+		shortAmount := widestLine - lineWidth                // difference from the widest line
+		shortAmount += max(0, width-(shortAmount+lineWidth)) // difference from the total width, if set
+
+		if shortAmount > 0 {
+			switch pos {
+			case Right:
+				s := strings.Repeat(" ", shortAmount)
+				if style != nil {
+					s = style.Styled(s)
+				}
+				l = s + l
+			case Center:
+				left := shortAmount / 2
+				right := left + shortAmount%2 // note that we put the remainder on the right
+
+				leftSpaces := strings.Repeat(" ", left)
+				rightSpaces := strings.Repeat(" ", right)
+
+				if style != nil {
+					leftSpaces = style.Styled(leftSpaces)
+					rightSpaces = style.Styled(rightSpaces)
+				}
+				l = leftSpaces + l + rightSpaces
+			default: // Left
+				s := strings.Repeat(" ", shortAmount)
+				if style != nil {
+					s = style.Styled(s)
+				}
+				l += s
+			}
+		}
+
+		b.WriteString(l)
+		if i < len(lines)-1 {
+			b.WriteRune('\n')
+		}
+	}
+
+	return b.String()
+}
+
+func alignTextVertical(str string, pos Position, height int, _ *termenv.Style) string {
+	strHeight := strings.Count(str, "\n") + 1
+	if height < strHeight {
+		return str
+	}
+
+	switch pos {
+	case Top:
+		return str + strings.Repeat("\n", height-strHeight)
+	case Center:
+		var topPadding, bottomPadding = (height - strHeight) / 2, (height - strHeight) / 2
+		if strHeight+topPadding+bottomPadding > height {
+			topPadding--
+		} else if strHeight+topPadding+bottomPadding < height {
+			bottomPadding++
+		}
+		return strings.Repeat("\n", topPadding) + str + strings.Repeat("\n", bottomPadding)
+	case Bottom:
+		return strings.Repeat("\n", height-strHeight) + str
+	}
+	return str
+}

+ 7 - 0
vendor/github.com/charmbracelet/lipgloss/ansi_unix.go

@@ -0,0 +1,7 @@
+//go:build !windows
+// +build !windows
+
+package lipgloss
+
+// enableLegacyWindowsANSI is only needed on Windows.
+func enableLegacyWindowsANSI() {}

+ 22 - 0
vendor/github.com/charmbracelet/lipgloss/ansi_windows.go

@@ -0,0 +1,22 @@
+//go:build windows
+// +build windows
+
+package lipgloss
+
+import (
+	"sync"
+
+	"github.com/muesli/termenv"
+)
+
+var enableANSI sync.Once
+
+// enableANSIColors enables support for ANSI color sequences in the Windows
+// default console (cmd.exe and the PowerShell application). Note that this
+// only works with Windows 10. Also note that Windows Terminal supports colors
+// by default.
+func enableLegacyWindowsANSI() {
+	enableANSI.Do(func() {
+		_, _ = termenv.EnableWindowsANSIConsole()
+	})
+}

+ 412 - 0
vendor/github.com/charmbracelet/lipgloss/borders.go

@@ -0,0 +1,412 @@
+package lipgloss
+
+import (
+	"strings"
+
+	"github.com/mattn/go-runewidth"
+	"github.com/muesli/reflow/ansi"
+	"github.com/muesli/termenv"
+)
+
+// Border contains a series of values which comprise the various parts of a
+// border.
+type Border struct {
+	Top         string
+	Bottom      string
+	Left        string
+	Right       string
+	TopLeft     string
+	TopRight    string
+	BottomRight string
+	BottomLeft  string
+}
+
+// GetTopSize returns the width of the top border. If borders contain runes of
+// varying widths, the widest rune is returned. If no border exists on the top
+// edge, 0 is returned.
+func (b Border) GetTopSize() int {
+	return getBorderEdgeWidth(b.TopLeft, b.Top, b.TopRight)
+}
+
+// GetRightSize returns the width of the right border. If borders contain
+// runes of varying widths, the widest rune is returned. If no border exists on
+// the right edge, 0 is returned.
+func (b Border) GetRightSize() int {
+	return getBorderEdgeWidth(b.TopRight, b.Top, b.BottomRight)
+}
+
+// GetBottomSize returns the width of the bottom border. If borders contain
+// runes of varying widths, the widest rune is returned. If no border exists on
+// the bottom edge, 0 is returned.
+func (b Border) GetBottomSize() int {
+	return getBorderEdgeWidth(b.BottomLeft, b.Bottom, b.BottomRight)
+}
+
+// GetLeftSize returns the width of the left border. If borders contain runes
+// of varying widths, the widest rune is returned. If no border exists on the
+// left edge, 0 is returned.
+func (b Border) GetLeftSize() int {
+	return getBorderEdgeWidth(b.TopLeft, b.Left, b.TopRight)
+}
+
+func getBorderEdgeWidth(borderParts ...string) (maxWidth int) {
+	for _, piece := range borderParts {
+		w := maxRuneWidth(piece)
+		if w > maxWidth {
+			maxWidth = w
+		}
+	}
+	return maxWidth
+}
+
+var (
+	noBorder = Border{}
+
+	normalBorder = Border{
+		Top:         "─",
+		Bottom:      "─",
+		Left:        "│",
+		Right:       "│",
+		TopLeft:     "┌",
+		TopRight:    "┐",
+		BottomLeft:  "└",
+		BottomRight: "┘",
+	}
+
+	roundedBorder = Border{
+		Top:         "─",
+		Bottom:      "─",
+		Left:        "│",
+		Right:       "│",
+		TopLeft:     "╭",
+		TopRight:    "╮",
+		BottomLeft:  "╰",
+		BottomRight: "╯",
+	}
+
+	blockBorder = Border{
+		Top:         "█",
+		Bottom:      "█",
+		Left:        "█",
+		Right:       "█",
+		TopLeft:     "█",
+		TopRight:    "█",
+		BottomLeft:  "█",
+		BottomRight: "█",
+	}
+
+	outerHalfBlockBorder = Border{
+		Top:         "▀",
+		Bottom:      "▄",
+		Left:        "▌",
+		Right:       "▐",
+		TopLeft:     "▛",
+		TopRight:    "▜",
+		BottomLeft:  "▙",
+		BottomRight: "▟",
+	}
+
+	innerHalfBlockBorder = Border{
+		Top:         "▄",
+		Bottom:      "▀",
+		Left:        "▐",
+		Right:       "▌",
+		TopLeft:     "▗",
+		TopRight:    "▖",
+		BottomLeft:  "▝",
+		BottomRight: "▘",
+	}
+
+	thickBorder = Border{
+		Top:         "━",
+		Bottom:      "━",
+		Left:        "┃",
+		Right:       "┃",
+		TopLeft:     "┏",
+		TopRight:    "┓",
+		BottomLeft:  "┗",
+		BottomRight: "┛",
+	}
+
+	doubleBorder = Border{
+		Top:         "═",
+		Bottom:      "═",
+		Left:        "║",
+		Right:       "║",
+		TopLeft:     "╔",
+		TopRight:    "╗",
+		BottomLeft:  "╚",
+		BottomRight: "╝",
+	}
+
+	hiddenBorder = Border{
+		Top:         " ",
+		Bottom:      " ",
+		Left:        " ",
+		Right:       " ",
+		TopLeft:     " ",
+		TopRight:    " ",
+		BottomLeft:  " ",
+		BottomRight: " ",
+	}
+)
+
+// NormalBorder returns a standard-type border with a normal weight and 90
+// degree corners.
+func NormalBorder() Border {
+	return normalBorder
+}
+
+// RoundedBorder returns a border with rounded corners.
+func RoundedBorder() Border {
+	return roundedBorder
+}
+
+// BlockBorder returns a border that takes the whole block.
+func BlockBorder() Border {
+	return blockBorder
+}
+
+// OuterHalfBlockBorder returns a half-block border that sits outside the frame.
+func OuterHalfBlockBorder() Border {
+	return outerHalfBlockBorder
+}
+
+// InnerHalfBlockBorder returns a half-block border that sits inside the frame.
+func InnerHalfBlockBorder() Border {
+	return innerHalfBlockBorder
+}
+
+// ThickBorder returns a border that's thicker than the one returned by
+// NormalBorder.
+func ThickBorder() Border {
+	return thickBorder
+}
+
+// DoubleBorder returns a border comprised of two thin strokes.
+func DoubleBorder() Border {
+	return doubleBorder
+}
+
+// HiddenBorder returns a border that renders as a series of single-cell
+// spaces. It's useful for cases when you want to remove a standard border but
+// maintain layout positioning. This said, you can still apply a background
+// color to a hidden border.
+func HiddenBorder() Border {
+	return hiddenBorder
+}
+
+func (s Style) applyBorder(str string) string {
+	var (
+		topSet    = s.isSet(borderTopKey)
+		rightSet  = s.isSet(borderRightKey)
+		bottomSet = s.isSet(borderBottomKey)
+		leftSet   = s.isSet(borderLeftKey)
+
+		border    = s.getBorderStyle()
+		hasTop    = s.getAsBool(borderTopKey, false)
+		hasRight  = s.getAsBool(borderRightKey, false)
+		hasBottom = s.getAsBool(borderBottomKey, false)
+		hasLeft   = s.getAsBool(borderLeftKey, false)
+
+		topFG    = s.getAsColor(borderTopForegroundKey)
+		rightFG  = s.getAsColor(borderRightForegroundKey)
+		bottomFG = s.getAsColor(borderBottomForegroundKey)
+		leftFG   = s.getAsColor(borderLeftForegroundKey)
+
+		topBG    = s.getAsColor(borderTopBackgroundKey)
+		rightBG  = s.getAsColor(borderRightBackgroundKey)
+		bottomBG = s.getAsColor(borderBottomBackgroundKey)
+		leftBG   = s.getAsColor(borderLeftBackgroundKey)
+	)
+
+	// If a border is set and no sides have been specifically turned on or off
+	// render borders on all sides.
+	if border != noBorder && !(topSet || rightSet || bottomSet || leftSet) {
+		hasTop = true
+		hasRight = true
+		hasBottom = true
+		hasLeft = true
+	}
+
+	// If no border is set or all borders are been disabled, abort.
+	if border == noBorder || (!hasTop && !hasRight && !hasBottom && !hasLeft) {
+		return str
+	}
+
+	lines, width := getLines(str)
+
+	if hasLeft {
+		if border.Left == "" {
+			border.Left = " "
+		}
+		width += maxRuneWidth(border.Left)
+	}
+
+	if hasRight && border.Right == "" {
+		border.Right = " "
+	}
+
+	// If corners should be rendered but are set with the empty string, fill them
+	// with a single space.
+	if hasTop && hasLeft && border.TopLeft == "" {
+		border.TopLeft = " "
+	}
+	if hasTop && hasRight && border.TopRight == "" {
+		border.TopRight = " "
+	}
+	if hasBottom && hasLeft && border.BottomLeft == "" {
+		border.BottomLeft = " "
+	}
+	if hasBottom && hasRight && border.BottomRight == "" {
+		border.BottomRight = " "
+	}
+
+	// Figure out which corners we should actually be using based on which
+	// sides are set to show.
+	if hasTop {
+		switch {
+		case !hasLeft && !hasRight:
+			border.TopLeft = ""
+			border.TopRight = ""
+		case !hasLeft:
+			border.TopLeft = ""
+		case !hasRight:
+			border.TopRight = ""
+		}
+	}
+	if hasBottom {
+		switch {
+		case !hasLeft && !hasRight:
+			border.BottomLeft = ""
+			border.BottomRight = ""
+		case !hasLeft:
+			border.BottomLeft = ""
+		case !hasRight:
+			border.BottomRight = ""
+		}
+	}
+
+	// For now, limit corners to one rune.
+	border.TopLeft = getFirstRuneAsString(border.TopLeft)
+	border.TopRight = getFirstRuneAsString(border.TopRight)
+	border.BottomRight = getFirstRuneAsString(border.BottomRight)
+	border.BottomLeft = getFirstRuneAsString(border.BottomLeft)
+
+	var out strings.Builder
+
+	// Render top
+	if hasTop {
+		top := renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width)
+		top = s.styleBorder(top, topFG, topBG)
+		out.WriteString(top)
+		out.WriteRune('\n')
+	}
+
+	leftRunes := []rune(border.Left)
+	leftIndex := 0
+
+	rightRunes := []rune(border.Right)
+	rightIndex := 0
+
+	// Render sides
+	for i, l := range lines {
+		if hasLeft {
+			r := string(leftRunes[leftIndex])
+			leftIndex++
+			if leftIndex >= len(leftRunes) {
+				leftIndex = 0
+			}
+			out.WriteString(s.styleBorder(r, leftFG, leftBG))
+		}
+		out.WriteString(l)
+		if hasRight {
+			r := string(rightRunes[rightIndex])
+			rightIndex++
+			if rightIndex >= len(rightRunes) {
+				rightIndex = 0
+			}
+			out.WriteString(s.styleBorder(r, rightFG, rightBG))
+		}
+		if i < len(lines)-1 {
+			out.WriteRune('\n')
+		}
+	}
+
+	// Render bottom
+	if hasBottom {
+		bottom := renderHorizontalEdge(border.BottomLeft, border.Bottom, border.BottomRight, width)
+		bottom = s.styleBorder(bottom, bottomFG, bottomBG)
+		out.WriteRune('\n')
+		out.WriteString(bottom)
+	}
+
+	return out.String()
+}
+
+// Render the horizontal (top or bottom) portion of a border.
+func renderHorizontalEdge(left, middle, right string, width int) string {
+	if width < 1 {
+		return ""
+	}
+
+	if middle == "" {
+		middle = " "
+	}
+
+	leftWidth := ansi.PrintableRuneWidth(left)
+	rightWidth := ansi.PrintableRuneWidth(right)
+
+	runes := []rune(middle)
+	j := 0
+
+	out := strings.Builder{}
+	out.WriteString(left)
+	for i := leftWidth + rightWidth; i < width+rightWidth; {
+		out.WriteRune(runes[j])
+		j++
+		if j >= len(runes) {
+			j = 0
+		}
+		i += ansi.PrintableRuneWidth(string(runes[j]))
+	}
+	out.WriteString(right)
+
+	return out.String()
+}
+
+// Apply foreground and background styling to a border.
+func (s Style) styleBorder(border string, fg, bg TerminalColor) string {
+	if fg == noColor && bg == noColor {
+		return border
+	}
+
+	var style = termenv.Style{}
+
+	if fg != noColor {
+		style = style.Foreground(fg.color(s.r))
+	}
+	if bg != noColor {
+		style = style.Background(bg.color(s.r))
+	}
+
+	return style.Styled(border)
+}
+
+func maxRuneWidth(str string) (width int) {
+	for _, r := range str {
+		w := runewidth.RuneWidth(r)
+		if w > width {
+			width = w
+		}
+	}
+	return width
+}
+
+func getFirstRuneAsString(str string) string {
+	if str == "" {
+		return str
+	}
+	r := []rune(str)
+	return string(r[0])
+}

+ 172 - 0
vendor/github.com/charmbracelet/lipgloss/color.go

@@ -0,0 +1,172 @@
+package lipgloss
+
+import (
+	"strconv"
+
+	"github.com/muesli/termenv"
+)
+
+// TerminalColor is a color intended to be rendered in the terminal.
+type TerminalColor interface {
+	color(*Renderer) termenv.Color
+	RGBA() (r, g, b, a uint32)
+}
+
+var noColor = NoColor{}
+
+// NoColor is used to specify the absence of color styling. When this is active
+// foreground colors will be rendered with the terminal's default text color,
+// and background colors will not be drawn at all.
+//
+// Example usage:
+//
+//	var style = someStyle.Copy().Background(lipgloss.NoColor{})
+type NoColor struct{}
+
+func (NoColor) color(*Renderer) termenv.Color {
+	return termenv.NoColor{}
+}
+
+// RGBA returns the RGBA value of this color. Because we have to return
+// something, despite this color being the absence of color, we're returning
+// black with 100% opacity.
+//
+// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
+//
+// Deprecated.
+func (n NoColor) RGBA() (r, g, b, a uint32) {
+	return 0x0, 0x0, 0x0, 0xFFFF
+}
+
+// Color specifies a color by hex or ANSI value. For example:
+//
+//	ansiColor := lipgloss.Color("21")
+//	hexColor := lipgloss.Color("#0000ff")
+type Color string
+
+func (c Color) color(r *Renderer) termenv.Color {
+	return r.ColorProfile().Color(string(c))
+}
+
+// RGBA returns the RGBA value of this color. This satisfies the Go Color
+// interface. Note that on error we return black with 100% opacity, or:
+//
+// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
+//
+// Deprecated.
+func (c Color) RGBA() (r, g, b, a uint32) {
+	return termenv.ConvertToRGB(c.color(renderer)).RGBA()
+}
+
+// ANSIColor is a color specified by an ANSI color value. It's merely syntactic
+// sugar for the more general Color function. Invalid colors will render as
+// black.
+//
+// Example usage:
+//
+//	// These two statements are equivalent.
+//	colorA := lipgloss.ANSIColor(21)
+//	colorB := lipgloss.Color("21")
+type ANSIColor uint
+
+func (ac ANSIColor) color(r *Renderer) termenv.Color {
+	return Color(strconv.FormatUint(uint64(ac), 10)).color(r)
+}
+
+// RGBA returns the RGBA value of this color. This satisfies the Go Color
+// interface. Note that on error we return black with 100% opacity, or:
+//
+// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
+//
+// Deprecated.
+func (ac ANSIColor) RGBA() (r, g, b, a uint32) {
+	cf := Color(strconv.FormatUint(uint64(ac), 10))
+	return cf.RGBA()
+}
+
+// AdaptiveColor provides color options for light and dark backgrounds. The
+// appropriate color will be returned at runtime based on the darkness of the
+// terminal background color.
+//
+// Example usage:
+//
+//	color := lipgloss.AdaptiveColor{Light: "#0000ff", Dark: "#000099"}
+type AdaptiveColor struct {
+	Light string
+	Dark  string
+}
+
+func (ac AdaptiveColor) color(r *Renderer) termenv.Color {
+	if r.HasDarkBackground() {
+		return Color(ac.Dark).color(r)
+	}
+	return Color(ac.Light).color(r)
+}
+
+// RGBA returns the RGBA value of this color. This satisfies the Go Color
+// interface. Note that on error we return black with 100% opacity, or:
+//
+// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
+//
+// Deprecated.
+func (ac AdaptiveColor) RGBA() (r, g, b, a uint32) {
+	return termenv.ConvertToRGB(ac.color(renderer)).RGBA()
+}
+
+// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
+// profiles. Automatic color degradation will not be performed.
+type CompleteColor struct {
+	TrueColor string
+	ANSI256   string
+	ANSI      string
+}
+
+func (c CompleteColor) color(r *Renderer) termenv.Color {
+	p := r.ColorProfile()
+	switch p {
+	case termenv.TrueColor:
+		return p.Color(c.TrueColor)
+	case termenv.ANSI256:
+		return p.Color(c.ANSI256)
+	case termenv.ANSI:
+		return p.Color(c.ANSI)
+	default:
+		return termenv.NoColor{}
+	}
+}
+
+// RGBA returns the RGBA value of this color. This satisfies the Go Color
+// interface. Note that on error we return black with 100% opacity, or:
+//
+// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
+// CompleteAdaptiveColor specifies exact values for truecolor, ANSI256, and ANSI color
+//
+// Deprecated.
+func (c CompleteColor) RGBA() (r, g, b, a uint32) {
+	return termenv.ConvertToRGB(c.color(renderer)).RGBA()
+}
+
+// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
+// profiles, with separate options for light and dark backgrounds. Automatic
+// color degradation will not be performed.
+type CompleteAdaptiveColor struct {
+	Light CompleteColor
+	Dark  CompleteColor
+}
+
+func (cac CompleteAdaptiveColor) color(r *Renderer) termenv.Color {
+	if r.HasDarkBackground() {
+		return cac.Dark.color(r)
+	}
+	return cac.Light.color(r)
+}
+
+// RGBA returns the RGBA value of this color. This satisfies the Go Color
+// interface. Note that on error we return black with 100% opacity, or:
+//
+// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
+//
+// Deprecated.
+func (cac CompleteAdaptiveColor) RGBA() (r, g, b, a uint32) {
+	return termenv.ConvertToRGB(cac.color(renderer)).RGBA()
+}

+ 481 - 0
vendor/github.com/charmbracelet/lipgloss/get.go

@@ -0,0 +1,481 @@
+package lipgloss
+
+import (
+	"strings"
+
+	"github.com/muesli/reflow/ansi"
+)
+
+// GetBold returns the style's bold value. If no value is set false is returned.
+func (s Style) GetBold() bool {
+	return s.getAsBool(boldKey, false)
+}
+
+// GetItalic returns the style's italic value. If no value is set false is
+// returned.
+func (s Style) GetItalic() bool {
+	return s.getAsBool(italicKey, false)
+}
+
+// GetUnderline returns the style's underline value. If no value is set false is
+// returned.
+func (s Style) GetUnderline() bool {
+	return s.getAsBool(underlineKey, false)
+}
+
+// GetStrikethrough returns the style's strikethrough value. If no value is set false
+// is returned.
+func (s Style) GetStrikethrough() bool {
+	return s.getAsBool(strikethroughKey, false)
+}
+
+// GetReverse returns the style's reverse value. If no value is set false is
+// returned.
+func (s Style) GetReverse() bool {
+	return s.getAsBool(reverseKey, false)
+}
+
+// GetBlink returns the style's blink value. If no value is set false is
+// returned.
+func (s Style) GetBlink() bool {
+	return s.getAsBool(blinkKey, false)
+}
+
+// GetFaint returns the style's faint value. If no value is set false is
+// returned.
+func (s Style) GetFaint() bool {
+	return s.getAsBool(faintKey, false)
+}
+
+// GetForeground returns the style's foreground color. If no value is set
+// NoColor{} is returned.
+func (s Style) GetForeground() TerminalColor {
+	return s.getAsColor(foregroundKey)
+}
+
+// GetBackground returns the style's back color. If no value is set
+// NoColor{} is returned.
+func (s Style) GetBackground() TerminalColor {
+	return s.getAsColor(backgroundKey)
+}
+
+// GetWidth returns the style's width setting. If no width is set 0 is
+// returned.
+func (s Style) GetWidth() int {
+	return s.getAsInt(widthKey)
+}
+
+// GetHeight returns the style's height setting. If no height is set 0 is
+// returned.
+func (s Style) GetHeight() int {
+	return s.getAsInt(heightKey)
+}
+
+// GetAlign returns the style's implicit horizontal alignment setting.
+// If no alignment is set Position.Left is returned.
+func (s Style) GetAlign() Position {
+	v := s.getAsPosition(alignHorizontalKey)
+	if v == Position(0) {
+		return Left
+	}
+	return v
+}
+
+// GetAlignHorizontal returns the style's implicit horizontal alignment setting.
+// If no alignment is set Position.Left is returned.
+func (s Style) GetAlignHorizontal() Position {
+	v := s.getAsPosition(alignHorizontalKey)
+	if v == Position(0) {
+		return Left
+	}
+	return v
+}
+
+// GetAlignVertical returns the style's implicit vertical alignment setting.
+// If no alignment is set Position.Top is returned.
+func (s Style) GetAlignVertical() Position {
+	v := s.getAsPosition(alignVerticalKey)
+	if v == Position(0) {
+		return Top
+	}
+	return v
+}
+
+// GetPadding returns the style's top, right, bottom, and left padding values,
+// in that order. 0 is returned for unset values.
+func (s Style) GetPadding() (top, right, bottom, left int) {
+	return s.getAsInt(paddingTopKey),
+		s.getAsInt(paddingRightKey),
+		s.getAsInt(paddingBottomKey),
+		s.getAsInt(paddingLeftKey)
+}
+
+// GetPaddingTop returns the style's top padding. If no value is set 0 is
+// returned.
+func (s Style) GetPaddingTop() int {
+	return s.getAsInt(paddingTopKey)
+}
+
+// GetPaddingRight returns the style's right padding. If no value is set 0 is
+// returned.
+func (s Style) GetPaddingRight() int {
+	return s.getAsInt(paddingRightKey)
+}
+
+// GetPaddingBottom returns the style's bottom padding. If no value is set 0 is
+// returned.
+func (s Style) GetPaddingBottom() int {
+	return s.getAsInt(paddingBottomKey)
+}
+
+// GetPaddingLeft returns the style's left padding. If no value is set 0 is
+// returned.
+func (s Style) GetPaddingLeft() int {
+	return s.getAsInt(paddingLeftKey)
+}
+
+// GetHorizontalPadding returns the style's left and right padding. Unset
+// values are measured as 0.
+func (s Style) GetHorizontalPadding() int {
+	return s.getAsInt(paddingLeftKey) + s.getAsInt(paddingRightKey)
+}
+
+// GetVerticalPadding returns the style's top and bottom padding. Unset values
+// are measured as 0.
+func (s Style) GetVerticalPadding() int {
+	return s.getAsInt(paddingTopKey) + s.getAsInt(paddingBottomKey)
+}
+
+// GetColorWhitespace returns the style's whitespace coloring setting. If no
+// value is set false is returned.
+func (s Style) GetColorWhitespace() bool {
+	return s.getAsBool(colorWhitespaceKey, false)
+}
+
+// GetMargin returns the style's top, right, bottom, and left margins, in that
+// order. 0 is returned for unset values.
+func (s Style) GetMargin() (top, right, bottom, left int) {
+	return s.getAsInt(marginTopKey),
+		s.getAsInt(marginRightKey),
+		s.getAsInt(marginBottomKey),
+		s.getAsInt(marginLeftKey)
+}
+
+// GetMarginTop returns the style's top margin. If no value is set 0 is
+// returned.
+func (s Style) GetMarginTop() int {
+	return s.getAsInt(marginTopKey)
+}
+
+// GetMarginRight returns the style's right margin. If no value is set 0 is
+// returned.
+func (s Style) GetMarginRight() int {
+	return s.getAsInt(marginRightKey)
+}
+
+// GetMarginBottom returns the style's bottom margin. If no value is set 0 is
+// returned.
+func (s Style) GetMarginBottom() int {
+	return s.getAsInt(marginBottomKey)
+}
+
+// GetMarginLeft returns the style's left margin. If no value is set 0 is
+// returned.
+func (s Style) GetMarginLeft() int {
+	return s.getAsInt(marginLeftKey)
+}
+
+// GetHorizontalMargins returns the style's left and right margins. Unset
+// values are measured as 0.
+func (s Style) GetHorizontalMargins() int {
+	return s.getAsInt(marginLeftKey) + s.getAsInt(marginRightKey)
+}
+
+// GetVerticalMargins returns the style's top and bottom padding. Unset values
+// are measured as 0.
+func (s Style) GetVerticalMargins() int {
+	return s.getAsInt(marginTopKey) + s.getAsInt(marginBottomKey)
+}
+
+// GetBorder returns the style's border style (type Border) and value for the
+// top, right, bottom, and left in that order. If no value is set for the
+// border style, Border{} is returned. For all other unset values false is
+// returned.
+func (s Style) GetBorder() (b Border, top, right, bottom, left bool) {
+	return s.getBorderStyle(),
+		s.getAsBool(borderTopKey, false),
+		s.getAsBool(borderRightKey, false),
+		s.getAsBool(borderBottomKey, false),
+		s.getAsBool(borderLeftKey, false)
+}
+
+// GetBorderStyle returns the style's border style (type Border). If no value
+// is set Border{} is returned.
+func (s Style) GetBorderStyle() Border {
+	return s.getBorderStyle()
+}
+
+// GetBorderTop returns the style's top border setting. If no value is set
+// false is returned.
+func (s Style) GetBorderTop() bool {
+	return s.getAsBool(borderTopKey, false)
+}
+
+// GetBorderRight returns the style's right border setting. If no value is set
+// false is returned.
+func (s Style) GetBorderRight() bool {
+	return s.getAsBool(borderRightKey, false)
+}
+
+// GetBorderBottom returns the style's bottom border setting. If no value is
+// set false is returned.
+func (s Style) GetBorderBottom() bool {
+	return s.getAsBool(borderBottomKey, false)
+}
+
+// GetBorderLeft returns the style's left border setting. If no value is
+// set false is returned.
+func (s Style) GetBorderLeft() bool {
+	return s.getAsBool(borderLeftKey, false)
+}
+
+// GetBorderTopForeground returns the style's border top foreground color. If
+// no value is set NoColor{} is returned.
+func (s Style) GetBorderTopForeground() TerminalColor {
+	return s.getAsColor(borderTopForegroundKey)
+}
+
+// GetBorderRightForeground returns the style's border right foreground color.
+// If no value is set NoColor{} is returned.
+func (s Style) GetBorderRightForeground() TerminalColor {
+	return s.getAsColor(borderRightForegroundKey)
+}
+
+// GetBorderBottomForeground returns the style's border bottom foreground
+// color.  If no value is set NoColor{} is returned.
+func (s Style) GetBorderBottomForeground() TerminalColor {
+	return s.getAsColor(borderBottomForegroundKey)
+}
+
+// GetBorderLeftForeground returns the style's border bottom foreground
+// color.  If no value is set NoColor{} is returned.
+func (s Style) GetBorderLeftForeground() TerminalColor {
+	return s.getAsColor(borderLeftForegroundKey)
+}
+
+// GetBorderTopBackground returns the style's border top background color. If
+// no value is set NoColor{} is returned.
+func (s Style) GetBorderTopBackground() TerminalColor {
+	return s.getAsColor(borderTopBackgroundKey)
+}
+
+// GetBorderRightBackground returns the style's border right background color.
+// If no value is set NoColor{} is returned.
+func (s Style) GetBorderRightBackground() TerminalColor {
+	return s.getAsColor(borderRightBackgroundKey)
+}
+
+// GetBorderBottomBackground returns the style's border bottom background
+// color.  If no value is set NoColor{} is returned.
+func (s Style) GetBorderBottomBackground() TerminalColor {
+	return s.getAsColor(borderBottomBackgroundKey)
+}
+
+// GetBorderLeftBackground returns the style's border bottom background
+// color.  If no value is set NoColor{} is returned.
+func (s Style) GetBorderLeftBackground() TerminalColor {
+	return s.getAsColor(borderLeftBackgroundKey)
+}
+
+// GetBorderTopWidth returns the width of the top border. If borders contain
+// runes of varying widths, the widest rune is returned. If no border exists on
+// the top edge, 0 is returned.
+//
+// Deprecated: This function simply calls Style.GetBorderTopSize.
+func (s Style) GetBorderTopWidth() int {
+	return s.GetBorderTopSize()
+}
+
+// GetBorderTopSize returns the width of the top border. If borders contain
+// runes of varying widths, the widest rune is returned. If no border exists on
+// the top edge, 0 is returned.
+func (s Style) GetBorderTopSize() int {
+	if !s.getAsBool(borderTopKey, false) {
+		return 0
+	}
+	return s.getBorderStyle().GetTopSize()
+}
+
+// GetBorderLeftSize returns the width of the left border. If borders contain
+// runes of varying widths, the widest rune is returned. If no border exists on
+// the left edge, 0 is returned.
+func (s Style) GetBorderLeftSize() int {
+	if !s.getAsBool(borderLeftKey, false) {
+		return 0
+	}
+	return s.getBorderStyle().GetLeftSize()
+}
+
+// GetBorderBottomSize returns the width of the bottom border. If borders
+// contain runes of varying widths, the widest rune is returned. If no border
+// exists on the left edge, 0 is returned.
+func (s Style) GetBorderBottomSize() int {
+	if !s.getAsBool(borderBottomKey, false) {
+		return 0
+	}
+	return s.getBorderStyle().GetBottomSize()
+}
+
+// GetBorderRightSize returns the width of the right border. If borders
+// contain runes of varying widths, the widest rune is returned. If no border
+// exists on the right edge, 0 is returned.
+func (s Style) GetBorderRightSize() int {
+	if !s.getAsBool(borderRightKey, false) {
+		return 0
+	}
+	return s.getBorderStyle().GetBottomSize()
+}
+
+// GetHorizontalBorderSize returns the width of the horizontal borders. If
+// borders contain runes of varying widths, the widest rune is returned. If no
+// border exists on the horizontal edges, 0 is returned.
+func (s Style) GetHorizontalBorderSize() int {
+	b := s.getBorderStyle()
+	return b.GetLeftSize() + b.GetRightSize()
+}
+
+// GetVerticalBorderSize returns the width of the horizontal borders. If
+// borders contain runes of varying widths, the widest rune is returned. If no
+// border exists on the horizontal edges, 0 is returned.
+func (s Style) GetVerticalBorderSize() int {
+	b := s.getBorderStyle()
+	return b.GetTopSize() + b.GetBottomSize()
+}
+
+// GetInline returns the style's inline setting. If no value is set false is
+// returned.
+func (s Style) GetInline() bool {
+	return s.getAsBool(inlineKey, false)
+}
+
+// GetMaxWidth returns the style's max width setting. If no value is set 0 is
+// returned.
+func (s Style) GetMaxWidth() int {
+	return s.getAsInt(maxWidthKey)
+}
+
+// GetMaxHeight returns the style's max width setting. If no value is set 0 is
+// returned.
+func (s Style) GetMaxHeight() int {
+	return s.getAsInt(maxHeightKey)
+}
+
+// GetUnderlineSpaces returns whether or not the style is set to underline
+// spaces. If not value is set false is returned.
+func (s Style) GetUnderlineSpaces() bool {
+	return s.getAsBool(underlineSpacesKey, false)
+}
+
+// GetStrikethroughSpaces returns whether or not the style is set to underline
+// spaces. If not value is set false is returned.
+func (s Style) GetStrikethroughSpaces() bool {
+	return s.getAsBool(strikethroughSpacesKey, false)
+}
+
+// GetHorizontalFrameSize returns the sum of the style's horizontal margins, padding
+// and border widths.
+//
+// Provisional: this method may be renamed.
+func (s Style) GetHorizontalFrameSize() int {
+	return s.GetHorizontalMargins() + s.GetHorizontalPadding() + s.GetHorizontalBorderSize()
+}
+
+// GetVerticalFrameSize returns the sum of the style's horizontal margins, padding
+// and border widths.
+//
+// Provisional: this method may be renamed.
+func (s Style) GetVerticalFrameSize() int {
+	return s.GetVerticalMargins() + s.GetVerticalPadding() + s.GetVerticalBorderSize()
+}
+
+// GetFrameSize returns the sum of the margins, padding and border width for
+// both the horizontal and vertical margins.
+func (s Style) GetFrameSize() (x, y int) {
+	return s.GetHorizontalFrameSize(), s.GetVerticalFrameSize()
+}
+
+// Returns whether or not the given property is set.
+func (s Style) isSet(k propKey) bool {
+	_, exists := s.rules[k]
+	return exists
+}
+
+func (s Style) getAsBool(k propKey, defaultVal bool) bool {
+	v, ok := s.rules[k]
+	if !ok {
+		return defaultVal
+	}
+	if b, ok := v.(bool); ok {
+		return b
+	}
+	return defaultVal
+}
+
+func (s Style) getAsColor(k propKey) TerminalColor {
+	v, ok := s.rules[k]
+	if !ok {
+		return noColor
+	}
+	if c, ok := v.(TerminalColor); ok {
+		return c
+	}
+	return noColor
+}
+
+func (s Style) getAsInt(k propKey) int {
+	v, ok := s.rules[k]
+	if !ok {
+		return 0
+	}
+	if i, ok := v.(int); ok {
+		return i
+	}
+	return 0
+}
+
+func (s Style) getAsPosition(k propKey) Position {
+	v, ok := s.rules[k]
+	if !ok {
+		return Position(0)
+	}
+	if p, ok := v.(Position); ok {
+		return p
+	}
+	return Position(0)
+}
+
+func (s Style) getBorderStyle() Border {
+	v, ok := s.rules[borderStyleKey]
+	if !ok {
+		return noBorder
+	}
+	if b, ok := v.(Border); ok {
+		return b
+	}
+	return noBorder
+}
+
+// Split a string into lines, additionally returning the size of the widest
+// line.
+func getLines(s string) (lines []string, widest int) {
+	lines = strings.Split(s, "\n")
+
+	for _, l := range lines {
+		w := ansi.PrintableRuneWidth(l)
+		if widest < w {
+			widest = w
+		}
+	}
+
+	return lines, widest
+}

+ 175 - 0
vendor/github.com/charmbracelet/lipgloss/join.go

@@ -0,0 +1,175 @@
+package lipgloss
+
+import (
+	"math"
+	"strings"
+
+	"github.com/muesli/reflow/ansi"
+)
+
+// JoinHorizontal is a utility function for horizontally joining two
+// potentially multi-lined strings along a vertical axis. The first argument is
+// the position, with 0 being all the way at the top and 1 being all the way
+// at the bottom.
+//
+// If you just want to align to the left, right or center you may as well just
+// use the helper constants Top, Center, and Bottom.
+//
+// Example:
+//
+//	blockB := "...\n...\n..."
+//	blockA := "...\n...\n...\n...\n..."
+//
+//	// Join 20% from the top
+//	str := lipgloss.JoinHorizontal(0.2, blockA, blockB)
+//
+//	// Join on the top edge
+//	str := lipgloss.JoinHorizontal(lipgloss.Top, blockA, blockB)
+func JoinHorizontal(pos Position, strs ...string) string {
+	if len(strs) == 0 {
+		return ""
+	}
+	if len(strs) == 1 {
+		return strs[0]
+	}
+
+	var (
+		// Groups of strings broken into multiple lines
+		blocks = make([][]string, len(strs))
+
+		// Max line widths for the above text blocks
+		maxWidths = make([]int, len(strs))
+
+		// Height of the tallest block
+		maxHeight int
+	)
+
+	// Break text blocks into lines and get max widths for each text block
+	for i, str := range strs {
+		blocks[i], maxWidths[i] = getLines(str)
+		if len(blocks[i]) > maxHeight {
+			maxHeight = len(blocks[i])
+		}
+	}
+
+	// Add extra lines to make each side the same height
+	for i := range blocks {
+		if len(blocks[i]) >= maxHeight {
+			continue
+		}
+
+		extraLines := make([]string, maxHeight-len(blocks[i]))
+
+		switch pos {
+		case Top:
+			blocks[i] = append(blocks[i], extraLines...)
+
+		case Bottom:
+			blocks[i] = append(extraLines, blocks[i]...)
+
+		default: // Somewhere in the middle
+			n := len(extraLines)
+			split := int(math.Round(float64(n) * pos.value()))
+			top := n - split
+			bottom := n - top
+
+			blocks[i] = append(extraLines[top:], blocks[i]...)
+			blocks[i] = append(blocks[i], extraLines[bottom:]...)
+		}
+	}
+
+	// Merge lines
+	var b strings.Builder
+	for i := range blocks[0] { // remember, all blocks have the same number of members now
+		for j, block := range blocks {
+			b.WriteString(block[i])
+
+			// Also make lines the same length
+			b.WriteString(strings.Repeat(" ", maxWidths[j]-ansi.PrintableRuneWidth(block[i])))
+		}
+		if i < len(blocks[0])-1 {
+			b.WriteRune('\n')
+		}
+	}
+
+	return b.String()
+}
+
+// JoinVertical is a utility function for vertically joining two potentially
+// multi-lined strings along a horizontal axis. The first argument is the
+// position, with 0 being all the way to the left and 1 being all the way to
+// the right.
+//
+// If you just want to align to the left, right or center you may as well just
+// use the helper constants Left, Center, and Right.
+//
+// Example:
+//
+//	blockB := "...\n...\n..."
+//	blockA := "...\n...\n...\n...\n..."
+//
+//	// Join 20% from the top
+//	str := lipgloss.JoinVertical(0.2, blockA, blockB)
+//
+//	// Join on the right edge
+//	str := lipgloss.JoinVertical(lipgloss.Right, blockA, blockB)
+func JoinVertical(pos Position, strs ...string) string {
+	if len(strs) == 0 {
+		return ""
+	}
+	if len(strs) == 1 {
+		return strs[0]
+	}
+
+	var (
+		blocks   = make([][]string, len(strs))
+		maxWidth int
+	)
+
+	for i := range strs {
+		var w int
+		blocks[i], w = getLines(strs[i])
+		if w > maxWidth {
+			maxWidth = w
+		}
+	}
+
+	var b strings.Builder
+	for i, block := range blocks {
+		for j, line := range block {
+			w := maxWidth - ansi.PrintableRuneWidth(line)
+
+			switch pos {
+			case Left:
+				b.WriteString(line)
+				b.WriteString(strings.Repeat(" ", w))
+
+			case Right:
+				b.WriteString(strings.Repeat(" ", w))
+				b.WriteString(line)
+
+			default: // Somewhere in the middle
+				if w < 1 {
+					b.WriteString(line)
+					break
+				}
+
+				split := int(math.Round(float64(w) * pos.value()))
+				right := w - split
+				left := w - right
+
+				b.WriteString(strings.Repeat(" ", left))
+				b.WriteString(line)
+				b.WriteString(strings.Repeat(" ", right))
+			}
+
+			// Write a newline as long as we're not on the last line of the
+			// last block.
+			if !(i == len(blocks)-1 && j == len(block)-1) {
+				b.WriteRune('\n')
+			}
+		}
+	}
+
+	return b.String()
+}

+ 154 - 0
vendor/github.com/charmbracelet/lipgloss/position.go

@@ -0,0 +1,154 @@
+package lipgloss
+
+import (
+	"math"
+	"strings"
+
+	"github.com/muesli/reflow/ansi"
+)
+
+// Position represents a position along a horizontal or vertical axis. It's in
+// situations where an axis is involved, like alignment, joining, placement and
+// so on.
+//
+// A value of 0 represents the start (the left or top) and 1 represents the end
+// (the right or bottom). 0.5 represents the center.
+//
+// There are constants Top, Bottom, Center, Left and Right in this package that
+// can be used to aid readability.
+type Position float64
+
+func (p Position) value() float64 {
+	return math.Min(1, math.Max(0, float64(p)))
+}
+
+// Position aliases.
+const (
+	Top    Position = 0.0
+	Bottom Position = 1.0
+	Center Position = 0.5
+	Left   Position = 0.0
+	Right  Position = 1.0
+)
+
+// Place places a string or text block vertically in an unstyled box of a given
+// width or height.
+func Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
+	return renderer.Place(width, height, hPos, vPos, str, opts...)
+}
+
+// Place places a string or text block vertically in an unstyled box of a given
+// width or height.
+func (r *Renderer) Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
+	return r.PlaceVertical(height, vPos, r.PlaceHorizontal(width, hPos, str, opts...), opts...)
+}
+
+// PlaceHorizontal places a string or text block horizontally in an unstyled
+// block of a given width. If the given width is shorter than the max width of
+// the string (measured by its longest line) this will be a noop.
+func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
+	return renderer.PlaceHorizontal(width, pos, str, opts...)
+}
+
+// PlaceHorizontal places a string or text block horizontally in an unstyled
+// block of a given width. If the given width is shorter than the max width of
+// the string (measured by it's longest line) this will be a noöp.
+func (r *Renderer) PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
+	lines, contentWidth := getLines(str)
+	gap := width - contentWidth
+
+	if gap <= 0 {
+		return str
+	}
+
+	ws := newWhitespace(r, opts...)
+
+	var b strings.Builder
+	for i, l := range lines {
+		// Is this line shorter than the longest line?
+		short := max(0, contentWidth-ansi.PrintableRuneWidth(l))
+
+		switch pos {
+		case Left:
+			b.WriteString(l)
+			b.WriteString(ws.render(gap + short))
+
+		case Right:
+			b.WriteString(ws.render(gap + short))
+			b.WriteString(l)
+
+		default: // somewhere in the middle
+			totalGap := gap + short
+
+			split := int(math.Round(float64(totalGap) * pos.value()))
+			left := totalGap - split
+			right := totalGap - left
+
+			b.WriteString(ws.render(left))
+			b.WriteString(l)
+			b.WriteString(ws.render(right))
+		}
+
+		if i < len(lines)-1 {
+			b.WriteRune('\n')
+		}
+	}
+
+	return b.String()
+}
+
+// PlaceVertical places a string or text block vertically in an unstyled block
+// of a given height. If the given height is shorter than the height of the
+// string (measured by its newlines) then this will be a noop.
+func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
+	return renderer.PlaceVertical(height, pos, str, opts...)
+}
+
+// PlaceVertical places a string or text block vertically in an unstyled block
+// of a given height. If the given height is shorter than the height of the
+// string (measured by it's newlines) then this will be a noöp.
+func (r *Renderer) PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
+	contentHeight := strings.Count(str, "\n") + 1
+	gap := height - contentHeight
+
+	if gap <= 0 {
+		return str
+	}
+
+	ws := newWhitespace(r, opts...)
+
+	_, width := getLines(str)
+	emptyLine := ws.render(width)
+	b := strings.Builder{}
+
+	switch pos {
+	case Top:
+		b.WriteString(str)
+		b.WriteRune('\n')
+		for i := 0; i < gap; i++ {
+			b.WriteString(emptyLine)
+			if i < gap-1 {
+				b.WriteRune('\n')
+			}
+		}
+
+	case Bottom:
+		b.WriteString(strings.Repeat(emptyLine+"\n", gap))
+		b.WriteString(str)
+
+	default: // Somewhere in the middle
+		split := int(math.Round(float64(gap) * pos.value()))
+		top := gap - split
+		bottom := gap - top
+
+		b.WriteString(strings.Repeat(emptyLine+"\n", top))
+		b.WriteString(str)
+
+		for i := 0; i < bottom; i++ {
+			b.WriteRune('\n')
+			b.WriteString(emptyLine)
+		}
+	}
+
+	return b.String()
+}

+ 143 - 0
vendor/github.com/charmbracelet/lipgloss/renderer.go

@@ -0,0 +1,143 @@
+package lipgloss
+
+import (
+	"io"
+
+	"github.com/muesli/termenv"
+)
+
+// We're manually creating the struct here to avoid initializing the output and
+// query the terminal multiple times.
+var renderer = &Renderer{
+	output: termenv.DefaultOutput(),
+}
+
+// Renderer is a lipgloss terminal renderer.
+type Renderer struct {
+	output            *termenv.Output
+	hasDarkBackground *bool
+}
+
+// RendererOption is a function that can be used to configure a [Renderer].
+type RendererOption func(r *Renderer)
+
+// DefaultRenderer returns the default renderer.
+func DefaultRenderer() *Renderer {
+	return renderer
+}
+
+// SetDefaultRenderer sets the default global renderer.
+func SetDefaultRenderer(r *Renderer) {
+	renderer = r
+}
+
+// NewRenderer creates a new Renderer.
+//
+// w will be used to determine the terminal's color capabilities.
+func NewRenderer(w io.Writer, opts ...termenv.OutputOption) *Renderer {
+	r := &Renderer{
+		output: termenv.NewOutput(w, opts...),
+	}
+	return r
+}
+
+// Output returns the termenv output.
+func (r *Renderer) Output() *termenv.Output {
+	return r.output
+}
+
+// SetOutput sets the termenv output.
+func (r *Renderer) SetOutput(o *termenv.Output) {
+	r.output = o
+}
+
+// ColorProfile returns the detected termenv color profile.
+func (r *Renderer) ColorProfile() termenv.Profile {
+	return r.output.Profile
+}
+
+// ColorProfile returns the detected termenv color profile.
+func ColorProfile() termenv.Profile {
+	return renderer.ColorProfile()
+}
+
+// SetColorProfile sets the color profile on the renderer. This function exists
+// mostly for testing purposes so that you can assure you're testing against
+// a specific profile.
+//
+// Outside of testing you likely won't want to use this function as the color
+// profile will detect and cache the terminal's color capabilities and choose
+// the best available profile.
+//
+// Available color profiles are:
+//
+//	termenv.Ascii     // no color, 1-bit
+//	termenv.ANSI      //16 colors, 4-bit
+//	termenv.ANSI256   // 256 colors, 8-bit
+//	termenv.TrueColor // 16,777,216 colors, 24-bit
+//
+// This function is thread-safe.
+func (r *Renderer) SetColorProfile(p termenv.Profile) {
+	r.output.Profile = p
+}
+
+// SetColorProfile sets the color profile on the default renderer. This
+// function exists mostly for testing purposes so that you can assure you're
+// testing against a specific profile.
+//
+// Outside of testing you likely won't want to use this function as the color
+// profile will detect and cache the terminal's color capabilities and choose
+// the best available profile.
+//
+// Available color profiles are:
+//
+//	termenv.Ascii     // no color, 1-bit
+//	termenv.ANSI      //16 colors, 4-bit
+//	termenv.ANSI256   // 256 colors, 8-bit
+//	termenv.TrueColor // 16,777,216 colors, 24-bit
+//
+// This function is thread-safe.
+func SetColorProfile(p termenv.Profile) {
+	renderer.SetColorProfile(p)
+}
+
+// HasDarkBackground returns whether or not the terminal has a dark background.
+func HasDarkBackground() bool {
+	return renderer.HasDarkBackground()
+}
+
+// HasDarkBackground returns whether or not the renderer will render to a dark
+// background. A dark background can either be auto-detected, or set explicitly
+// on the renderer.
+func (r *Renderer) HasDarkBackground() bool {
+	if r.hasDarkBackground != nil {
+		return *r.hasDarkBackground
+	}
+	return r.output.HasDarkBackground()
+}
+
+// SetHasDarkBackground sets the background color detection value for the
+// default renderer. This function exists mostly for testing purposes so that
+// you can assure you're testing against a specific background color setting.
+//
+// Outside of testing you likely won't want to use this function as the
+// backgrounds value will be automatically detected and cached against the
+// terminal's current background color setting.
+//
+// This function is thread-safe.
+func SetHasDarkBackground(b bool) {
+	renderer.SetHasDarkBackground(b)
+}
+
+// SetHasDarkBackground sets the background color detection value on the
+// renderer. This function exists mostly for testing purposes so that you can
+// assure you're testing against a specific background color setting.
+//
+// Outside of testing you likely won't want to use this function as the
+// backgrounds value will be automatically detected and cached against the
+// terminal's current background color setting.
+//
+// This function is thread-safe.
+func (r *Renderer) SetHasDarkBackground(b bool) {
+	r.hasDarkBackground = &b
+}

+ 43 - 0
vendor/github.com/charmbracelet/lipgloss/runes.go

@@ -0,0 +1,43 @@
+package lipgloss
+
+import (
+	"strings"
+)
+
+// StyleRunes apply a given style to runes at the given indices in the string.
+// Note that you must provide styling options for both matched and unmatched
+// runes. Indices out of bounds will be ignored.
+func StyleRunes(str string, indices []int, matched, unmatched Style) string {
+	// Convert slice of indices to a map for easier lookups
+	m := make(map[int]struct{})
+	for _, i := range indices {
+		m[i] = struct{}{}
+	}
+
+	var (
+		out   strings.Builder
+		group strings.Builder
+		style Style
+		runes = []rune(str)
+	)
+
+	for i, r := range runes {
+		group.WriteRune(r)
+
+		_, matches := m[i]
+		_, nextMatches := m[i+1]
+
+		if matches != nextMatches || i == len(runes)-1 {
+			// Flush
+			if matches {
+				style = matched
+			} else {
+				style = unmatched
+			}
+			out.WriteString(style.Render(group.String()))
+			group.Reset()
+		}
+	}
+
+	return out.String()
+}

+ 634 - 0
vendor/github.com/charmbracelet/lipgloss/set.go

@@ -0,0 +1,634 @@
+package lipgloss
+
+// This could (should) probably just be moved into NewStyle(). We've broken it
+// out, so we can call it in a lazy way.
+func (s *Style) init() {
+	if s.rules == nil {
+		s.rules = make(rules)
+	}
+}
+
+// Set a value on the underlying rules map.
+func (s *Style) set(key propKey, value interface{}) {
+	s.init()
+
+	switch v := value.(type) {
+	case int:
+		// We don't allow negative integers on any of our values, so just keep
+		// them at zero or above. We could use uints instead, but the
+		// conversions are a little tedious, so we're sticking with ints for
+		// sake of usability.
+		s.rules[key] = max(0, v)
+	default:
+		s.rules[key] = v
+	}
+}
+
+// Bold sets a bold formatting rule.
+func (s Style) Bold(v bool) Style {
+	s.set(boldKey, v)
+	return s
+}
+
+// Italic sets an italic formatting rule. In some terminal emulators this will
+// render with "reverse" coloring if not italic font variant is available.
+func (s Style) Italic(v bool) Style {
+	s.set(italicKey, v)
+	return s
+}
+
+// Underline sets an underline rule. By default, underlines will not be drawn on
+// whitespace like margins and padding. To change this behavior set
+// UnderlineSpaces.
+func (s Style) Underline(v bool) Style {
+	s.set(underlineKey, v)
+	return s
+}
+
+// Strikethrough sets a strikethrough rule. By default, strikes will not be
+// drawn on whitespace like margins and padding. To change this behavior set
+// StrikethroughSpaces.
+func (s Style) Strikethrough(v bool) Style {
+	s.set(strikethroughKey, v)
+	return s
+}
+
+// Reverse sets a rule for inverting foreground and background colors.
+func (s Style) Reverse(v bool) Style {
+	s.set(reverseKey, v)
+	return s
+}
+
+// Blink sets a rule for blinking foreground text.
+func (s Style) Blink(v bool) Style {
+	s.set(blinkKey, v)
+	return s
+}
+
+// Faint sets a rule for rendering the foreground color in a dimmer shade.
+func (s Style) Faint(v bool) Style {
+	s.set(faintKey, v)
+	return s
+}
+
+// Foreground sets a foreground color.
+//
+//	// Sets the foreground to blue
+//	s := lipgloss.NewStyle().Foreground(lipgloss.Color("#0000ff"))
+//
+//	// Removes the foreground color
+//	s.Foreground(lipgloss.NoColor)
+func (s Style) Foreground(c TerminalColor) Style {
+	s.set(foregroundKey, c)
+	return s
+}
+
+// Background sets a background color.
+func (s Style) Background(c TerminalColor) Style {
+	s.set(backgroundKey, c)
+	return s
+}
+
+// Width sets the width of the block before applying margins. The width, if
+// set, also determines where text will wrap.
+func (s Style) Width(i int) Style {
+	s.set(widthKey, i)
+	return s
+}
+
+// Height sets the height of the block before applying margins. If the height of
+// the text block is less than this value after applying padding (or not), the
+// block will be set to this height.
+func (s Style) Height(i int) Style {
+	s.set(heightKey, i)
+	return s
+}
+
+// Align is a shorthand method for setting horizontal and vertical alignment.
+//
+// With one argument, the position value is applied to the horizontal alignment.
+//
+// With two arguments, the value is applied to the vertical and horizontal
+// alignments, in that order.
+func (s Style) Align(p ...Position) Style {
+	if len(p) > 0 {
+		s.set(alignHorizontalKey, p[0])
+	}
+	if len(p) > 1 {
+		s.set(alignVerticalKey, p[1])
+	}
+	return s
+}
+
+// AlignHorizontal sets a horizontal text alignment rule.
+func (s Style) AlignHorizontal(p Position) Style {
+	s.set(alignHorizontalKey, p)
+	return s
+}
+
+// AlignVertical sets a text alignment rule.
+func (s Style) AlignVertical(p Position) Style {
+	s.set(alignVerticalKey, p)
+	return s
+}
+
+// Padding is a shorthand method for setting padding on all sides at once.
+//
+// With one argument, the value is applied to all sides.
+//
+// With two arguments, the value is applied to the vertical and horizontal
+// sides, in that order.
+//
+// With three arguments, the value is applied to the top side, the horizontal
+// sides, and the bottom side, in that order.
+//
+// With four arguments, the value is applied clockwise starting from the top
+// side, followed by the right side, then the bottom, and finally the left.
+//
+// With more than four arguments no padding will be added.
+func (s Style) Padding(i ...int) Style {
+	top, right, bottom, left, ok := whichSidesInt(i...)
+	if !ok {
+		return s
+	}
+
+	s.set(paddingTopKey, top)
+	s.set(paddingRightKey, right)
+	s.set(paddingBottomKey, bottom)
+	s.set(paddingLeftKey, left)
+	return s
+}
+
+// PaddingLeft adds padding on the left.
+func (s Style) PaddingLeft(i int) Style {
+	s.set(paddingLeftKey, i)
+	return s
+}
+
+// PaddingRight adds padding on the right.
+func (s Style) PaddingRight(i int) Style {
+	s.set(paddingRightKey, i)
+	return s
+}
+
+// PaddingTop adds padding to the top of the block.
+func (s Style) PaddingTop(i int) Style {
+	s.set(paddingTopKey, i)
+	return s
+}
+
+// PaddingBottom adds padding to the bottom of the block.
+func (s Style) PaddingBottom(i int) Style {
+	s.set(paddingBottomKey, i)
+	return s
+}
+
+// ColorWhitespace determines whether or not the background color should be
+// applied to the padding. This is true by default as it's more than likely the
+// desired and expected behavior, but it can be disabled for certain graphic
+// effects.
+func (s Style) ColorWhitespace(v bool) Style {
+	s.set(colorWhitespaceKey, v)
+	return s
+}
+
+// Margin is a shorthand method for setting margins on all sides at once.
+//
+// With one argument, the value is applied to all sides.
+//
+// With two arguments, the value is applied to the vertical and horizontal
+// sides, in that order.
+//
+// With three arguments, the value is applied to the top side, the horizontal
+// sides, and the bottom side, in that order.
+//
+// With four arguments, the value is applied clockwise starting from the top
+// side, followed by the right side, then the bottom, and finally the left.
+//
+// With more than four arguments no margin will be added.
+func (s Style) Margin(i ...int) Style {
+	top, right, bottom, left, ok := whichSidesInt(i...)
+	if !ok {
+		return s
+	}
+
+	s.set(marginTopKey, top)
+	s.set(marginRightKey, right)
+	s.set(marginBottomKey, bottom)
+	s.set(marginLeftKey, left)
+	return s
+}
+
+// MarginLeft sets the value of the left margin.
+func (s Style) MarginLeft(i int) Style {
+	s.set(marginLeftKey, i)
+	return s
+}
+
+// MarginRight sets the value of the right margin.
+func (s Style) MarginRight(i int) Style {
+	s.set(marginRightKey, i)
+	return s
+}
+
+// MarginTop sets the value of the top margin.
+func (s Style) MarginTop(i int) Style {
+	s.set(marginTopKey, i)
+	return s
+}
+
+// MarginBottom sets the value of the bottom margin.
+func (s Style) MarginBottom(i int) Style {
+	s.set(marginBottomKey, i)
+	return s
+}
+
+// MarginBackground sets the background color of the margin. Note that this is
+// also set when inheriting from a style with a background color. In that case
+// the background color on that style will set the margin color on this style.
+func (s Style) MarginBackground(c TerminalColor) Style {
+	s.set(marginBackgroundKey, c)
+	return s
+}
+
+// Border is shorthand for setting the border style and which sides should
+// have a border at once. The variadic argument sides works as follows:
+//
+// With one value, the value is applied to all sides.
+//
+// With two values, the values are applied to the vertical and horizontal
+// sides, in that order.
+//
+// With three values, the values are applied to the top side, the horizontal
+// sides, and the bottom side, in that order.
+//
+// With four values, the values are applied clockwise starting from the top
+// side, followed by the right side, then the bottom, and finally the left.
+//
+// With more than four arguments the border will be applied to all sides.
+//
+// Examples:
+//
+//	// Applies borders to the top and bottom only
+//	lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true, false)
+//
+//	// Applies rounded borders to the right and bottom only
+//	lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), false, true, true, false)
+func (s Style) Border(b Border, sides ...bool) Style {
+	s.set(borderStyleKey, b)
+
+	top, right, bottom, left, ok := whichSidesBool(sides...)
+	if !ok {
+		top = true
+		right = true
+		bottom = true
+		left = true
+	}
+
+	s.set(borderTopKey, top)
+	s.set(borderRightKey, right)
+	s.set(borderBottomKey, bottom)
+	s.set(borderLeftKey, left)
+
+	return s
+}
+
+// BorderStyle defines the Border on a style. A Border contains a series of
+// definitions for the sides and corners of a border.
+//
+// Note that if border visibility has not been set for any sides when setting
+// the border style, the border will be enabled for all sides during rendering.
+//
+// You can define border characters as you'd like, though several default
+// styles are included: NormalBorder(), RoundedBorder(), BlockBorder(),
+// OuterHalfBlockBorder(), InnerHalfBlockBorder(), ThickBorder(),
+// and DoubleBorder().
+//
+// Example:
+//
+//	lipgloss.NewStyle().BorderStyle(lipgloss.ThickBorder())
+func (s Style) BorderStyle(b Border) Style {
+	s.set(borderStyleKey, b)
+	return s
+}
+
+// BorderTop determines whether or not to draw a top border.
+func (s Style) BorderTop(v bool) Style {
+	s.set(borderTopKey, v)
+	return s
+}
+
+// BorderRight determines whether or not to draw a right border.
+func (s Style) BorderRight(v bool) Style {
+	s.set(borderRightKey, v)
+	return s
+}
+
+// BorderBottom determines whether or not to draw a bottom border.
+func (s Style) BorderBottom(v bool) Style {
+	s.set(borderBottomKey, v)
+	return s
+}
+
+// BorderLeft determines whether or not to draw a left border.
+func (s Style) BorderLeft(v bool) Style {
+	s.set(borderLeftKey, v)
+	return s
+}
+
+// BorderForeground is a shorthand function for setting all of the
+// foreground colors of the borders at once. The arguments work as follows:
+//
+// With one argument, the argument is applied to all sides.
+//
+// With two arguments, the arguments are applied to the vertical and horizontal
+// sides, in that order.
+//
+// With three arguments, the arguments are applied to the top side, the
+// horizontal sides, and the bottom side, in that order.
+//
+// With four arguments, the arguments are applied clockwise starting from the
+// top side, followed by the right side, then the bottom, and finally the left.
+//
+// With more than four arguments nothing will be set.
+func (s Style) BorderForeground(c ...TerminalColor) Style {
+	if len(c) == 0 {
+		return s
+	}
+
+	top, right, bottom, left, ok := whichSidesColor(c...)
+	if !ok {
+		return s
+	}
+
+	s.set(borderTopForegroundKey, top)
+	s.set(borderRightForegroundKey, right)
+	s.set(borderBottomForegroundKey, bottom)
+	s.set(borderLeftForegroundKey, left)
+
+	return s
+}
+
+// BorderTopForeground set the foreground color for the top of the border.
+func (s Style) BorderTopForeground(c TerminalColor) Style {
+	s.set(borderTopForegroundKey, c)
+	return s
+}
+
+// BorderRightForeground sets the foreground color for the right side of the
+// border.
+func (s Style) BorderRightForeground(c TerminalColor) Style {
+	s.set(borderRightForegroundKey, c)
+	return s
+}
+
+// BorderBottomForeground sets the foreground color for the bottom of the
+// border.
+func (s Style) BorderBottomForeground(c TerminalColor) Style {
+	s.set(borderBottomForegroundKey, c)
+	return s
+}
+
+// BorderLeftForeground sets the foreground color for the left side of the
+// border.
+func (s Style) BorderLeftForeground(c TerminalColor) Style {
+	s.set(borderLeftForegroundKey, c)
+	return s
+}
+
+// BorderBackground is a shorthand function for setting all of the
+// background colors of the borders at once. The arguments work as follows:
+//
+// With one argument, the argument is applied to all sides.
+//
+// With two arguments, the arguments are applied to the vertical and horizontal
+// sides, in that order.
+//
+// With three arguments, the arguments are applied to the top side, the
+// horizontal sides, and the bottom side, in that order.
+//
+// With four arguments, the arguments are applied clockwise starting from the
+// top side, followed by the right side, then the bottom, and finally the left.
+//
+// With more than four arguments nothing will be set.
+func (s Style) BorderBackground(c ...TerminalColor) Style {
+	if len(c) == 0 {
+		return s
+	}
+
+	top, right, bottom, left, ok := whichSidesColor(c...)
+	if !ok {
+		return s
+	}
+
+	s.set(borderTopBackgroundKey, top)
+	s.set(borderRightBackgroundKey, right)
+	s.set(borderBottomBackgroundKey, bottom)
+	s.set(borderLeftBackgroundKey, left)
+
+	return s
+}
+
+// BorderTopBackground sets the background color of the top of the border.
+func (s Style) BorderTopBackground(c TerminalColor) Style {
+	s.set(borderTopBackgroundKey, c)
+	return s
+}
+
+// BorderRightBackground sets the background color of right side the border.
+func (s Style) BorderRightBackground(c TerminalColor) Style {
+	s.set(borderRightBackgroundKey, c)
+	return s
+}
+
+// BorderBottomBackground sets the background color of the bottom of the
+// border.
+func (s Style) BorderBottomBackground(c TerminalColor) Style {
+	s.set(borderBottomBackgroundKey, c)
+	return s
+}
+
+// BorderLeftBackground set the background color of the left side of the
+// border.
+func (s Style) BorderLeftBackground(c TerminalColor) Style {
+	s.set(borderLeftBackgroundKey, c)
+	return s
+}
+
+// Inline makes rendering output one line and disables the rendering of
+// margins, padding and borders. This is useful when you need a style to apply
+// only to font rendering and don't want it to change any physical dimensions.
+// It works well with Style.MaxWidth.
+//
+// Because this in intended to be used at the time of render, this method will
+// not mutate the style and instead return a copy.
+//
+// Example:
+//
+//	var userInput string = "..."
+//	var userStyle = text.Style{ /* ... */ }
+//	fmt.Println(userStyle.Inline(true).Render(userInput))
+func (s Style) Inline(v bool) Style {
+	o := s.Copy()
+	o.set(inlineKey, v)
+	return o
+}
+
+// MaxWidth applies a max width to a given style. This is useful in enforcing
+// a certain width at render time, particularly with arbitrary strings and
+// styles.
+//
+// Because this in intended to be used at the time of render, this method will
+// not mutate the style and instead return a copy.
+//
+// Example:
+//
+//	var userInput string = "..."
+//	var userStyle = text.Style{ /* ... */ }
+//	fmt.Println(userStyle.MaxWidth(16).Render(userInput))
+func (s Style) MaxWidth(n int) Style {
+	o := s.Copy()
+	o.set(maxWidthKey, n)
+	return o
+}
+
+// MaxHeight applies a max height to a given style. This is useful in enforcing
+// a certain height at render time, particularly with arbitrary strings and
+// styles.
+//
+// Because this in intended to be used at the time of render, this method will
+// not mutate the style and instead return a copy.
+func (s Style) MaxHeight(n int) Style {
+	o := s.Copy()
+	o.set(maxHeightKey, n)
+	return o
+}
+
+// UnderlineSpaces determines whether to underline spaces between words. By
+// default, this is true. Spaces can also be underlined without underlining the
+// text itself.
+func (s Style) UnderlineSpaces(v bool) Style {
+	s.set(underlineSpacesKey, v)
+	return s
+}
+
+// StrikethroughSpaces determines whether to apply strikethroughs to spaces
+// between words. By default, this is true. Spaces can also be struck without
+// underlining the text itself.
+func (s Style) StrikethroughSpaces(v bool) Style {
+	s.set(strikethroughSpacesKey, v)
+	return s
+}
+
+// Renderer sets the renderer for the style. This is useful for changing the
+// renderer for a style that is being used in a different context.
+func (s Style) Renderer(r *Renderer) Style {
+	s.r = r
+	return s
+}
+
+// whichSidesInt is a helper method for setting values on sides of a block based
+// on the number of arguments. It follows the CSS shorthand rules for blocks
+// like margin, padding. and borders. Here are how the rules work:
+//
+// 0 args:  do nothing
+// 1 arg:   all sides
+// 2 args:  top -> bottom
+// 3 args:  top -> horizontal -> bottom
+// 4 args:  top -> right -> bottom -> left
+// 5+ args: do nothing.
+func whichSidesInt(i ...int) (top, right, bottom, left int, ok bool) {
+	switch len(i) {
+	case 1:
+		top = i[0]
+		bottom = i[0]
+		left = i[0]
+		right = i[0]
+		ok = true
+	case 2:
+		top = i[0]
+		bottom = i[0]
+		left = i[1]
+		right = i[1]
+		ok = true
+	case 3:
+		top = i[0]
+		left = i[1]
+		right = i[1]
+		bottom = i[2]
+		ok = true
+	case 4:
+		top = i[0]
+		right = i[1]
+		bottom = i[2]
+		left = i[3]
+		ok = true
+	}
+	return top, right, bottom, left, ok
+}
+
+// whichSidesBool is like whichSidesInt, except it operates on a series of
+// boolean values. See the comment on whichSidesInt for details on how this
+// works.
+func whichSidesBool(i ...bool) (top, right, bottom, left bool, ok bool) {
+	switch len(i) {
+	case 1:
+		top = i[0]
+		bottom = i[0]
+		left = i[0]
+		right = i[0]
+		ok = true
+	case 2:
+		top = i[0]
+		bottom = i[0]
+		left = i[1]
+		right = i[1]
+		ok = true
+	case 3:
+		top = i[0]
+		left = i[1]
+		right = i[1]
+		bottom = i[2]
+		ok = true
+	case 4:
+		top = i[0]
+		right = i[1]
+		bottom = i[2]
+		left = i[3]
+		ok = true
+	}
+	return top, right, bottom, left, ok
+}
+
+// whichSidesColor is like whichSides, except it operates on a series of
+// boolean values. See the comment on whichSidesInt for details on how this
+// works.
+func whichSidesColor(i ...TerminalColor) (top, right, bottom, left TerminalColor, ok bool) {
+	switch len(i) {
+	case 1:
+		top = i[0]
+		bottom = i[0]
+		left = i[0]
+		right = i[0]
+		ok = true
+	case 2:
+		top = i[0]
+		bottom = i[0]
+		left = i[1]
+		right = i[1]
+		ok = true
+	case 3:
+		top = i[0]
+		left = i[1]
+		right = i[1]
+		bottom = i[2]
+		ok = true
+	case 4:
+		top = i[0]
+		right = i[1]
+		bottom = i[2]
+		left = i[3]
+		ok = true
+	}
+	return top, right, bottom, left, ok
+}

+ 41 - 0
vendor/github.com/charmbracelet/lipgloss/size.go

@@ -0,0 +1,41 @@
+package lipgloss
+
+import (
+	"strings"
+
+	"github.com/muesli/reflow/ansi"
+)
+
+// Width returns the cell width of characters in the string. ANSI sequences are
+// ignored and characters wider than one cell (such as Chinese characters and
+// emojis) are appropriately measured.
+//
+// You should use this instead of len(string) len([]rune(string) as neither
+// will give you accurate results.
+func Width(str string) (width int) {
+	for _, l := range strings.Split(str, "\n") {
+		w := ansi.PrintableRuneWidth(l)
+		if w > width {
+			width = w
+		}
+	}
+
+	return width
+}
+
+// Height returns height of a string in cells. This is done simply by
+// counting \n characters. If your strings use \r\n for newlines you should
+// convert them to \n first, or simply write a separate function for measuring
+// height.
+func Height(str string) int {
+	return strings.Count(str, "\n") + 1
+}
+
+// Size returns the width and height of the string in cells. ANSI sequences are
+// ignored and characters wider than one cell (such as Chinese characters and
+// emojis) are appropriately measured.
+func Size(str string) (width, height int) {
+	width = Width(str)
+	height = Height(str)
+	return width, height
+}

+ 497 - 0
vendor/github.com/charmbracelet/lipgloss/style.go

@@ -0,0 +1,497 @@
+package lipgloss
+
+import (
+	"strings"
+	"unicode"
+
+	"github.com/muesli/reflow/truncate"
+	"github.com/muesli/reflow/wordwrap"
+	"github.com/muesli/reflow/wrap"
+	"github.com/muesli/termenv"
+)
+
+// Property for a key.
+type propKey int
+
+// Available properties.
+const (
+	boldKey propKey = iota
+	italicKey
+	underlineKey
+	strikethroughKey
+	reverseKey
+	blinkKey
+	faintKey
+	foregroundKey
+	backgroundKey
+	widthKey
+	heightKey
+	alignHorizontalKey
+	alignVerticalKey
+
+	// Padding.
+	paddingTopKey
+	paddingRightKey
+	paddingBottomKey
+	paddingLeftKey
+
+	colorWhitespaceKey
+
+	// Margins.
+	marginTopKey
+	marginRightKey
+	marginBottomKey
+	marginLeftKey
+	marginBackgroundKey
+
+	// Border runes.
+	borderStyleKey
+
+	// Border edges.
+	borderTopKey
+	borderRightKey
+	borderBottomKey
+	borderLeftKey
+
+	// Border foreground colors.
+	borderTopForegroundKey
+	borderRightForegroundKey
+	borderBottomForegroundKey
+	borderLeftForegroundKey
+
+	// Border background colors.
+	borderTopBackgroundKey
+	borderRightBackgroundKey
+	borderBottomBackgroundKey
+	borderLeftBackgroundKey
+
+	inlineKey
+	maxWidthKey
+	maxHeightKey
+	underlineSpacesKey
+	strikethroughSpacesKey
+)
+
+// A set of properties.
+type rules map[propKey]interface{}
+
+// NewStyle returns a new, empty Style. While it's syntactic sugar for the
+// Style{} primitive, it's recommended to use this function for creating styles
+// in case the underlying implementation changes. It takes an optional string
+// value to be set as the underlying string value for this style.
+func NewStyle() Style {
+	return renderer.NewStyle()
+}
+
+// NewStyle returns a new, empty Style. While it's syntactic sugar for the
+// Style{} primitive, it's recommended to use this function for creating styles
+// in case the underlying implementation changes. It takes an optional string
+// value to be set as the underlying string value for this style.
+func (r *Renderer) NewStyle() Style {
+	s := Style{r: r}
+	return s
+}
+
+// Style contains a set of rules that comprise a style as a whole.
+type Style struct {
+	r     *Renderer
+	rules map[propKey]interface{}
+	value string
+}
+
+// joinString joins a list of strings into a single string separated with a
+// space.
+func joinString(strs ...string) string {
+	return strings.Join(strs, " ")
+}
+
+// SetString sets the underlying string value for this style. To render once
+// the underlying string is set, use the Style.String. This method is
+// a convenience for cases when having a stringer implementation is handy, such
+// as when using fmt.Sprintf. You can also simply define a style and render out
+// strings directly with Style.Render.
+func (s Style) SetString(strs ...string) Style {
+	s.value = joinString(strs...)
+	return s
+}
+
+// Value returns the raw, unformatted, underlying string value for this style.
+func (s Style) Value() string {
+	return s.value
+}
+
+// String implements stringer for a Style, returning the rendered result based
+// on the rules in this style. An underlying string value must be set with
+// Style.SetString prior to using this method.
+func (s Style) String() string {
+	return s.Render()
+}
+
+// Copy returns a copy of this style, including any underlying string values.
+func (s Style) Copy() Style {
+	o := NewStyle()
+	o.init()
+	for k, v := range s.rules {
+		o.rules[k] = v
+	}
+	o.r = s.r
+	o.value = s.value
+	return o
+}
+
+// Inherit overlays the style in the argument onto this style by copying each explicitly
+// set value from the argument style onto this style if it is not already explicitly set.
+// Existing set values are kept intact and not overwritten.
+//
+// Margins, padding, and underlying string values are not inherited.
+func (s Style) Inherit(i Style) Style {
+	s.init()
+
+	for k, v := range i.rules {
+		switch k {
+		case marginTopKey, marginRightKey, marginBottomKey, marginLeftKey:
+			// Margins are not inherited
+			continue
+		case paddingTopKey, paddingRightKey, paddingBottomKey, paddingLeftKey:
+			// Padding is not inherited
+			continue
+		case backgroundKey:
+			// The margins also inherit the background color
+			if !s.isSet(marginBackgroundKey) && !i.isSet(marginBackgroundKey) {
+				s.rules[marginBackgroundKey] = v
+			}
+		}
+
+		if _, exists := s.rules[k]; exists {
+			continue
+		}
+		s.rules[k] = v
+	}
+	return s
+}
+
+// Render applies the defined style formatting to a given string.
+func (s Style) Render(strs ...string) string {
+	if s.r == nil {
+		s.r = renderer
+	}
+	if s.value != "" {
+		strs = append([]string{s.value}, strs...)
+	}
+
+	var (
+		str = joinString(strs...)
+
+		te           = s.r.ColorProfile().String()
+		teSpace      = s.r.ColorProfile().String()
+		teWhitespace = s.r.ColorProfile().String()
+
+		bold          = s.getAsBool(boldKey, false)
+		italic        = s.getAsBool(italicKey, false)
+		underline     = s.getAsBool(underlineKey, false)
+		strikethrough = s.getAsBool(strikethroughKey, false)
+		reverse       = s.getAsBool(reverseKey, false)
+		blink         = s.getAsBool(blinkKey, false)
+		faint         = s.getAsBool(faintKey, false)
+
+		fg = s.getAsColor(foregroundKey)
+		bg = s.getAsColor(backgroundKey)
+
+		width           = s.getAsInt(widthKey)
+		height          = s.getAsInt(heightKey)
+		horizontalAlign = s.getAsPosition(alignHorizontalKey)
+		verticalAlign   = s.getAsPosition(alignVerticalKey)
+
+		topPadding    = s.getAsInt(paddingTopKey)
+		rightPadding  = s.getAsInt(paddingRightKey)
+		bottomPadding = s.getAsInt(paddingBottomKey)
+		leftPadding   = s.getAsInt(paddingLeftKey)
+
+		colorWhitespace = s.getAsBool(colorWhitespaceKey, true)
+		inline          = s.getAsBool(inlineKey, false)
+		maxWidth        = s.getAsInt(maxWidthKey)
+		maxHeight       = s.getAsInt(maxHeightKey)
+
+		underlineSpaces     = underline && s.getAsBool(underlineSpacesKey, true)
+		strikethroughSpaces = strikethrough && s.getAsBool(strikethroughSpacesKey, true)
+
+		// Do we need to style whitespace (padding and space outside
+		// paragraphs) separately?
+		styleWhitespace = reverse
+
+		// Do we need to style spaces separately?
+		useSpaceStyler = underlineSpaces || strikethroughSpaces
+	)
+
+	if len(s.rules) == 0 {
+		return str
+	}
+
+	// Enable support for ANSI on the legacy Windows cmd.exe console. This is a
+	// no-op on non-Windows systems and on Windows runs only once.
+	enableLegacyWindowsANSI()
+
+	if bold {
+		te = te.Bold()
+	}
+	if italic {
+		te = te.Italic()
+	}
+	if underline {
+		te = te.Underline()
+	}
+	if reverse {
+		if reverse {
+			teWhitespace = teWhitespace.Reverse()
+		}
+		te = te.Reverse()
+	}
+	if blink {
+		te = te.Blink()
+	}
+	if faint {
+		te = te.Faint()
+	}
+
+	if fg != noColor {
+		te = te.Foreground(fg.color(s.r))
+		if styleWhitespace {
+			teWhitespace = teWhitespace.Foreground(fg.color(s.r))
+		}
+		if useSpaceStyler {
+			teSpace = teSpace.Foreground(fg.color(s.r))
+		}
+	}
+
+	if bg != noColor {
+		te = te.Background(bg.color(s.r))
+		if colorWhitespace {
+			teWhitespace = teWhitespace.Background(bg.color(s.r))
+		}
+		if useSpaceStyler {
+			teSpace = teSpace.Background(bg.color(s.r))
+		}
+	}
+
+	if underline {
+		te = te.Underline()
+	}
+	if strikethrough {
+		te = te.CrossOut()
+	}
+
+	if underlineSpaces {
+		teSpace = teSpace.Underline()
+	}
+	if strikethroughSpaces {
+		teSpace = teSpace.CrossOut()
+	}
+
+	// Strip newlines in single line mode
+	if inline {
+		str = strings.ReplaceAll(str, "\n", "")
+	}
+
+	// Word wrap
+	if !inline && width > 0 {
+		wrapAt := width - leftPadding - rightPadding
+		str = wordwrap.String(str, wrapAt)
+		str = wrap.String(str, wrapAt) // force-wrap long strings
+	}
+
+	// Render core text
+	{
+		var b strings.Builder
+
+		l := strings.Split(str, "\n")
+		for i := range l {
+			if useSpaceStyler {
+				// Look for spaces and apply a different styler
+				for _, r := range l[i] {
+					if unicode.IsSpace(r) {
+						b.WriteString(teSpace.Styled(string(r)))
+						continue
+					}
+					b.WriteString(te.Styled(string(r)))
+				}
+			} else {
+				b.WriteString(te.Styled(l[i]))
+			}
+			if i != len(l)-1 {
+				b.WriteRune('\n')
+			}
+		}
+
+		str = b.String()
+	}
+
+	// Padding
+	if !inline {
+		if leftPadding > 0 {
+			var st *termenv.Style
+			if colorWhitespace || styleWhitespace {
+				st = &teWhitespace
+			}
+			str = padLeft(str, leftPadding, st)
+		}
+
+		if rightPadding > 0 {
+			var st *termenv.Style
+			if colorWhitespace || styleWhitespace {
+				st = &teWhitespace
+			}
+			str = padRight(str, rightPadding, st)
+		}
+
+		if topPadding > 0 {
+			str = strings.Repeat("\n", topPadding) + str
+		}
+
+		if bottomPadding > 0 {
+			str += strings.Repeat("\n", bottomPadding)
+		}
+	}
+
+	// Height
+	if height > 0 {
+		str = alignTextVertical(str, verticalAlign, height, nil)
+	}
+
+	// Set alignment. This will also pad short lines with spaces so that all
+	// lines are the same length, so we run it under a few different conditions
+	// beyond alignment.
+	{
+		numLines := strings.Count(str, "\n")
+
+		if !(numLines == 0 && width == 0) {
+			var st *termenv.Style
+			if colorWhitespace || styleWhitespace {
+				st = &teWhitespace
+			}
+			str = alignTextHorizontal(str, horizontalAlign, width, st)
+		}
+	}
+
+	if !inline {
+		str = s.applyBorder(str)
+		str = s.applyMargins(str, inline)
+	}
+
+	// Truncate according to MaxWidth
+	if maxWidth > 0 {
+		lines := strings.Split(str, "\n")
+
+		for i := range lines {
+			lines[i] = truncate.String(lines[i], uint(maxWidth))
+		}
+
+		str = strings.Join(lines, "\n")
+	}
+
+	// Truncate according to MaxHeight
+	if maxHeight > 0 {
+		lines := strings.Split(str, "\n")
+		str = strings.Join(lines[:min(maxHeight, len(lines))], "\n")
+	}
+
+	return str
+}
+
+func (s Style) applyMargins(str string, inline bool) string {
+	var (
+		topMargin    = s.getAsInt(marginTopKey)
+		rightMargin  = s.getAsInt(marginRightKey)
+		bottomMargin = s.getAsInt(marginBottomKey)
+		leftMargin   = s.getAsInt(marginLeftKey)
+
+		styler termenv.Style
+	)
+
+	bgc := s.getAsColor(marginBackgroundKey)
+	if bgc != noColor {
+		styler = styler.Background(bgc.color(s.r))
+	}
+
+	// Add left and right margin
+	str = padLeft(str, leftMargin, &styler)
+	str = padRight(str, rightMargin, &styler)
+
+	// Top/bottom margin
+	if !inline {
+		_, width := getLines(str)
+		spaces := strings.Repeat(" ", width)
+
+		if topMargin > 0 {
+			str = styler.Styled(strings.Repeat(spaces+"\n", topMargin)) + str
+		}
+		if bottomMargin > 0 {
+			str += styler.Styled(strings.Repeat("\n"+spaces, bottomMargin))
+		}
+	}
+
+	return str
+}
+
+// Apply left padding.
+func padLeft(str string, n int, style *termenv.Style) string {
+	if n == 0 {
+		return str
+	}
+
+	sp := strings.Repeat(" ", n)
+	if style != nil {
+		sp = style.Styled(sp)
+	}
+
+	b := strings.Builder{}
+	l := strings.Split(str, "\n")
+
+	for i := range l {
+		b.WriteString(sp)
+		b.WriteString(l[i])
+		if i != len(l)-1 {
+			b.WriteRune('\n')
+		}
+	}
+
+	return b.String()
+}
+
+// Apply right padding.
+func padRight(str string, n int, style *termenv.Style) string {
+	if n == 0 || str == "" {
+		return str
+	}
+
+	sp := strings.Repeat(" ", n)
+	if style != nil {
+		sp = style.Styled(sp)
+	}
+
+	b := strings.Builder{}
+	l := strings.Split(str, "\n")
+
+	for i := range l {
+		b.WriteString(l[i])
+		b.WriteString(sp)
+		if i != len(l)-1 {
+			b.WriteRune('\n')
+		}
+	}
+
+	return b.String()
+}
+
+func max(a, b int) int {
+	if a > b {
+		return a
+	}
+	return b
+}
+
+func min(a, b int) int {
+	if a < b {
+		return a
+	}
+	return b
+}

+ 306 - 0
vendor/github.com/charmbracelet/lipgloss/unset.go

@@ -0,0 +1,306 @@
+package lipgloss
+
+// UnsetBold removes the bold style rule, if set.
+func (s Style) UnsetBold() Style {
+	delete(s.rules, boldKey)
+	return s
+}
+
+// UnsetItalic removes the italic style rule, if set.
+func (s Style) UnsetItalic() Style {
+	delete(s.rules, italicKey)
+	return s
+}
+
+// UnsetUnderline removes the underline style rule, if set.
+func (s Style) UnsetUnderline() Style {
+	delete(s.rules, underlineKey)
+	return s
+}
+
+// UnsetStrikethrough removes the strikethrough style rule, if set.
+func (s Style) UnsetStrikethrough() Style {
+	delete(s.rules, strikethroughKey)
+	return s
+}
+
+// UnsetReverse removes the reverse style rule, if set.
+func (s Style) UnsetReverse() Style {
+	delete(s.rules, reverseKey)
+	return s
+}
+
+// UnsetBlink removes the bold style rule, if set.
+func (s Style) UnsetBlink() Style {
+	delete(s.rules, blinkKey)
+	return s
+}
+
+// UnsetFaint removes the faint style rule, if set.
+func (s Style) UnsetFaint() Style {
+	delete(s.rules, faintKey)
+	return s
+}
+
+// UnsetForeground removes the foreground style rule, if set.
+func (s Style) UnsetForeground() Style {
+	delete(s.rules, foregroundKey)
+	return s
+}
+
+// UnsetBackground removes the background style rule, if set.
+func (s Style) UnsetBackground() Style {
+	delete(s.rules, backgroundKey)
+	return s
+}
+
+// UnsetWidth removes the width style rule, if set.
+func (s Style) UnsetWidth() Style {
+	delete(s.rules, widthKey)
+	return s
+}
+
+// UnsetHeight removes the height style rule, if set.
+func (s Style) UnsetHeight() Style {
+	delete(s.rules, heightKey)
+	return s
+}
+
+// UnsetAlign removes the horizontal and vertical text alignment style rule, if set.
+func (s Style) UnsetAlign() Style {
+	delete(s.rules, alignHorizontalKey)
+	delete(s.rules, alignVerticalKey)
+	return s
+}
+
+// UnsetAlignHorizontal removes the horizontal text alignment style rule, if set.
+func (s Style) UnsetAlignHorizontal() Style {
+	delete(s.rules, alignHorizontalKey)
+	return s
+}
+
+// UnsetAlignVertical removes the vertical text alignment style rule, if set.
+func (s Style) UnsetAlignVertical() Style {
+	delete(s.rules, alignVerticalKey)
+	return s
+}
+
+// UnsetPadding removes all padding style rules.
+func (s Style) UnsetPadding() Style {
+	delete(s.rules, paddingLeftKey)
+	delete(s.rules, paddingRightKey)
+	delete(s.rules, paddingTopKey)
+	delete(s.rules, paddingBottomKey)
+	return s
+}
+
+// UnsetPaddingLeft removes the left padding style rule, if set.
+func (s Style) UnsetPaddingLeft() Style {
+	delete(s.rules, paddingLeftKey)
+	return s
+}
+
+// UnsetPaddingRight removes the right padding style rule, if set.
+func (s Style) UnsetPaddingRight() Style {
+	delete(s.rules, paddingRightKey)
+	return s
+}
+
+// UnsetPaddingTop removes the top padding style rule, if set.
+func (s Style) UnsetPaddingTop() Style {
+	delete(s.rules, paddingTopKey)
+	return s
+}
+
+// UnsetPaddingBottom removes the bottom style rule, if set.
+func (s Style) UnsetPaddingBottom() Style {
+	delete(s.rules, paddingBottomKey)
+	return s
+}
+
+// UnsetColorWhitespace removes the rule for coloring padding, if set.
+func (s Style) UnsetColorWhitespace() Style {
+	delete(s.rules, colorWhitespaceKey)
+	return s
+}
+
+// UnsetMargins removes all margin style rules.
+func (s Style) UnsetMargins() Style {
+	delete(s.rules, marginLeftKey)
+	delete(s.rules, marginRightKey)
+	delete(s.rules, marginTopKey)
+	delete(s.rules, marginBottomKey)
+	return s
+}
+
+// UnsetMarginLeft removes the left margin style rule, if set.
+func (s Style) UnsetMarginLeft() Style {
+	delete(s.rules, marginLeftKey)
+	return s
+}
+
+// UnsetMarginRight removes the right margin style rule, if set.
+func (s Style) UnsetMarginRight() Style {
+	delete(s.rules, marginRightKey)
+	return s
+}
+
+// UnsetMarginTop removes the top margin style rule, if set.
+func (s Style) UnsetMarginTop() Style {
+	delete(s.rules, marginTopKey)
+	return s
+}
+
+// UnsetMarginBottom removes the bottom margin style rule, if set.
+func (s Style) UnsetMarginBottom() Style {
+	delete(s.rules, marginBottomKey)
+	return s
+}
+
+// UnsetMarginBackground removes the margin's background color. Note that the
+// margin's background color can be set from the background color of another
+// style during inheritance.
+func (s Style) UnsetMarginBackground() Style {
+	delete(s.rules, marginBackgroundKey)
+	return s
+}
+
+// UnsetBorderStyle removes the border style rule, if set.
+func (s Style) UnsetBorderStyle() Style {
+	delete(s.rules, borderStyleKey)
+	return s
+}
+
+// UnsetBorderTop removes the border top style rule, if set.
+func (s Style) UnsetBorderTop() Style {
+	delete(s.rules, borderTopKey)
+	return s
+}
+
+// UnsetBorderRight removes the border right style rule, if set.
+func (s Style) UnsetBorderRight() Style {
+	delete(s.rules, borderRightKey)
+	return s
+}
+
+// UnsetBorderBottom removes the border bottom style rule, if set.
+func (s Style) UnsetBorderBottom() Style {
+	delete(s.rules, borderBottomKey)
+	return s
+}
+
+// UnsetBorderLeft removes the border left style rule, if set.
+func (s Style) UnsetBorderLeft() Style {
+	delete(s.rules, borderLeftKey)
+	return s
+}
+
+// UnsetBorderForeground removes all border foreground color styles, if set.
+func (s Style) UnsetBorderForeground() Style {
+	delete(s.rules, borderTopForegroundKey)
+	delete(s.rules, borderRightForegroundKey)
+	delete(s.rules, borderBottomForegroundKey)
+	delete(s.rules, borderLeftForegroundKey)
+	return s
+}
+
+// UnsetBorderTopForeground removes the top border foreground color rule,
+// if set.
+func (s Style) UnsetBorderTopForeground() Style {
+	delete(s.rules, borderTopForegroundKey)
+	return s
+}
+
+// UnsetBorderRightForeground removes the right border foreground color rule,
+// if set.
+func (s Style) UnsetBorderRightForeground() Style {
+	delete(s.rules, borderRightForegroundKey)
+	return s
+}
+
+// UnsetBorderBottomForeground removes the bottom border foreground color
+// rule, if set.
+func (s Style) UnsetBorderBottomForeground() Style {
+	delete(s.rules, borderBottomForegroundKey)
+	return s
+}
+
+// UnsetBorderLeftForeground removes the left border foreground color rule,
+// if set.
+func (s Style) UnsetBorderLeftForeground() Style {
+	delete(s.rules, borderLeftForegroundKey)
+	return s
+}
+
+// UnsetBorderBackground removes all border background color styles, if
+// set.
+func (s Style) UnsetBorderBackground() Style {
+	delete(s.rules, borderTopBackgroundKey)
+	delete(s.rules, borderRightBackgroundKey)
+	delete(s.rules, borderBottomBackgroundKey)
+	delete(s.rules, borderLeftBackgroundKey)
+	return s
+}
+
+// UnsetBorderTopBackgroundColor removes the top border background color rule,
+// if set.
+func (s Style) UnsetBorderTopBackgroundColor() Style {
+	delete(s.rules, borderTopBackgroundKey)
+	return s
+}
+
+// UnsetBorderRightBackground removes the right border background color
+// rule, if set.
+func (s Style) UnsetBorderRightBackground() Style {
+	delete(s.rules, borderRightBackgroundKey)
+	return s
+}
+
+// UnsetBorderBottomBackground removes the bottom border background color
+// rule, if set.
+func (s Style) UnsetBorderBottomBackground() Style {
+	delete(s.rules, borderBottomBackgroundKey)
+	return s
+}
+
+// UnsetBorderLeftBackground removes the left border color rule, if set.
+func (s Style) UnsetBorderLeftBackground() Style {
+	delete(s.rules, borderLeftBackgroundKey)
+	return s
+}
+
+// UnsetInline removes the inline style rule, if set.
+func (s Style) UnsetInline() Style {
+	delete(s.rules, inlineKey)
+	return s
+}
+
+// UnsetMaxWidth removes the max width style rule, if set.
+func (s Style) UnsetMaxWidth() Style {
+	delete(s.rules, maxWidthKey)
+	return s
+}
+
+// UnsetMaxHeight removes the max height style rule, if set.
+func (s Style) UnsetMaxHeight() Style {
+	delete(s.rules, maxHeightKey)
+	return s
+}
+
+// UnsetUnderlineSpaces removes the value set by UnderlineSpaces.
+func (s Style) UnsetUnderlineSpaces() Style {
+	delete(s.rules, underlineSpacesKey)
+	return s
+}
+
+// UnsetStrikethroughSpaces removes the value set by StrikethroughSpaces.
+func (s Style) UnsetStrikethroughSpaces() Style {
+	delete(s.rules, strikethroughSpacesKey)
+	return s
+}
+
+// UnsetString sets the underlying string value to the empty string.
+func (s Style) UnsetString() Style {
+	s.value = ""
+	return s
+}

+ 83 - 0
vendor/github.com/charmbracelet/lipgloss/whitespace.go

@@ -0,0 +1,83 @@
+package lipgloss
+
+import (
+	"strings"
+
+	"github.com/muesli/reflow/ansi"
+	"github.com/muesli/termenv"
+)
+
+// whitespace is a whitespace renderer.
+type whitespace struct {
+	re    *Renderer
+	style termenv.Style
+	chars string
+}
+
+// newWhitespace creates a new whitespace renderer. The order of the options
+// matters, it you'r using WithWhitespaceRenderer, make sure it comes first as
+// other options might depend on it.
+func newWhitespace(r *Renderer, opts ...WhitespaceOption) *whitespace {
+	w := &whitespace{
+		re:    r,
+		style: r.ColorProfile().String(),
+	}
+	for _, opt := range opts {
+		opt(w)
+	}
+	return w
+}
+
+// Render whitespaces.
+func (w whitespace) render(width int) string {
+	if w.chars == "" {
+		w.chars = " "
+	}
+
+	r := []rune(w.chars)
+	j := 0
+	b := strings.Builder{}
+
+	// Cycle through runes and print them into the whitespace.
+	for i := 0; i < width; {
+		b.WriteRune(r[j])
+		j++
+		if j >= len(r) {
+			j = 0
+		}
+		i += ansi.PrintableRuneWidth(string(r[j]))
+	}
+
+	// Fill any extra gaps white spaces. This might be necessary if any runes
+	// are more than one cell wide, which could leave a one-rune gap.
+	short := width - ansi.PrintableRuneWidth(b.String())
+	if short > 0 {
+		b.WriteString(strings.Repeat(" ", short))
+	}
+
+	return w.style.Styled(b.String())
+}
+
+// WhitespaceOption sets a styling rule for rendering whitespace.
+type WhitespaceOption func(*whitespace)
+
+// WithWhitespaceForeground sets the color of the characters in the whitespace.
+func WithWhitespaceForeground(c TerminalColor) WhitespaceOption {
+	return func(w *whitespace) {
+		w.style = w.style.Foreground(c.color(w.re))
+	}
+}
+
+// WithWhitespaceBackground sets the background color of the whitespace.
+func WithWhitespaceBackground(c TerminalColor) WhitespaceOption {
+	return func(w *whitespace) {
+		w.style = w.style.Background(c.color(w.re))
+	}
+}
+
+// WithWhitespaceChars sets the characters to be rendered in the whitespace.
+func WithWhitespaceChars(s string) WhitespaceOption {
+	return func(w *whitespace) {
+		w.chars = s
+	}
+}

+ 20 - 0
vendor/github.com/containerd/console/.golangci.yml

@@ -0,0 +1,20 @@
+linters:
+  enable:
+    - gofmt
+    - goimports
+    - ineffassign
+    - misspell
+    - revive
+    - staticcheck
+    - structcheck
+    - unconvert
+    - unused
+    - varcheck
+    - vet
+  disable:
+    - errcheck
+
+run:
+  timeout: 3m
+  skip-dirs:
+    - vendor

+ 3 - 14
vendor/github.com/gdamore/tcell/v2/LICENSE → vendor/github.com/containerd/console/LICENSE

@@ -1,7 +1,7 @@
 
                                  Apache License
                            Version 2.0, January 2004
-                        http://www.apache.org/licenses/
+                        https://www.apache.org/licenses/
 
    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 
@@ -176,24 +176,13 @@
 
    END OF TERMS AND CONDITIONS
 
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
+   Copyright The containerd Authors
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
 
-       http://www.apache.org/licenses/LICENSE-2.0
+       https://www.apache.org/licenses/LICENSE-2.0
 
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,

+ 29 - 0
vendor/github.com/containerd/console/README.md

@@ -0,0 +1,29 @@
+# console
+
+[![PkgGoDev](https://pkg.go.dev/badge/github.com/containerd/console)](https://pkg.go.dev/github.com/containerd/console)
+[![Build Status](https://github.com/containerd/console/workflows/CI/badge.svg)](https://github.com/containerd/console/actions?query=workflow%3ACI)
+[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/console)](https://goreportcard.com/report/github.com/containerd/console)
+
+Golang package for dealing with consoles.  Light on deps and a simple API.
+
+## Modifying the current process
+
+```go
+current := console.Current()
+defer current.Reset()
+
+if err := current.SetRaw(); err != nil {
+}
+ws, err := current.Size()
+current.Resize(ws)
+```
+
+## Project details
+
+console is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
+As a containerd sub-project, you will find the:
+ * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
+ * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
+ * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
+
+information in our [`containerd/project`](https://github.com/containerd/project) repository.

+ 87 - 0
vendor/github.com/containerd/console/console.go

@@ -0,0 +1,87 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"errors"
+	"io"
+	"os"
+)
+
+var ErrNotAConsole = errors.New("provided file is not a console")
+
+type File interface {
+	io.ReadWriteCloser
+
+	// Fd returns its file descriptor
+	Fd() uintptr
+	// Name returns its file name
+	Name() string
+}
+
+type Console interface {
+	File
+
+	// Resize resizes the console to the provided window size
+	Resize(WinSize) error
+	// ResizeFrom resizes the calling console to the size of the
+	// provided console
+	ResizeFrom(Console) error
+	// SetRaw sets the console in raw mode
+	SetRaw() error
+	// DisableEcho disables echo on the console
+	DisableEcho() error
+	// Reset restores the console to its orignal state
+	Reset() error
+	// Size returns the window size of the console
+	Size() (WinSize, error)
+}
+
+// WinSize specifies the window size of the console
+type WinSize struct {
+	// Height of the console
+	Height uint16
+	// Width of the console
+	Width uint16
+	x     uint16
+	y     uint16
+}
+
+// Current returns the current process' console
+func Current() (c Console) {
+	var err error
+	// Usually all three streams (stdin, stdout, and stderr)
+	// are open to the same console, but some might be redirected,
+	// so try all three.
+	for _, s := range []*os.File{os.Stderr, os.Stdout, os.Stdin} {
+		if c, err = ConsoleFromFile(s); err == nil {
+			return c
+		}
+	}
+	// One of the std streams should always be a console
+	// for the design of this function.
+	panic(err)
+}
+
+// ConsoleFromFile returns a console using the provided file
+// nolint:revive
+func ConsoleFromFile(f File) (Console, error) {
+	if err := checkConsole(f); err != nil {
+		return nil, err
+	}
+	return newMaster(f)
+}

+ 281 - 0
vendor/github.com/containerd/console/console_linux.go

@@ -0,0 +1,281 @@
+//go:build linux
+// +build linux
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"io"
+	"os"
+	"sync"
+
+	"golang.org/x/sys/unix"
+)
+
+const (
+	maxEvents = 128
+)
+
+// Epoller manages multiple epoll consoles using edge-triggered epoll api so we
+// dont have to deal with repeated wake-up of EPOLLER or EPOLLHUP.
+// For more details, see:
+// - https://github.com/systemd/systemd/pull/4262
+// - https://github.com/moby/moby/issues/27202
+//
+// Example usage of Epoller and EpollConsole can be as follow:
+//
+//	epoller, _ := NewEpoller()
+//	epollConsole, _ := epoller.Add(console)
+//	go epoller.Wait()
+//	var (
+//		b  bytes.Buffer
+//		wg sync.WaitGroup
+//	)
+//	wg.Add(1)
+//	go func() {
+//		io.Copy(&b, epollConsole)
+//		wg.Done()
+//	}()
+//	// perform I/O on the console
+//	epollConsole.Shutdown(epoller.CloseConsole)
+//	wg.Wait()
+//	epollConsole.Close()
+type Epoller struct {
+	efd       int
+	mu        sync.Mutex
+	fdMapping map[int]*EpollConsole
+	closeOnce sync.Once
+}
+
+// NewEpoller returns an instance of epoller with a valid epoll fd.
+func NewEpoller() (*Epoller, error) {
+	efd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC)
+	if err != nil {
+		return nil, err
+	}
+	return &Epoller{
+		efd:       efd,
+		fdMapping: make(map[int]*EpollConsole),
+	}, nil
+}
+
+// Add creates an epoll console based on the provided console. The console will
+// be registered with EPOLLET (i.e. using edge-triggered notification) and its
+// file descriptor will be set to non-blocking mode. After this, user should use
+// the return console to perform I/O.
+func (e *Epoller) Add(console Console) (*EpollConsole, error) {
+	sysfd := int(console.Fd())
+	// Set sysfd to non-blocking mode
+	if err := unix.SetNonblock(sysfd, true); err != nil {
+		return nil, err
+	}
+
+	ev := unix.EpollEvent{
+		Events: unix.EPOLLIN | unix.EPOLLOUT | unix.EPOLLRDHUP | unix.EPOLLET,
+		Fd:     int32(sysfd),
+	}
+	if err := unix.EpollCtl(e.efd, unix.EPOLL_CTL_ADD, sysfd, &ev); err != nil {
+		return nil, err
+	}
+	ef := &EpollConsole{
+		Console: console,
+		sysfd:   sysfd,
+		readc:   sync.NewCond(&sync.Mutex{}),
+		writec:  sync.NewCond(&sync.Mutex{}),
+	}
+	e.mu.Lock()
+	e.fdMapping[sysfd] = ef
+	e.mu.Unlock()
+	return ef, nil
+}
+
+// Wait starts the loop to wait for its consoles' notifications and signal
+// appropriate console that it can perform I/O.
+func (e *Epoller) Wait() error {
+	events := make([]unix.EpollEvent, maxEvents)
+	for {
+		n, err := unix.EpollWait(e.efd, events, -1)
+		if err != nil {
+			// EINTR: The call was interrupted by a signal handler before either
+			// any of the requested events occurred or the timeout expired
+			if err == unix.EINTR {
+				continue
+			}
+			return err
+		}
+		for i := 0; i < n; i++ {
+			ev := &events[i]
+			// the console is ready to be read from
+			if ev.Events&(unix.EPOLLIN|unix.EPOLLHUP|unix.EPOLLERR) != 0 {
+				if epfile := e.getConsole(int(ev.Fd)); epfile != nil {
+					epfile.signalRead()
+				}
+			}
+			// the console is ready to be written to
+			if ev.Events&(unix.EPOLLOUT|unix.EPOLLHUP|unix.EPOLLERR) != 0 {
+				if epfile := e.getConsole(int(ev.Fd)); epfile != nil {
+					epfile.signalWrite()
+				}
+			}
+		}
+	}
+}
+
+// CloseConsole unregisters the console's file descriptor from epoll interface
+func (e *Epoller) CloseConsole(fd int) error {
+	e.mu.Lock()
+	defer e.mu.Unlock()
+	delete(e.fdMapping, fd)
+	return unix.EpollCtl(e.efd, unix.EPOLL_CTL_DEL, fd, &unix.EpollEvent{})
+}
+
+func (e *Epoller) getConsole(sysfd int) *EpollConsole {
+	e.mu.Lock()
+	f := e.fdMapping[sysfd]
+	e.mu.Unlock()
+	return f
+}
+
+// Close closes the epoll fd
+func (e *Epoller) Close() error {
+	closeErr := os.ErrClosed // default to "file already closed"
+	e.closeOnce.Do(func() {
+		closeErr = unix.Close(e.efd)
+	})
+	return closeErr
+}
+
+// EpollConsole acts like a console but registers its file descriptor with an
+// epoll fd and uses epoll API to perform I/O.
+type EpollConsole struct {
+	Console
+	readc  *sync.Cond
+	writec *sync.Cond
+	sysfd  int
+	closed bool
+}
+
+// Read reads up to len(p) bytes into p. It returns the number of bytes read
+// (0 <= n <= len(p)) and any error encountered.
+//
+// If the console's read returns EAGAIN or EIO, we assume that it's a
+// temporary error because the other side went away and wait for the signal
+// generated by epoll event to continue.
+func (ec *EpollConsole) Read(p []byte) (n int, err error) {
+	var read int
+	ec.readc.L.Lock()
+	defer ec.readc.L.Unlock()
+	for {
+		read, err = ec.Console.Read(p[n:])
+		n += read
+		if err != nil {
+			var hangup bool
+			if perr, ok := err.(*os.PathError); ok {
+				hangup = (perr.Err == unix.EAGAIN || perr.Err == unix.EIO)
+			} else {
+				hangup = (err == unix.EAGAIN || err == unix.EIO)
+			}
+			// if the other end disappear, assume this is temporary and wait for the
+			// signal to continue again. Unless we didnt read anything and the
+			// console is already marked as closed then we should exit
+			if hangup && !(n == 0 && len(p) > 0 && ec.closed) {
+				ec.readc.Wait()
+				continue
+			}
+		}
+		break
+	}
+	// if we didnt read anything then return io.EOF to end gracefully
+	if n == 0 && len(p) > 0 && err == nil {
+		err = io.EOF
+	}
+	// signal for others that we finished the read
+	ec.readc.Signal()
+	return n, err
+}
+
+// Writes len(p) bytes from p to the console. It returns the number of bytes
+// written from p (0 <= n <= len(p)) and any error encountered that caused
+// the write to stop early.
+//
+// If writes to the console returns EAGAIN or EIO, we assume that it's a
+// temporary error because the other side went away and wait for the signal
+// generated by epoll event to continue.
+func (ec *EpollConsole) Write(p []byte) (n int, err error) {
+	var written int
+	ec.writec.L.Lock()
+	defer ec.writec.L.Unlock()
+	for {
+		written, err = ec.Console.Write(p[n:])
+		n += written
+		if err != nil {
+			var hangup bool
+			if perr, ok := err.(*os.PathError); ok {
+				hangup = (perr.Err == unix.EAGAIN || perr.Err == unix.EIO)
+			} else {
+				hangup = (err == unix.EAGAIN || err == unix.EIO)
+			}
+			// if the other end disappears, assume this is temporary and wait for the
+			// signal to continue again.
+			if hangup {
+				ec.writec.Wait()
+				continue
+			}
+		}
+		// unrecoverable error, break the loop and return the error
+		break
+	}
+	if n < len(p) && err == nil {
+		err = io.ErrShortWrite
+	}
+	// signal for others that we finished the write
+	ec.writec.Signal()
+	return n, err
+}
+
+// Shutdown closes the file descriptor and signals call waiters for this fd.
+// It accepts a callback which will be called with the console's fd. The
+// callback typically will be used to do further cleanup such as unregister the
+// console's fd from the epoll interface.
+// User should call Shutdown and wait for all I/O operation to be finished
+// before closing the console.
+func (ec *EpollConsole) Shutdown(close func(int) error) error {
+	ec.readc.L.Lock()
+	defer ec.readc.L.Unlock()
+	ec.writec.L.Lock()
+	defer ec.writec.L.Unlock()
+
+	ec.readc.Broadcast()
+	ec.writec.Broadcast()
+	ec.closed = true
+	return close(ec.sysfd)
+}
+
+// signalRead signals that the console is readable.
+func (ec *EpollConsole) signalRead() {
+	ec.readc.L.Lock()
+	ec.readc.Signal()
+	ec.readc.L.Unlock()
+}
+
+// signalWrite signals that the console is writable.
+func (ec *EpollConsole) signalWrite() {
+	ec.writec.L.Lock()
+	ec.writec.Signal()
+	ec.writec.L.Unlock()
+}

+ 157 - 0
vendor/github.com/containerd/console/console_unix.go

@@ -0,0 +1,157 @@
+//go:build darwin || freebsd || linux || netbsd || openbsd || zos
+// +build darwin freebsd linux netbsd openbsd zos
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"golang.org/x/sys/unix"
+)
+
+// NewPty creates a new pty pair
+// The master is returned as the first console and a string
+// with the path to the pty slave is returned as the second
+func NewPty() (Console, string, error) {
+	f, err := openpt()
+	if err != nil {
+		return nil, "", err
+	}
+	slave, err := ptsname(f)
+	if err != nil {
+		return nil, "", err
+	}
+	if err := unlockpt(f); err != nil {
+		return nil, "", err
+	}
+	m, err := newMaster(f)
+	if err != nil {
+		return nil, "", err
+	}
+	return m, slave, nil
+}
+
+type master struct {
+	f        File
+	original *unix.Termios
+}
+
+func (m *master) Read(b []byte) (int, error) {
+	return m.f.Read(b)
+}
+
+func (m *master) Write(b []byte) (int, error) {
+	return m.f.Write(b)
+}
+
+func (m *master) Close() error {
+	return m.f.Close()
+}
+
+func (m *master) Resize(ws WinSize) error {
+	return tcswinsz(m.f.Fd(), ws)
+}
+
+func (m *master) ResizeFrom(c Console) error {
+	ws, err := c.Size()
+	if err != nil {
+		return err
+	}
+	return m.Resize(ws)
+}
+
+func (m *master) Reset() error {
+	if m.original == nil {
+		return nil
+	}
+	return tcset(m.f.Fd(), m.original)
+}
+
+func (m *master) getCurrent() (unix.Termios, error) {
+	var termios unix.Termios
+	if err := tcget(m.f.Fd(), &termios); err != nil {
+		return unix.Termios{}, err
+	}
+	return termios, nil
+}
+
+func (m *master) SetRaw() error {
+	rawState, err := m.getCurrent()
+	if err != nil {
+		return err
+	}
+	rawState = cfmakeraw(rawState)
+	rawState.Oflag = rawState.Oflag | unix.OPOST
+	return tcset(m.f.Fd(), &rawState)
+}
+
+func (m *master) DisableEcho() error {
+	rawState, err := m.getCurrent()
+	if err != nil {
+		return err
+	}
+	rawState.Lflag = rawState.Lflag &^ unix.ECHO
+	return tcset(m.f.Fd(), &rawState)
+}
+
+func (m *master) Size() (WinSize, error) {
+	return tcgwinsz(m.f.Fd())
+}
+
+func (m *master) Fd() uintptr {
+	return m.f.Fd()
+}
+
+func (m *master) Name() string {
+	return m.f.Name()
+}
+
+// checkConsole checks if the provided file is a console
+func checkConsole(f File) error {
+	var termios unix.Termios
+	if tcget(f.Fd(), &termios) != nil {
+		return ErrNotAConsole
+	}
+	return nil
+}
+
+func newMaster(f File) (Console, error) {
+	m := &master{
+		f: f,
+	}
+	t, err := m.getCurrent()
+	if err != nil {
+		return nil, err
+	}
+	m.original = &t
+	return m, nil
+}
+
+// ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair
+// created by us acts normally. In particular, a not-very-well-known default of
+// Linux unix98 ptys is that they have +onlcr by default. While this isn't a
+// problem for terminal emulators, because we relay data from the terminal we
+// also relay that funky line discipline.
+func ClearONLCR(fd uintptr) error {
+	return setONLCR(fd, false)
+}
+
+// SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair
+// created by us acts as intended for a terminal emulator.
+func SetONLCR(fd uintptr) error {
+	return setONLCR(fd, true)
+}

+ 222 - 0
vendor/github.com/containerd/console/console_windows.go

@@ -0,0 +1,222 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"errors"
+	"fmt"
+	"os"
+
+	"golang.org/x/sys/windows"
+)
+
+var (
+	vtInputSupported  bool
+	ErrNotImplemented = errors.New("not implemented")
+)
+
+func (m *master) initStdios() {
+	// Note: We discard console mode warnings, because in/out can be redirected.
+	//
+	// TODO: Investigate opening CONOUT$/CONIN$ to handle this correctly
+
+	m.in = windows.Handle(os.Stdin.Fd())
+	if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil {
+		// Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
+		if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
+			vtInputSupported = true
+		}
+		// Unconditionally set the console mode back even on failure because SetConsoleMode
+		// remembers invalid bits on input handles.
+		windows.SetConsoleMode(m.in, m.inMode)
+	}
+
+	m.out = windows.Handle(os.Stdout.Fd())
+	if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil {
+		if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
+			m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
+		} else {
+			windows.SetConsoleMode(m.out, m.outMode)
+		}
+	}
+
+	m.err = windows.Handle(os.Stderr.Fd())
+	if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil {
+		if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
+			m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
+		} else {
+			windows.SetConsoleMode(m.err, m.errMode)
+		}
+	}
+}
+
+type master struct {
+	in     windows.Handle
+	inMode uint32
+
+	out     windows.Handle
+	outMode uint32
+
+	err     windows.Handle
+	errMode uint32
+}
+
+func (m *master) SetRaw() error {
+	if err := makeInputRaw(m.in, m.inMode); err != nil {
+		return err
+	}
+
+	// Set StdOut and StdErr to raw mode, we ignore failures since
+	// windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of
+	// Windows.
+
+	windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
+
+	windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
+
+	return nil
+}
+
+func (m *master) Reset() error {
+	var errs []error
+
+	for _, s := range []struct {
+		fd   windows.Handle
+		mode uint32
+	}{
+		{m.in, m.inMode},
+		{m.out, m.outMode},
+		{m.err, m.errMode},
+	} {
+		if err := windows.SetConsoleMode(s.fd, s.mode); err != nil {
+			// we can't just abort on the first error, otherwise we might leave
+			// the console in an unexpected state.
+			errs = append(errs, fmt.Errorf("unable to restore console mode: %w", err))
+		}
+	}
+
+	if len(errs) > 0 {
+		return errs[0]
+	}
+
+	return nil
+}
+
+func (m *master) Size() (WinSize, error) {
+	var info windows.ConsoleScreenBufferInfo
+	err := windows.GetConsoleScreenBufferInfo(m.out, &info)
+	if err != nil {
+		return WinSize{}, fmt.Errorf("unable to get console info: %w", err)
+	}
+
+	winsize := WinSize{
+		Width:  uint16(info.Window.Right - info.Window.Left + 1),
+		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
+	}
+
+	return winsize, nil
+}
+
+func (m *master) Resize(ws WinSize) error {
+	return ErrNotImplemented
+}
+
+func (m *master) ResizeFrom(c Console) error {
+	return ErrNotImplemented
+}
+
+func (m *master) DisableEcho() error {
+	mode := m.inMode &^ windows.ENABLE_ECHO_INPUT
+	mode |= windows.ENABLE_PROCESSED_INPUT
+	mode |= windows.ENABLE_LINE_INPUT
+
+	if err := windows.SetConsoleMode(m.in, mode); err != nil {
+		return fmt.Errorf("unable to set console to disable echo: %w", err)
+	}
+
+	return nil
+}
+
+func (m *master) Close() error {
+	return nil
+}
+
+func (m *master) Read(b []byte) (int, error) {
+	return os.Stdin.Read(b)
+}
+
+func (m *master) Write(b []byte) (int, error) {
+	return os.Stdout.Write(b)
+}
+
+func (m *master) Fd() uintptr {
+	return uintptr(m.in)
+}
+
+// on windows, console can only be made from os.Std{in,out,err}, hence there
+// isnt a single name here we can use. Return a dummy "console" value in this
+// case should be sufficient.
+func (m *master) Name() string {
+	return "console"
+}
+
+// makeInputRaw puts the terminal (Windows Console) connected to the given
+// file descriptor into raw mode
+func makeInputRaw(fd windows.Handle, mode uint32) error {
+	// See
+	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
+	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
+
+	// Disable these modes
+	mode &^= windows.ENABLE_ECHO_INPUT
+	mode &^= windows.ENABLE_LINE_INPUT
+	mode &^= windows.ENABLE_MOUSE_INPUT
+	mode &^= windows.ENABLE_WINDOW_INPUT
+	mode &^= windows.ENABLE_PROCESSED_INPUT
+
+	// Enable these modes
+	mode |= windows.ENABLE_EXTENDED_FLAGS
+	mode |= windows.ENABLE_INSERT_MODE
+	mode |= windows.ENABLE_QUICK_EDIT_MODE
+
+	if vtInputSupported {
+		mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
+	}
+
+	if err := windows.SetConsoleMode(fd, mode); err != nil {
+		return fmt.Errorf("unable to set console to raw mode: %w", err)
+	}
+
+	return nil
+}
+
+func checkConsole(f File) error {
+	var mode uint32
+	if err := windows.GetConsoleMode(windows.Handle(f.Fd()), &mode); err != nil {
+		return err
+	}
+	return nil
+}
+
+func newMaster(f File) (Console, error) {
+	if f != os.Stdin && f != os.Stdout && f != os.Stderr {
+		return nil, errors.New("creating a console from a file is not supported on windows")
+	}
+	m := &master{}
+	m.initStdios()
+	return m, nil
+}

+ 46 - 0
vendor/github.com/containerd/console/pty_freebsd_cgo.go

@@ -0,0 +1,46 @@
+//go:build freebsd && cgo
+// +build freebsd,cgo
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"fmt"
+	"os"
+)
+
+/*
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+*/
+import "C"
+
+// openpt allocates a new pseudo-terminal and establishes a connection with its
+// control device.
+func openpt() (*os.File, error) {
+	fd, err := C.posix_openpt(C.O_RDWR)
+	if err != nil {
+		return nil, fmt.Errorf("posix_openpt: %w", err)
+	}
+	if _, err := C.grantpt(fd); err != nil {
+		C.close(fd)
+		return nil, fmt.Errorf("grantpt: %w", err)
+	}
+	return os.NewFile(uintptr(fd), ""), nil
+}

+ 37 - 0
vendor/github.com/containerd/console/pty_freebsd_nocgo.go

@@ -0,0 +1,37 @@
+//go:build freebsd && !cgo
+// +build freebsd,!cgo
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"os"
+)
+
+//
+// Implementing the functions below requires cgo support.  Non-cgo stubs
+// versions are defined below to enable cross-compilation of source code
+// that depends on these functions, but the resultant cross-compiled
+// binaries cannot actually be used.  If the stub function(s) below are
+// actually invoked they will display an error message and cause the
+// calling process to exit.
+//
+
+func openpt() (*os.File, error) {
+	panic("openpt() support requires cgo.")
+}

+ 31 - 0
vendor/github.com/containerd/console/pty_unix.go

@@ -0,0 +1,31 @@
+//go:build darwin || linux || netbsd || openbsd
+// +build darwin linux netbsd openbsd
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"os"
+
+	"golang.org/x/sys/unix"
+)
+
+// openpt allocates a new pseudo-terminal by opening the /dev/ptmx device
+func openpt() (*os.File, error) {
+	return os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0)
+}

+ 43 - 0
vendor/github.com/containerd/console/pty_zos.go

@@ -0,0 +1,43 @@
+//go:build zos
+// +build zos
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"fmt"
+	"os"
+)
+
+// openpt allocates a new pseudo-terminal by opening the first available /dev/ptypXX device
+func openpt() (*os.File, error) {
+	var f *os.File
+	var err error
+	for i := 0; ; i++ {
+		ptyp := fmt.Sprintf("/dev/ptyp%04d", i)
+		f, err = os.OpenFile(ptyp, os.O_RDWR, 0600)
+		if err == nil {
+			break
+		}
+		if os.IsNotExist(err) {
+			return nil, err
+		}
+		// else probably Resource Busy
+	}
+	return f, nil
+}

+ 44 - 0
vendor/github.com/containerd/console/tc_darwin.go

@@ -0,0 +1,44 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"fmt"
+	"os"
+
+	"golang.org/x/sys/unix"
+)
+
+const (
+	cmdTcGet = unix.TIOCGETA
+	cmdTcSet = unix.TIOCSETA
+)
+
+// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
+// unlockpt should be called before opening the slave side of a pty.
+func unlockpt(f *os.File) error {
+	return unix.IoctlSetPointerInt(int(f.Fd()), unix.TIOCPTYUNLK, 0)
+}
+
+// ptsname retrieves the name of the first available pts for the given master.
+func ptsname(f *os.File) (string, error) {
+	n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCPTYGNAME)
+	if err != nil {
+		return "", err
+	}
+	return fmt.Sprintf("/dev/pts/%d", n), nil
+}

+ 58 - 0
vendor/github.com/containerd/console/tc_freebsd_cgo.go

@@ -0,0 +1,58 @@
+//go:build freebsd && cgo
+// +build freebsd,cgo
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"fmt"
+	"os"
+
+	"golang.org/x/sys/unix"
+)
+
+/*
+#include <stdlib.h>
+#include <unistd.h>
+*/
+import "C"
+
+const (
+	cmdTcGet = unix.TIOCGETA
+	cmdTcSet = unix.TIOCSETA
+)
+
+// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
+// unlockpt should be called before opening the slave side of a pty.
+func unlockpt(f *os.File) error {
+	fd := C.int(f.Fd())
+	if _, err := C.unlockpt(fd); err != nil {
+		C.close(fd)
+		return fmt.Errorf("unlockpt: %w", err)
+	}
+	return nil
+}
+
+// ptsname retrieves the name of the first available pts for the given master.
+func ptsname(f *os.File) (string, error) {
+	n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN)
+	if err != nil {
+		return "", err
+	}
+	return fmt.Sprintf("/dev/pts/%d", n), nil
+}

+ 56 - 0
vendor/github.com/containerd/console/tc_freebsd_nocgo.go

@@ -0,0 +1,56 @@
+//go:build freebsd && !cgo
+// +build freebsd,!cgo
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"fmt"
+	"os"
+
+	"golang.org/x/sys/unix"
+)
+
+const (
+	cmdTcGet = unix.TIOCGETA
+	cmdTcSet = unix.TIOCSETA
+)
+
+//
+// Implementing the functions below requires cgo support.  Non-cgo stubs
+// versions are defined below to enable cross-compilation of source code
+// that depends on these functions, but the resultant cross-compiled
+// binaries cannot actually be used.  If the stub function(s) below are
+// actually invoked they will display an error message and cause the
+// calling process to exit.
+//
+
+// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
+// unlockpt should be called before opening the slave side of a pty.
+func unlockpt(f *os.File) error {
+	panic("unlockpt() support requires cgo.")
+}
+
+// ptsname retrieves the name of the first available pts for the given master.
+func ptsname(f *os.File) (string, error) {
+	n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN)
+	if err != nil {
+		return "", err
+	}
+	return fmt.Sprintf("/dev/pts/%d", n), nil
+}

+ 51 - 0
vendor/github.com/containerd/console/tc_linux.go

@@ -0,0 +1,51 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"fmt"
+	"os"
+	"unsafe"
+
+	"golang.org/x/sys/unix"
+)
+
+const (
+	cmdTcGet = unix.TCGETS
+	cmdTcSet = unix.TCSETS
+)
+
+// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
+// unlockpt should be called before opening the slave side of a pty.
+func unlockpt(f *os.File) error {
+	var u int32
+	// XXX do not use unix.IoctlSetPointerInt here, see commit dbd69c59b81.
+	if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))); err != 0 {
+		return err
+	}
+	return nil
+}
+
+// ptsname retrieves the name of the first available pts for the given master.
+func ptsname(f *os.File) (string, error) {
+	var u uint32
+	// XXX do not use unix.IoctlGetInt here, see commit dbd69c59b81.
+	if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&u))); err != 0 {
+		return "", err
+	}
+	return fmt.Sprintf("/dev/pts/%d", u), nil
+}

+ 45 - 0
vendor/github.com/containerd/console/tc_netbsd.go

@@ -0,0 +1,45 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"bytes"
+	"os"
+
+	"golang.org/x/sys/unix"
+)
+
+const (
+	cmdTcGet = unix.TIOCGETA
+	cmdTcSet = unix.TIOCSETA
+)
+
+// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
+// unlockpt should be called before opening the slave side of a pty.
+// This does not exist on NetBSD, it does not allocate controlling terminals on open
+func unlockpt(f *os.File) error {
+	return nil
+}
+
+// ptsname retrieves the name of the first available pts for the given master.
+func ptsname(f *os.File) (string, error) {
+	ptm, err := unix.IoctlGetPtmget(int(f.Fd()), unix.TIOCPTSNAME)
+	if err != nil {
+		return "", err
+	}
+	return string(ptm.Sn[:bytes.IndexByte(ptm.Sn[:], 0)]), nil
+}

+ 52 - 0
vendor/github.com/containerd/console/tc_openbsd_cgo.go

@@ -0,0 +1,52 @@
+//go:build openbsd && cgo
+// +build openbsd,cgo
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"os"
+
+	"golang.org/x/sys/unix"
+)
+
+//#include <stdlib.h>
+import "C"
+
+const (
+	cmdTcGet = unix.TIOCGETA
+	cmdTcSet = unix.TIOCSETA
+)
+
+// ptsname retrieves the name of the first available pts for the given master.
+func ptsname(f *os.File) (string, error) {
+	ptspath, err := C.ptsname(C.int(f.Fd()))
+	if err != nil {
+		return "", err
+	}
+	return C.GoString(ptspath), nil
+}
+
+// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
+// unlockpt should be called before opening the slave side of a pty.
+func unlockpt(f *os.File) error {
+	if _, err := C.grantpt(C.int(f.Fd())); err != nil {
+		return err
+	}
+	return nil
+}

+ 48 - 0
vendor/github.com/containerd/console/tc_openbsd_nocgo.go

@@ -0,0 +1,48 @@
+//go:build openbsd && !cgo
+// +build openbsd,!cgo
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+//
+// Implementing the functions below requires cgo support.  Non-cgo stubs
+// versions are defined below to enable cross-compilation of source code
+// that depends on these functions, but the resultant cross-compiled
+// binaries cannot actually be used.  If the stub function(s) below are
+// actually invoked they will display an error message and cause the
+// calling process to exit.
+//
+
+package console
+
+import (
+	"os"
+
+	"golang.org/x/sys/unix"
+)
+
+const (
+	cmdTcGet = unix.TIOCGETA
+	cmdTcSet = unix.TIOCSETA
+)
+
+func ptsname(f *os.File) (string, error) {
+	panic("ptsname() support requires cgo.")
+}
+
+func unlockpt(f *os.File) error {
+	panic("unlockpt() support requires cgo.")
+}

+ 92 - 0
vendor/github.com/containerd/console/tc_unix.go

@@ -0,0 +1,92 @@
+//go:build darwin || freebsd || linux || netbsd || openbsd || zos
+// +build darwin freebsd linux netbsd openbsd zos
+
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"golang.org/x/sys/unix"
+)
+
+func tcget(fd uintptr, p *unix.Termios) error {
+	termios, err := unix.IoctlGetTermios(int(fd), cmdTcGet)
+	if err != nil {
+		return err
+	}
+	*p = *termios
+	return nil
+}
+
+func tcset(fd uintptr, p *unix.Termios) error {
+	return unix.IoctlSetTermios(int(fd), cmdTcSet, p)
+}
+
+func tcgwinsz(fd uintptr) (WinSize, error) {
+	var ws WinSize
+
+	uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
+	if err != nil {
+		return ws, err
+	}
+
+	// Translate from unix.Winsize to console.WinSize
+	ws.Height = uws.Row
+	ws.Width = uws.Col
+	ws.x = uws.Xpixel
+	ws.y = uws.Ypixel
+	return ws, nil
+}
+
+func tcswinsz(fd uintptr, ws WinSize) error {
+	// Translate from console.WinSize to unix.Winsize
+
+	var uws unix.Winsize
+	uws.Row = ws.Height
+	uws.Col = ws.Width
+	uws.Xpixel = ws.x
+	uws.Ypixel = ws.y
+
+	return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, &uws)
+}
+
+func setONLCR(fd uintptr, enable bool) error {
+	var termios unix.Termios
+	if err := tcget(fd, &termios); err != nil {
+		return err
+	}
+	if enable {
+		// Set +onlcr so we can act like a real terminal
+		termios.Oflag |= unix.ONLCR
+	} else {
+		// Set -onlcr so we don't have to deal with \r.
+		termios.Oflag &^= unix.ONLCR
+	}
+	return tcset(fd, &termios)
+}
+
+func cfmakeraw(t unix.Termios) unix.Termios {
+	t.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
+	t.Oflag &^= unix.OPOST
+	t.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
+	t.Cflag &^= (unix.CSIZE | unix.PARENB)
+	t.Cflag &^= unix.CS8
+	t.Cc[unix.VMIN] = 1
+	t.Cc[unix.VTIME] = 0
+
+	return t
+}

+ 39 - 0
vendor/github.com/containerd/console/tc_zos.go

@@ -0,0 +1,39 @@
+/*
+   Copyright The containerd Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package console
+
+import (
+	"os"
+	"strings"
+
+	"golang.org/x/sys/unix"
+)
+
+const (
+	cmdTcGet = unix.TCGETS
+	cmdTcSet = unix.TCSETS
+)
+
+// unlockpt is a no-op on zos.
+func unlockpt(_ *os.File) error {
+	return nil
+}
+
+// ptsname retrieves the name of the first available pts for the given master.
+func ptsname(f *os.File) (string, error) {
+	return "/dev/ttyp" + strings.TrimPrefix(f.Name(), "/dev/ptyp"), nil
+}

+ 0 - 16
vendor/github.com/epiclabs-io/winman/.travis.yml

@@ -1,16 +0,0 @@
-language: go
-
-env:
-  - GO111MODULE=on
-
-go:
-  - '1.12.x'
-  - '1.13.x'
-  - tip
-
-matrix:
-  allow_failures:
-    - go: tip
-
-script:
-  - go test ./...

+ 0 - 80
vendor/github.com/epiclabs-io/winman/README.md

@@ -1,80 +0,0 @@
-# Winman - go Window Manager for terminal UIs
-[![Build Status](https://travis-ci.org/epiclabs-io/winman.svg?branch=master)](https://travis-ci.org/epiclabs-io/winman)
-[![Go Report](https://goreportcard.com/badge/github.com/epiclabs-io/winman)](https://goreportcard.com/report/github.com/epiclabs-io/winman)
-[![Godoc Reference](https://godoc.org/github.com/epiclabs-io/winman?status.svg)](https://pkg.go.dev/github.com/epiclabs-io/winman)
-
-Winman is a basic yet powerful window manager in go for terminal-based user interfaces that plugs into [tview](github.com/rivo/tview).
-
-![Screenshot](demos/showcase/showcase.gif)
-
-
-It supports floating windows that can be dragged, resized and maximized. Windows can have buttons on the title bar, for example to close them, help commands or maximize / minimize.
-
-Windows can also be modal, meaning that other windows don't receive input while
-a modal window is on top. You can control whether the user can drag or resize windows around the screen.
-
-Windows can overlap each other by setting their Z-index. Any `tview.Primitive` can be added to a window, thus you can combine with any other existing `tview` widget! Check [tview](github.com/rivo/tview) for a complete list of available widgets you can use.
-
-## Installation
-
-```bash
-go get github.com/epiclabs-io/winman
-```
-
-## Hello world
-
-```go
-package main
-
-import (
-	"github.com/epiclabs-io/winman"
-	"github.com/rivo/tview"
-)
-
-func main() {
-
-	app := tview.NewApplication()
-	wm := winman.NewWindowManager()
-
-	content := tview.NewTextView().
-		SetText("Hello, world!").       // set content of the text view
-		SetTextAlign(tview.AlignCenter) // align text to the center of the text view
-
-	window := wm.NewWindow(). // create new window and add it to the window manager
-					Show().                   // make window visible
-					SetRoot(content).         // have the text view above be the content of the window
-					SetDraggable(true).       // make window draggable around the screen
-					SetResizable(true).       // make the window resizable
-					SetTitle("Hi!").          // set the window title
-					AddButton(&winman.Button{ // create a button with an X to close the application
-			Symbol:  'X',
-			OnClick: func() { app.Stop() }, // close the application
-		})
-
-	window.SetRect(5, 5, 30, 10) // place the window
-
-	// now, execute the application:
-	if err := app.SetRoot(wm, true).EnableMouse(true).Run(); err != nil {
-		panic(err)
-	}
-}
-
-```
-![hello world](demos/helloworld/helloworld.gif)
-
-## Documentation
-
-Refer to https://pkg.go.dev/github.com/epiclabs-io/winman for the package's documentation.
-The `demos` directory contains a showcase demonstrating the different aspects of this library
-
-## Dependencies
-
-This package works with [tview](github.com/rivo/tview) and its dependencies.
-
-## Your Feedback
-
-Add your issue here on GitHub. Feel free to get in touch if you have any questions.
-
-## Author(s)
-
-This package is written and maintained by Javier Peletier ([@jpeletier](https://github.com/jpeletier)) - [Epic Labs](https://www.epiclabs.io)

+ 0 - 20
vendor/github.com/epiclabs-io/winman/button.go

@@ -1,20 +0,0 @@
-package winman
-
-// ButtonSide determines the alignment of a window button
-type ButtonSide int16
-
-const (
-	// ButtonLeft will set the button to be drawn on the left
-	ButtonLeft = iota
-	// ButtonRight will set the button to be drawn on the right
-	ButtonRight
-)
-
-// Button represents a button on the window title bar
-type Button struct {
-	Symbol    rune // icon for the button
-	offsetX   int  // where the button is drawn
-	offsetY   int
-	Alignment ButtonSide // alignment of the button, left or right
-	OnClick   func()     // callback to be invoked when the button is clicked
-}

+ 0 - 83
vendor/github.com/epiclabs-io/winman/clipregion.go

@@ -1,83 +0,0 @@
-package winman
-
-import "github.com/gdamore/tcell/v2"
-
-// ClipRegion implements tcell.Screen and only allows setting content within
-// a defined region
-type ClipRegion struct {
-	tcell.Screen
-	x      int
-	y      int
-	width  int
-	height int
-	style  tcell.Style
-}
-
-// NewClipRegion Creates a new clipped screen with the given rectangular coordinates
-func NewClipRegion(screen tcell.Screen, x, y, width, height int) *ClipRegion {
-	return &ClipRegion{
-		Screen: screen,
-		x:      x,
-		y:      y,
-		width:  width,
-		height: height,
-		style:  tcell.StyleDefault,
-	}
-}
-
-// InRect returns true if the given coordinates are within this clipped region
-func (cr *ClipRegion) InRect(x, y int) bool {
-	return !(x < cr.x || y < cr.y || x >= cr.x+cr.width || y >= cr.y+cr.height)
-}
-
-// Fill implements tcell.Screen.Fill
-func (cr *ClipRegion) Fill(ch rune, style tcell.Style) {
-	for x := cr.x; x < cr.width; x++ {
-		for y := cr.y; y < cr.height; y++ {
-			cr.SetContent(x, y, ch, nil, style)
-		}
-	}
-}
-
-// SetCell is an older API, and will be removed.  Please use
-// SetContent instead; SetCell is implemented in terms of SetContent.
-func (cr *ClipRegion) SetCell(x int, y int, style tcell.Style, ch ...rune) {
-	if len(ch) > 0 {
-		cr.SetContent(x, y, ch[0], ch[1:], style)
-	} else {
-		cr.SetContent(x, y, ' ', nil, style)
-	}
-}
-
-// SetContent sets the contents of the given cell location.  If
-// the coordinates are out of range, then the operation is ignored.
-//
-// The first rune is the primary non-zero width rune.  The array
-// that follows is a possible list of combining characters to append,
-// and will usually be nil (no combining characters.)
-//
-// The results are not displayed until Show() or Sync() is called.
-//
-// Note that wide (East Asian full width) runes occupy two cells,
-// and attempts to place character at next cell to the right will have
-// undefined effects.  Wide runes that are printed in the
-// last column will be replaced with a single width space on output.
-func (cr *ClipRegion) SetContent(x int, y int, mainc rune, combc []rune, style tcell.Style) {
-	if cr.InRect(x, y) {
-		cr.Screen.SetContent(x, y, mainc, combc, style)
-	}
-}
-
-// ShowCursor is used to display the cursor at a given location.
-// If the coordinates -1, -1 are given or are otherwise outside the
-// dimensions of the screen, the cursor will be hidden.
-func (cr *ClipRegion) ShowCursor(x int, y int) {
-	if cr.InRect(x, y) {
-		cr.Screen.ShowCursor(x, y)
-	}
-}
-
-// Clear clears the clipped region
-func (cr *ClipRegion) Clear() {
-	cr.Fill(' ', cr.style)
-}

+ 0 - 21
vendor/github.com/epiclabs-io/winman/doc.go

@@ -1,21 +0,0 @@
-/*
-
-Package winman implements a basic yet powerful window manager that can be used
-with tview (github.com/rivo/tview).
-
-It supports floating windows that can be dragged, resized and maximized.
-Windows can have buttons on the title bar, for example to close them,
-help commands or maximize / minimize.
-
-Windows can also be modal, meaning that other windows don't receive input while
-a modal window is on top.
-
-You can control whether the user can drag or resize windows around the screen.
-
-Windows can overlap each other by setting their Z-index
-
-Any tview.Primitive can be added to a window.
-
-
-*/
-package winman

+ 0 - 374
vendor/github.com/epiclabs-io/winman/manager.go

@@ -1,374 +0,0 @@
-package winman
-
-import (
-	"sync"
-
-	"github.com/gdamore/tcell/v2"
-	"github.com/rivo/tview"
-)
-
-// WindowEdge enumerates the different window edges and corners
-type WindowEdge int16
-
-// Different window edges
-const (
-	EdgeNone WindowEdge = iota
-	EdgeTop
-	EdgeRight
-	EdgeBottom
-	EdgeLeft
-	EdgeBottomRight
-	EdgeBottomLeft
-)
-
-// WindowZTop is used with SetZ to move a window to the top
-const WindowZTop = -1
-
-// WindowZBottom is used with SetZ to move a window to the bottom
-const WindowZBottom = 0
-
-// MinWindowWidth sets the minimum width a window can have as part of a window manager
-var MinWindowWidth = 3
-
-// MinWindowHeight sets the minimum height a window can have as part of a window manager
-var MinWindowHeight = 3
-
-// inRect returns true if the given coordinates are within the window
-func inRect(wnd Window, x, y int) bool {
-	return NewRect(wnd.GetRect()).Contains(x, y)
-}
-
-// Manager represents a Window Manager primitive
-type Manager struct {
-	*tview.Box
-
-	// The windows to be positioned.
-	windows Stack
-
-	dragOffsetX, dragOffsetY int
-	draggedWindow            Window
-	draggedEdge              WindowEdge
-	sync.Mutex
-}
-
-// NewWindowManager returns a ready to use window manager
-func NewWindowManager() *Manager {
-	wm := &Manager{
-		Box: tview.NewBox(),
-	}
-	return wm
-}
-
-// NewWindow creates a new (hidden) window and adds it to this window manager
-func (wm *Manager) NewWindow() *WindowBase {
-	wnd := NewWindow()
-	wm.AddWindow(wnd)
-	return wnd
-}
-
-// AddWindow adds the given window to the window manager
-func (wm *Manager) AddWindow(window Window) *Manager {
-	wm.Lock()
-	defer wm.Unlock()
-	wm.windows.Push(window)
-	return wm
-}
-
-// RemoveWindow removes the given window from this window manager
-func (wm *Manager) RemoveWindow(window Window) *Manager {
-	wm.Lock()
-	defer wm.Unlock()
-	wm.windows.Remove(window)
-	return wm
-}
-
-// Center centers the given window relative to the window manager
-func (wm *Manager) Center(window Window) *Manager {
-	mx, my, mw, mh := wm.GetInnerRect()
-	_, _, width, height := window.GetRect()
-	x := mx + (mw-width)/2
-	y := my + (mh-height)/2
-	window.SetRect(x, y, width, height)
-	return wm
-}
-
-// WindowCount returns the number of windows managed by this window manager
-func (wm *Manager) WindowCount() int {
-	wm.Lock()
-	defer wm.Unlock()
-	return len(wm.windows)
-}
-
-// Window returns the window at the given z index
-func (wm *Manager) Window(z int) Window {
-	wm.Lock()
-	defer wm.Unlock()
-	wnd, _ := wm.windows.Item(z).(Window)
-	return wnd
-}
-
-func (wm *Manager) getZ(window Window) int {
-	return wm.windows.IndexOf(window)
-}
-
-// GetZ returns the z index of the given window
-// returns -1 if the given window is not part of this manager
-func (wm *Manager) GetZ(window Window) int {
-	wm.Lock()
-	defer wm.Unlock()
-	return wm.getZ(window)
-}
-
-func (wm *Manager) setZ(window Window, newZ int) {
-	wm.windows.Move(window, newZ)
-}
-
-// SetZ moves the given window to the given z index
-// The special constants WindowZTop and WindowZBottom can be used
-func (wm *Manager) SetZ(window Window, newZ int) *Manager {
-	wm.Lock()
-	defer wm.Unlock()
-	wm.setZ(window, newZ)
-	return wm
-}
-
-// Focus is called when this primitive receives focus
-// implements tview.Primitive.Focus
-func (wm *Manager) Focus(delegate func(p tview.Primitive)) {
-	wm.Lock()
-
-	window, _ := wm.windows.Find(func(wi interface{}) bool {
-		return wi.(Window).IsVisible()
-	}).(Window)
-
-	if window != nil {
-		wm.Unlock()
-		window.Focus(delegate)
-		return
-	}
-	wm.Unlock()
-}
-
-// HasFocus returns whether or not this primitive has focus.
-// implements tview.Focusable
-func (wm *Manager) HasFocus() bool {
-	wm.Lock()
-	defer wm.Unlock()
-	// iterate over all windows. If any has focus, then the
-	// this window manager has focus.
-	return nil != wm.windows.Find(func(wi interface{}) bool {
-		return wi.(Window).HasFocus()
-	})
-}
-
-// Draw draws this primitive onto the screen.
-// implements tview.Primitive.Draw
-func (wm *Manager) Draw(screen tcell.Screen) {
-	wm.Box.Draw(screen)
-
-	wm.Lock()
-	defer wm.Unlock()
-
-	// Ensure that the window with focus has the highest Z-index:
-	topWindowIndex := len(wm.windows) - 1
-	for i := topWindowIndex; i >= 0; i-- {
-		window := wm.windows[i].(Window)
-		if window.IsVisible() && window.HasFocus() {
-			if i < topWindowIndex {
-				wm.setZ(window, WindowZTop) // move focused window on top
-			}
-			break
-		}
-	}
-
-	// make sure windows are not out of bounds, too small,
-	// or too big to fit within the window manager:
-	for _, wndItem := range wm.windows {
-		window := wndItem.(Window)
-		if !window.IsVisible() {
-			continue
-		}
-		mx, my, mw, mh := wm.GetInnerRect()
-		x, y, w, h := window.GetRect()
-
-		// Avoid window overflowing on the left:
-		if x < mx {
-			x = mx
-		}
-
-		// Avoid window overflowing on the top:
-		if y < my {
-			y = my
-		}
-
-		// Fix window that is too narrow:
-		if w < MinWindowWidth {
-			w = MinWindowWidth
-		}
-
-		// Fix window that is too short:
-		if h < MinWindowHeight {
-			h = MinWindowHeight
-		}
-
-		// reduce windows that are too wide,
-		// or fix size if the window is maximized
-		if w > mw || window.IsMaximized() {
-			w = mw
-			x = mx
-		}
-
-		// reduce windows that are too tall,
-		// or fix size if the window is maximized
-		if h > mh || window.IsMaximized() {
-			h = mh
-			y = my
-		}
-
-		// Avoid window overflowing the right edge
-		if x+w > mx+mw {
-			x = mx + mw - w
-		}
-
-		// Avoid window overflowing the bottom edge
-		if y+h > my+mh {
-			y = my + mh - h
-		}
-
-		// reposition window to the new coordinates:
-		window.SetRect(x, y, w, h)
-
-		// now we can draw it
-		window.Draw(screen)
-	}
-}
-
-// MouseHandler returns the mouse handler for this primitive.
-// implements tview.Primitive.MouseHandler
-func (wm *Manager) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
-	return wm.WrapMouseHandler(func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
-		// ignore mouse events out of the bounds of the window manager
-		if !wm.InRect(event.Position()) {
-			return false, nil
-		}
-		wm.Lock()
-
-		// check if there is an active drag operation:
-		if wm.draggedWindow != nil {
-			switch action {
-			case tview.MouseLeftUp:
-				wm.draggedWindow = nil // if the button is released, stop the drag operation
-			case tview.MouseMove:
-				x, y := event.Position()
-				wx, wy, ww, wh := wm.draggedWindow.GetRect()
-				// depending if the drag operation is on the top or edges, either move the window or resize
-				if wm.draggedEdge == EdgeTop && wm.draggedWindow.IsDraggable() {
-					wm.draggedWindow.SetRect(x-wm.dragOffsetX, y-wm.dragOffsetY, ww, wh) // move window
-				} else {
-					// resize window pulling from the corresponding edge
-					if wm.draggedWindow.IsResizable() {
-						switch wm.draggedEdge {
-						case EdgeRight:
-							wm.draggedWindow.SetRect(wx, wy, x-wx+1, wh)
-						case EdgeBottom:
-							wm.draggedWindow.SetRect(wx, wy, ww, y-wy+1)
-						case EdgeLeft:
-							wm.draggedWindow.SetRect(x, wy, ww+wx-x, wh)
-						case EdgeBottomRight:
-							wm.draggedWindow.SetRect(wx, wy, x-wx+1, y-wy+1)
-						case EdgeBottomLeft:
-							wm.draggedWindow.SetRect(x, wy, ww+wx-x, y-wy+1)
-						}
-					}
-				}
-				wm.Unlock()
-				return true, nil
-			}
-		}
-
-		lastModal := false
-		// Pass mouse events along to the window with highest Z
-		// that is hit by the mouse
-		// Stop if the last window was a modal.
-		for i := len(wm.windows) - 1; i >= 0 && !lastModal; i-- {
-			window := wm.windows[i].(Window)
-			if !window.IsVisible() { // skip hidden windows
-				continue
-			}
-
-			// if this is a modal window, then don't give a chance for
-			// other windows to get mouse events
-			lastModal = window.IsModal() // if true, will exit loop on the next iteration
-
-			x, y := event.Position()
-			if !inRect(window, x, y) {
-				// skip this window since it is not hit
-				continue
-			}
-
-			if action == tview.MouseLeftDown && window.HasBorder() {
-				// initiate a drag operation
-				if !window.HasFocus() {
-					setFocus(window)
-				}
-				wx, wy, ww, wh := window.GetRect()
-				wm.draggedEdge = EdgeNone
-				switch {
-				case y == wy+wh-1:
-					switch {
-					case x == wx:
-						wm.draggedEdge = EdgeBottomLeft
-					case x == wx+ww-1:
-						wm.draggedEdge = EdgeBottomRight
-					default:
-						wm.draggedEdge = EdgeBottom
-					}
-				case x == wx:
-					wm.draggedEdge = EdgeLeft
-				case x == wx+ww-1:
-					wm.draggedEdge = EdgeRight
-				case y == wy:
-					wm.draggedEdge = EdgeTop
-				}
-				if wm.draggedEdge != EdgeNone {
-					// drag detected. Remember where the drag operation started
-					wm.draggedWindow = window
-					wm.dragOffsetX = x - wx
-					wm.dragOffsetY = y - wy
-					wm.Unlock()
-					return true, nil
-				}
-			}
-			wm.Unlock()
-			// no drag operation detected.
-			// pass the mouse events to the window itself.
-			return window.MouseHandler()(action, event, setFocus)
-		}
-		wm.Unlock()
-
-		return
-	})
-}
-
-// InputHandler returns a handler which receives key events when it has focus.
-func (wm *Manager) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
-	return wm.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
-		wm.Lock()
-		// Pass key events along to the window with highest Z that is visible and has focus
-		var window Window
-		for i := len(wm.windows) - 1; i >= 0; i-- {
-			window = wm.windows[i].(Window)
-			if window.HasFocus() {
-				break
-			}
-			window = nil
-		}
-		wm.Unlock()
-		if window != nil {
-			inputHandler := window.InputHandler()
-			if inputHandler != nil {
-				inputHandler(event, setFocus)
-			}
-		}
-	})
-}

+ 0 - 32
vendor/github.com/epiclabs-io/winman/rect.go

@@ -1,32 +0,0 @@
-package winman
-
-import "fmt"
-
-// Rect represents rectangular coordinates
-type Rect struct {
-	X int // x coordinate
-	Y int // y coordinate
-	W int // width
-	H int // height
-}
-
-// NewRect instantiates a new Rect with the given coordinates
-func NewRect(x, y, w, h int) Rect {
-	return Rect{x, y, w, h}
-}
-
-// String implements Stringer
-func (r Rect) String() string {
-	return fmt.Sprintf("{(%d, %d) %dx%d}", r.X, r.Y, r.W, r.H)
-}
-
-// Contains returns true if the given coordinates are within this rectangle
-func (r Rect) Contains(x, y int) bool {
-	return x >= r.X && x < r.X+r.W && y >= r.Y && y < r.Y+r.H
-
-}
-
-// Rect returns the four coordinates of the rectangle: x, y, width and height
-func (r *Rect) Rect() (int, int, int, int) {
-	return r.X, r.Y, r.W, r.H
-}

+ 0 - 99
vendor/github.com/epiclabs-io/winman/stack.go

@@ -1,99 +0,0 @@
-package winman
-
-// Stack represents a stack of unique items
-type Stack []interface{}
-
-// Push puts an element on the stack
-// if the item is already in the stack, this operation does nothing
-func (s *Stack) Push(newItem interface{}) {
-	if newItem == nil {
-		panic("cannot add nil item to stack")
-	}
-	for _, item := range *s {
-		if item == newItem {
-			return
-		}
-	}
-	*s = append(*s, newItem)
-}
-
-// Pop pops the stack returning and removing the topmost element
-func (s *Stack) Pop() interface{} {
-	lenItems := len(*s)
-	if lenItems == 0 {
-		return nil
-	}
-	var item interface{}
-	item, *s = (*s)[lenItems-1], (*s)[:lenItems-1]
-	return item
-}
-
-// Remove removes the given item from the stack
-// if the item does not exist, this function does nothing
-func (s *Stack) Remove(item interface{}) {
-	i := s.IndexOf(item)
-	if i != -1 {
-		*s = append((*s)[:i], (*s)[i+1:]...)
-	}
-}
-
-// Item returns the given item from the stack
-// returns nil if the index is out of bounds
-func (s Stack) Item(i int) interface{} {
-	if i < 0 || i >= len(s) {
-		return nil
-	}
-	return s[i]
-}
-
-// IndexOf searches the stack for the given item and returns its index
-func (s Stack) IndexOf(searchItem interface{}) int {
-	for i, item := range s {
-		if item == searchItem {
-			return i
-		}
-	}
-	return -1
-}
-
-// Move finds an item in the stack and places it at the given index,
-// shifting items up or down to make space
-func (s *Stack) Move(item interface{}, targetIndex int) {
-	oldIndex := s.IndexOf(item)
-	if oldIndex == -1 {
-		return
-	}
-	lenS := len(*s)
-
-	if targetIndex < 0 || targetIndex >= lenS {
-		targetIndex = lenS - 1
-	}
-
-	newStack := make([]interface{}, lenS)
-	for i, j := 0, 0; i < lenS; j++ {
-		if j == oldIndex {
-			j++
-		}
-		if i == targetIndex {
-			j--
-		} else {
-			newStack[i] = (*s)[j]
-		}
-		i++
-	}
-
-	newStack[targetIndex] = item
-	*s = newStack
-}
-
-// Find searches the stack top down for an item that meets the custom
-// criteria specified by the passed function
-func (s Stack) Find(f func(item interface{}) bool) interface{} {
-	for i := len(s) - 1; i >= 0; i-- {
-		item := s[i]
-		if f(item) {
-			return item
-		}
-	}
-	return nil
-}

+ 0 - 280
vendor/github.com/epiclabs-io/winman/window.go

@@ -1,280 +0,0 @@
-package winman
-
-import (
-	"fmt"
-
-	"github.com/gdamore/tcell/v2"
-	"github.com/rivo/tview"
-)
-
-// Window interface defines primitives that can be managed by the Window Manager
-type Window interface {
-	tview.Primitive
-	// IsModal defines this window as modal. When a window is modal, input cannot go to other windows
-	IsModal() bool
-
-	// IsMaximized returns true when the window is maximized and takes all the space available to the
-	// Window Manager
-	IsMaximized() bool
-
-	// IsResizable returns true when the window can be resized by the user
-	IsResizable() bool
-
-	// IsDraggable returns true when the window can be moved by the user by dragging
-	// the title bar
-	IsDraggable() bool
-
-	// IsVisible returns true when the window has to be drawn and can receive focus
-	IsVisible() bool
-
-	// HasBorder returns true if the window must have a border
-	HasBorder() bool
-}
-
-// WindowBase defines a basic window
-type WindowBase struct {
-	*tview.Box
-	root        tview.Primitive // The item contained in the window
-	buttons     []*Button       // window buttons on the title bar
-	border      bool            // whether to render a border
-	restoreRect Rect            // store previous coordinates after restoring from maximize
-	maximized   bool            // whether the window is maximized to the entire window manager area
-	Draggable   bool            //whether this window can be dragged around with the mouse
-	Resizable   bool            // whether this window is user-resizable
-	Modal       bool            // whether this window is modal
-	Visible     bool            // whether this window is rendered
-}
-
-// NewWindow creates a new window
-func NewWindow() *WindowBase {
-	window := &WindowBase{
-		Box: tview.NewBox(), // initialize underlying box
-	}
-	window.restoreRect = NewRect(window.GetRect())
-	window.SetBorder(true)
-	return window
-}
-
-// SetRoot sets the main content of the window
-func (w *WindowBase) SetRoot(root tview.Primitive) *WindowBase {
-	w.root = root
-	return w
-}
-
-// GetRoot returns the primitive that represents the main content of the window
-func (w *WindowBase) GetRoot() tview.Primitive {
-	return w.root
-}
-
-// SetModal makes this window modal. A modal window captures all input
-func (w *WindowBase) SetModal(modal bool) *WindowBase {
-	w.Modal = modal
-	return w
-}
-
-// IsModal returns true if this window is modal
-func (w *WindowBase) IsModal() bool {
-	return w.Modal
-}
-
-// HasBorder returns true if this window has a border
-// windows without border cannot be resized or dragged by the user
-func (w *WindowBase) HasBorder() bool {
-	return w.border
-}
-
-// SetBorder sets the flag indicating whether or not the box should have a
-// border.
-func (w *WindowBase) SetBorder(show bool) *WindowBase {
-	w.border = show
-	w.Box.SetBorder(show)
-	return w
-}
-
-// IsDraggable returns true if this window can be dragged by the user
-func (w *WindowBase) IsDraggable() bool {
-	return w.Draggable
-}
-
-// SetDraggable sets if this window can be dragged by the user
-func (w *WindowBase) SetDraggable(draggable bool) *WindowBase {
-	w.Draggable = draggable
-	return w
-}
-
-// IsResizable returns true if the user may resize this window
-func (w *WindowBase) IsResizable() bool {
-	return w.Resizable
-}
-
-// SetResizable sets if this window can be resized
-func (w *WindowBase) SetResizable(resizable bool) *WindowBase {
-	w.Resizable = resizable
-	return w
-}
-
-// SetTitle sets the window title
-func (w *WindowBase) SetTitle(text string) *WindowBase {
-	w.Box.SetTitle(text)
-	return w
-}
-
-// IsVisible returns true if this window is rendered and may
-// get focus
-func (w *WindowBase) IsVisible() bool {
-	return w.Visible
-}
-
-// Show makes the window visible
-func (w *WindowBase) Show() *WindowBase {
-	w.Visible = true
-	return w
-}
-
-// Hide hides this window
-func (w *WindowBase) Hide() *WindowBase {
-	w.Visible = false
-	return w
-}
-
-// Draw draws this primitive on to the screen
-func (w *WindowBase) Draw(screen tcell.Screen) {
-	if w.HasFocus() { // if the window has focus, make sure the underlying box shows a thicker border
-		w.Box.Focus(nil)
-	} else {
-		w.Box.Blur()
-	}
-	w.Box.Draw(screen) // draw the window frame
-
-	// draw the underlying root primitive within the window bounds
-	if w.root != nil {
-		x, y, width, height := w.GetInnerRect()
-		w.root.SetRect(x, y, width, height)
-		w.root.Draw(NewClipRegion(screen, x, y, width, height))
-	}
-
-	// draw the window border
-	if w.border {
-		x, y, width, height := w.GetRect()
-		screen = NewClipRegion(screen, x, y, width, height)
-		for _, button := range w.buttons {
-			buttonX, buttonY := button.offsetX+x, button.offsetY+y
-			if button.offsetX < 0 {
-				buttonX += width
-			}
-			if button.offsetY < 0 {
-				buttonY += height
-			}
-
-			// render the window title buttons
-			tview.Print(screen, tview.Escape(fmt.Sprintf("[%c]", button.Symbol)), buttonX-1, buttonY, 9, 0, tcell.ColorYellow)
-		}
-	}
-}
-
-// Maximize signals the window manager to resize this window to the maximum size available
-func (w *WindowBase) Maximize() *WindowBase {
-	w.restoreRect = NewRect(w.GetRect())
-	w.maximized = true
-	return w
-}
-
-// IsMaximized returns true if this window is maximized
-func (w *WindowBase) IsMaximized() bool {
-	return w.maximized
-}
-
-// Restore restores the window to the size it had before maximizing
-func (w *WindowBase) Restore() *WindowBase {
-	w.SetRect(w.restoreRect.Rect())
-	w.maximized = false
-	return w
-}
-
-// Focus is called when this primitive receives focus.
-func (w *WindowBase) Focus(delegate func(p tview.Primitive)) {
-	if w.root != nil {
-		delegate(w.root)
-	} else {
-		delegate(w.Box)
-	}
-	w.Visible = true
-}
-
-// HasFocus returns whether or not this primitive has focus.
-func (w *WindowBase) HasFocus() bool {
-	if !w.Visible {
-		return false
-	}
-	if w.root != nil {
-		return w.root.HasFocus()
-	}
-	return w.Box.HasFocus()
-}
-
-// MouseHandler returns a mouse handler for this primitive
-func (w *WindowBase) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
-	return w.WrapMouseHandler(func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) {
-		if action == tview.MouseLeftClick {
-			x, y := event.Position()
-			wx, wy, width, _ := w.GetRect()
-
-			// check if any window button was pressed
-			// if the window does not have border, it cannot receive button events
-			if y == wy && w.border {
-				for _, button := range w.buttons {
-					if button.offsetX >= 0 && x == wx+button.offsetX || button.offsetX < 0 && x == wx+width+button.offsetX {
-						if button.OnClick != nil {
-							button.OnClick()
-						}
-						return true, nil
-					}
-				}
-			}
-		}
-		// pass on clicks to the root primitive, if any
-		if w.root != nil {
-			return w.root.MouseHandler()(action, event, setFocus)
-		}
-		return w.Box.MouseHandler()(action, event, setFocus)
-	})
-}
-
-// InputHandler returns a handler which receives key events when it has focus.
-func (w *WindowBase) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
-	if w.root != nil {
-		return w.root.InputHandler()
-	}
-	return nil
-}
-
-// AddButton adds a new window button to the title bar
-func (w *WindowBase) AddButton(button *Button) *WindowBase {
-	w.buttons = append(w.buttons, button)
-
-	offsetLeft, offsetRight := 2, -3
-	for _, button := range w.buttons {
-		if button.Alignment == ButtonRight {
-			button.offsetX = offsetRight
-			offsetRight -= 3
-		} else {
-			button.offsetX = offsetLeft
-			offsetLeft += 3
-		}
-	}
-
-	return w
-}
-
-// GetButton returns the given button
-func (w *WindowBase) GetButton(i int) *Button {
-	if i < 0 || i >= len(w.buttons) {
-		return nil
-	}
-	return w.buttons[i]
-}
-
-// ButtonCount returns the number of buttons in the window title bar
-func (w *WindowBase) ButtonCount() int {
-	return len(w.buttons)
-}

+ 0 - 13
vendor/github.com/gdamore/encoding/.appveyor.yml

@@ -1,13 +0,0 @@
-version: 1.0.{build}
-clone_folder: c:\gopath\src\github.com\gdamore\encoding
-environment:
-  GOPATH: c:\gopath
-build_script:
-- go version
-- go env
-- SET PATH=%LOCALAPPDATA%\atom\bin;%GOPATH%\bin;%PATH%
-- go get -t ./...
-- go build
-- go install ./...
-test_script:
-- go test ./...

+ 0 - 7
vendor/github.com/gdamore/encoding/.travis.yml

@@ -1,7 +0,0 @@
-language: go
-
-go:
-  - 1.9.x
-  - 1.10.x
-  - 1.11.x
-  - tip

+ 0 - 202
vendor/github.com/gdamore/encoding/LICENSE

@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.

+ 0 - 19
vendor/github.com/gdamore/encoding/README.md

@@ -1,19 +0,0 @@
-## encoding
-
-[![Linux Status](https://img.shields.io/travis/gdamore/encoding.svg?label=linux)](https://travis-ci.org/gdamore/encoding)
-[![Windows Status](https://img.shields.io/appveyor/ci/gdamore/encoding.svg?label=windows)](https://ci.appveyor.com/project/gdamore/encoding)
-[![Apache License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/gdamore/encoding/blob/master/LICENSE)
-[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/gdamore/encoding)
-[![Go Report Card](http://goreportcard.com/badge/gdamore/encoding)](http://goreportcard.com/report/gdamore/encoding)
-
-Package encoding provides a number of encodings that are missing from the
-standard Go [encoding]("https://godoc.org/golang.org/x/text/encoding") package.
-
-We hope that we can contribute these to the standard Go library someday.  It
-turns out that some of these are useful for dealing with I/O streams coming
-from non-UTF friendly sources.
-
-The UTF8 Encoder is also useful for situations where valid UTF-8 might be
-carried in streams that contain non-valid UTF; in particular I use it for
-helping me cope with terminals that embed escape sequences in otherwise
-valid UTF-8.

+ 0 - 36
vendor/github.com/gdamore/encoding/ascii.go

@@ -1,36 +0,0 @@
-// Copyright 2015 Garrett D'Amore
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use file except in compliance with the License.
-// You may obtain a copy of the license at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package encoding
-
-import (
-	"golang.org/x/text/encoding"
-)
-
-// ASCII represents the 7-bit US-ASCII scheme.  It decodes directly to
-// UTF-8 without change, as all ASCII values are legal UTF-8.
-// Unicode values less than 128 (i.e. 7 bits) map 1:1 with ASCII.
-// It encodes runes outside of that to 0x1A, the ASCII substitution character.
-var ASCII encoding.Encoding
-
-func init() {
-	amap := make(map[byte]rune)
-	for i := 128; i <= 255; i++ {
-		amap[byte(i)] = RuneError
-	}
-
-	cm := &Charmap{Map: amap}
-	cm.Init()
-	ASCII = cm
-}

+ 0 - 196
vendor/github.com/gdamore/encoding/charmap.go

@@ -1,196 +0,0 @@
-// Copyright 2015 Garrett D'Amore
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use file except in compliance with the License.
-// You may obtain a copy of the license at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package encoding
-
-import (
-	"sync"
-	"unicode/utf8"
-
-	"golang.org/x/text/encoding"
-	"golang.org/x/text/transform"
-)
-
-const (
-	// RuneError is an alias for the UTF-8 replacement rune, '\uFFFD'.
-	RuneError = '\uFFFD'
-
-	// RuneSelf is the rune below which UTF-8 and the Unicode values are
-	// identical.  Its also the limit for ASCII.
-	RuneSelf = 0x80
-
-	// ASCIISub is the ASCII substitution character.
-	ASCIISub = '\x1a'
-)
-
-// Charmap is a structure for setting up encodings for 8-bit character sets,
-// for transforming between UTF8 and that other character set.  It has some
-// ideas borrowed from golang.org/x/text/encoding/charmap, but it uses a
-// different implementation.  This implementation uses maps, and supports
-// user-defined maps.
-//
-// We do assume that a character map has a reasonable substitution character,
-// and that valid encodings are stable (exactly a 1:1 map) and stateless
-// (that is there is no shift character or anything like that.)  Hence this
-// approach will not work for many East Asian character sets.
-//
-// Measurement shows little or no measurable difference in the performance of
-// the two approaches.  The difference was down to a couple of nsec/op, and
-// no consistent pattern as to which ran faster.  With the conversion to
-// UTF-8 the code takes about 25 nsec/op.  The conversion in the reverse
-// direction takes about 100 nsec/op.  (The larger cost for conversion
-// from UTF-8 is most likely due to the need to convert the UTF-8 byte stream
-// to a rune before conversion.
-//
-type Charmap struct {
-	transform.NopResetter
-	bytes map[rune]byte
-	runes [256][]byte
-	once  sync.Once
-
-	// The map between bytes and runes.  To indicate that a specific
-	// byte value is invalid for a charcter set, use the rune
-	// utf8.RuneError.  Values that are absent from this map will
-	// be assumed to have the identity mapping -- that is the default
-	// is to assume ISO8859-1, where all 8-bit characters have the same
-	// numeric value as their Unicode runes.  (Not to be confused with
-	// the UTF-8 values, which *will* be different for non-ASCII runes.)
-	//
-	// If no values less than RuneSelf are changed (or have non-identity
-	// mappings), then the character set is assumed to be an ASCII
-	// superset, and certain assumptions and optimizations become
-	// available for ASCII bytes.
-	Map map[byte]rune
-
-	// The ReplacementChar is the byte value to use for substitution.
-	// It should normally be ASCIISub for ASCII encodings.  This may be
-	// unset (left to zero) for mappings that are strictly ASCII supersets.
-	// In that case ASCIISub will be assumed instead.
-	ReplacementChar byte
-}
-
-type cmapDecoder struct {
-	transform.NopResetter
-	runes [256][]byte
-}
-
-type cmapEncoder struct {
-	transform.NopResetter
-	bytes   map[rune]byte
-	replace byte
-}
-
-// Init initializes internal values of a character map.  This should
-// be done early, to minimize the cost of allocation of transforms
-// later.  It is not strictly necessary however, as the allocation
-// functions will arrange to call it if it has not already been done.
-func (c *Charmap) Init() {
-	c.once.Do(c.initialize)
-}
-
-func (c *Charmap) initialize() {
-	c.bytes = make(map[rune]byte)
-	ascii := true
-
-	for i := 0; i < 256; i++ {
-		r, ok := c.Map[byte(i)]
-		if !ok {
-			r = rune(i)
-		}
-		if r < 128 && r != rune(i) {
-			ascii = false
-		}
-		if r != RuneError {
-			c.bytes[r] = byte(i)
-		}
-		utf := make([]byte, utf8.RuneLen(r))
-		utf8.EncodeRune(utf, r)
-		c.runes[i] = utf
-	}
-	if ascii && c.ReplacementChar == '\x00' {
-		c.ReplacementChar = ASCIISub
-	}
-}
-
-// NewDecoder returns a Decoder the converts from the 8-bit
-// character set to UTF-8.  Unknown mappings, if any, are mapped
-// to '\uFFFD'.
-func (c *Charmap) NewDecoder() *encoding.Decoder {
-	c.Init()
-	return &encoding.Decoder{Transformer: &cmapDecoder{runes: c.runes}}
-}
-
-// NewEncoder returns a Transformer that converts from UTF8 to the
-// 8-bit character set.  Unknown mappings are mapped to 0x1A.
-func (c *Charmap) NewEncoder() *encoding.Encoder {
-	c.Init()
-	return &encoding.Encoder{
-		Transformer: &cmapEncoder{
-			bytes:   c.bytes,
-			replace: c.ReplacementChar,
-		},
-	}
-}
-
-func (d *cmapDecoder) Transform(dst, src []byte, atEOF bool) (int, int, error) {
-	var e error
-	var ndst, nsrc int
-
-	for _, c := range src {
-		b := d.runes[c]
-		l := len(b)
-
-		if ndst+l > len(dst) {
-			e = transform.ErrShortDst
-			break
-		}
-		for i := 0; i < l; i++ {
-			dst[ndst] = b[i]
-			ndst++
-		}
-		nsrc++
-	}
-	return ndst, nsrc, e
-}
-
-func (d *cmapEncoder) Transform(dst, src []byte, atEOF bool) (int, int, error) {
-	var e error
-	var ndst, nsrc int
-	for nsrc < len(src) {
-		if ndst >= len(dst) {
-			e = transform.ErrShortDst
-			break
-		}
-
-		r, sz := utf8.DecodeRune(src[nsrc:])
-		if r == utf8.RuneError && sz == 1 {
-			// If its inconclusive due to insufficient data in
-			// in the source, report it
-			if !atEOF && !utf8.FullRune(src[nsrc:]) {
-				e = transform.ErrShortSrc
-				break
-			}
-		}
-
-		if c, ok := d.bytes[r]; ok {
-			dst[ndst] = c
-		} else {
-			dst[ndst] = d.replace
-		}
-		nsrc += sz
-		ndst++
-	}
-
-	return ndst, nsrc, e
-}

+ 0 - 17
vendor/github.com/gdamore/encoding/doc.go

@@ -1,17 +0,0 @@
-// Copyright 2015 Garrett D'Amore
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use file except in compliance with the License.
-// You may obtain a copy of the license at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package encoding provides a few of the encoding structures that are
-// missing from the Go x/text/encoding tree.
-package encoding

+ 0 - 273
vendor/github.com/gdamore/encoding/ebcdic.go

@@ -1,273 +0,0 @@
-// Copyright 2015 Garrett D'Amore
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use file except in compliance with the License.
-// You may obtain a copy of the license at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package encoding
-
-import (
-	"golang.org/x/text/encoding"
-)
-
-// EBCDIC represents the 8-bit EBCDIC scheme, found in some mainframe
-// environments.  If you don't know what this is, consider yourself lucky.
-var EBCDIC encoding.Encoding
-
-func init() {
-	cm := &Charmap{
-		ReplacementChar: '\x3f',
-		Map: map[byte]rune{
-			// 0x00-0x03 match
-			0x04: RuneError,
-			0x05: '\t',
-			0x06: RuneError,
-			0x07: '\x7f',
-			0x08: RuneError,
-			0x09: RuneError,
-			0x0a: RuneError,
-			// 0x0b-0x13 match
-			0x14: RuneError,
-			0x15: '\x85', // Not in any ISO code
-			0x16: '\x08',
-			0x17: RuneError,
-			// 0x18-0x19 match
-			0x1a: RuneError,
-			0x1b: RuneError,
-			// 0x1c-0x1f match
-			0x20: RuneError,
-			0x21: RuneError,
-			0x22: RuneError,
-			0x23: RuneError,
-			0x24: RuneError,
-			0x25: '\n',
-			0x26: '\x17',
-			0x27: '\x1b',
-			0x28: RuneError,
-			0x29: RuneError,
-			0x2a: RuneError,
-			0x2b: RuneError,
-			0x2c: RuneError,
-			0x2d: '\x05',
-			0x2e: '\x06',
-			0x2f: '\x07',
-			0x30: RuneError,
-			0x31: RuneError,
-			0x32: '\x16',
-			0x33: RuneError,
-			0x34: RuneError,
-			0x35: RuneError,
-			0x36: RuneError,
-			0x37: '\x04',
-			0x38: RuneError,
-			0x39: RuneError,
-			0x3a: RuneError,
-			0x3b: RuneError,
-			0x3c: '\x14',
-			0x3d: '\x15',
-			0x3e: RuneError,
-			0x3f: '\x1a', // also replacement char
-			0x40: ' ',
-			0x41: '\xa0',
-			0x42: RuneError,
-			0x43: RuneError,
-			0x44: RuneError,
-			0x45: RuneError,
-			0x46: RuneError,
-			0x47: RuneError,
-			0x48: RuneError,
-			0x49: RuneError,
-			0x4a: RuneError,
-			0x4b: '.',
-			0x4c: '<',
-			0x4d: '(',
-			0x4e: '+',
-			0x4f: '|',
-			0x50: '&',
-			0x51: RuneError,
-			0x52: RuneError,
-			0x53: RuneError,
-			0x54: RuneError,
-			0x55: RuneError,
-			0x56: RuneError,
-			0x57: RuneError,
-			0x58: RuneError,
-			0x59: RuneError,
-			0x5a: '!',
-			0x5b: '$',
-			0x5c: '*',
-			0x5d: ')',
-			0x5e: ';',
-			0x5f: '¬',
-			0x60: '-',
-			0x61: '/',
-			0x62: RuneError,
-			0x63: RuneError,
-			0x64: RuneError,
-			0x65: RuneError,
-			0x66: RuneError,
-			0x67: RuneError,
-			0x68: RuneError,
-			0x69: RuneError,
-			0x6a: '¦',
-			0x6b: ',',
-			0x6c: '%',
-			0x6d: '_',
-			0x6e: '>',
-			0x6f: '?',
-			0x70: RuneError,
-			0x71: RuneError,
-			0x72: RuneError,
-			0x73: RuneError,
-			0x74: RuneError,
-			0x75: RuneError,
-			0x76: RuneError,
-			0x77: RuneError,
-			0x78: RuneError,
-			0x79: '`',
-			0x7a: ':',
-			0x7b: '#',
-			0x7c: '@',
-			0x7d: '\'',
-			0x7e: '=',
-			0x7f: '"',
-			0x80: RuneError,
-			0x81: 'a',
-			0x82: 'b',
-			0x83: 'c',
-			0x84: 'd',
-			0x85: 'e',
-			0x86: 'f',
-			0x87: 'g',
-			0x88: 'h',
-			0x89: 'i',
-			0x8a: RuneError,
-			0x8b: RuneError,
-			0x8c: RuneError,
-			0x8d: RuneError,
-			0x8e: RuneError,
-			0x8f: '±',
-			0x90: RuneError,
-			0x91: 'j',
-			0x92: 'k',
-			0x93: 'l',
-			0x94: 'm',
-			0x95: 'n',
-			0x96: 'o',
-			0x97: 'p',
-			0x98: 'q',
-			0x99: 'r',
-			0x9a: RuneError,
-			0x9b: RuneError,
-			0x9c: RuneError,
-			0x9d: RuneError,
-			0x9e: RuneError,
-			0x9f: RuneError,
-			0xa0: RuneError,
-			0xa1: '~',
-			0xa2: 's',
-			0xa3: 't',
-			0xa4: 'u',
-			0xa5: 'v',
-			0xa6: 'w',
-			0xa7: 'x',
-			0xa8: 'y',
-			0xa9: 'z',
-			0xaa: RuneError,
-			0xab: RuneError,
-			0xac: RuneError,
-			0xad: RuneError,
-			0xae: RuneError,
-			0xaf: RuneError,
-			0xb0: '^',
-			0xb1: RuneError,
-			0xb2: RuneError,
-			0xb3: RuneError,
-			0xb4: RuneError,
-			0xb5: RuneError,
-			0xb6: RuneError,
-			0xb7: RuneError,
-			0xb8: RuneError,
-			0xb9: RuneError,
-			0xba: '[',
-			0xbb: ']',
-			0xbc: RuneError,
-			0xbd: RuneError,
-			0xbe: RuneError,
-			0xbf: RuneError,
-			0xc0: '{',
-			0xc1: 'A',
-			0xc2: 'B',
-			0xc3: 'C',
-			0xc4: 'D',
-			0xc5: 'E',
-			0xc6: 'F',
-			0xc7: 'G',
-			0xc8: 'H',
-			0xc9: 'I',
-			0xca: '\xad', // NB: soft hyphen
-			0xcb: RuneError,
-			0xcc: RuneError,
-			0xcd: RuneError,
-			0xce: RuneError,
-			0xcf: RuneError,
-			0xd0: '}',
-			0xd1: 'J',
-			0xd2: 'K',
-			0xd3: 'L',
-			0xd4: 'M',
-			0xd5: 'N',
-			0xd6: 'O',
-			0xd7: 'P',
-			0xd8: 'Q',
-			0xd9: 'R',
-			0xda: RuneError,
-			0xdb: RuneError,
-			0xdc: RuneError,
-			0xdd: RuneError,
-			0xde: RuneError,
-			0xdf: RuneError,
-			0xe0: '\\',
-			0xe1: '\u2007', // Non-breaking space
-			0xe2: 'S',
-			0xe3: 'T',
-			0xe4: 'U',
-			0xe5: 'V',
-			0xe6: 'W',
-			0xe7: 'X',
-			0xe8: 'Y',
-			0xe9: 'Z',
-			0xea: RuneError,
-			0xeb: RuneError,
-			0xec: RuneError,
-			0xed: RuneError,
-			0xee: RuneError,
-			0xef: RuneError,
-			0xf0: '0',
-			0xf1: '1',
-			0xf2: '2',
-			0xf3: '3',
-			0xf4: '4',
-			0xf5: '5',
-			0xf6: '6',
-			0xf7: '7',
-			0xf8: '8',
-			0xf9: '9',
-			0xfa: RuneError,
-			0xfb: RuneError,
-			0xfc: RuneError,
-			0xfd: RuneError,
-			0xfe: RuneError,
-			0xff: RuneError,
-		}}
-	cm.Init()
-	EBCDIC = cm
-}

+ 0 - 33
vendor/github.com/gdamore/encoding/latin1.go

@@ -1,33 +0,0 @@
-// Copyright 2015 Garrett D'Amore
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use file except in compliance with the License.
-// You may obtain a copy of the license at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package encoding
-
-import (
-	"golang.org/x/text/encoding"
-)
-
-// ISO8859_1 represents the 8-bit ISO8859-1 scheme.  It decodes directly to
-// UTF-8 without change, as all ISO8859-1 values are legal UTF-8.
-// Unicode values less than 256 (i.e. 8 bits) map 1:1 with 8859-1.
-// It encodes runes outside of that to 0x1A, the ASCII substitution character.
-var ISO8859_1 encoding.Encoding
-
-func init() {
-	cm := &Charmap{}
-	cm.Init()
-
-	// 8859-1 is the 8-bit identity map for Unicode.
-	ISO8859_1 = cm
-}

+ 0 - 35
vendor/github.com/gdamore/encoding/latin5.go

@@ -1,35 +0,0 @@
-// Copyright 2015 Garrett D'Amore
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use file except in compliance with the License.
-// You may obtain a copy of the license at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package encoding
-
-import (
-	"golang.org/x/text/encoding"
-)
-
-// ISO8859_9 represents the 8-bit ISO8859-9 scheme.
-var ISO8859_9 encoding.Encoding
-
-func init() {
-	cm := &Charmap{Map: map[byte]rune{
-		0xD0: 'Ğ',
-		0xDD: 'İ',
-		0xDE: 'Ş',
-		0xF0: 'ğ',
-		0xFD: 'ı',
-		0xFE: 'ş',
-	}}
-	cm.Init()
-	ISO8859_9 = cm
-}

+ 0 - 35
vendor/github.com/gdamore/encoding/utf8.go

@@ -1,35 +0,0 @@
-// Copyright 2015 Garrett D'Amore
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use file except in compliance with the License.
-// You may obtain a copy of the license at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package encoding
-
-import (
-	"golang.org/x/text/encoding"
-)
-
-type validUtf8 struct{}
-
-// UTF8 is an encoding for UTF-8.  All it does is verify that the UTF-8
-// in is valid.  The main reason for its existence is that it will detect
-// and report ErrSrcShort or ErrDstShort, whereas the Nop encoding just
-// passes every byte, blithely.
-var UTF8 encoding.Encoding = validUtf8{}
-
-func (validUtf8) NewDecoder() *encoding.Decoder {
-	return &encoding.Decoder{Transformer: encoding.UTF8Validator}
-}
-
-func (validUtf8) NewEncoder() *encoding.Encoder {
-	return &encoding.Encoder{Transformer: encoding.UTF8Validator}
-}

+ 0 - 13
vendor/github.com/gdamore/tcell/v2/.appveyor.yml

@@ -1,13 +0,0 @@
-version: 1.0.{build}
-clone_folder: c:\gopath\src\github.com\gdamore\tcell
-environment:
-  GOPATH: c:\gopath
-build_script:
-- go version
-- go env
-- SET PATH=%LOCALAPPDATA%\atom\bin;%GOPATH%\bin;%PATH%
-- go get -t ./...
-- go build
-- go install ./...
-test_script:
-- go test ./...

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików