summaryrefslogtreecommitdiff
path: root/vendor/github.com/charmbracelet
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-24 17:58:01 -0600
committermo khan <mo@mokhan.ca>2025-07-24 17:58:01 -0600
commit72296119fc9755774719f8f625ad03e0e0ec457a (patch)
treeed236ddee12a20fb55b7cfecf13f62d3a000dcb5 /vendor/github.com/charmbracelet
parenta920a8cfe415858bb2777371a77018599ffed23f (diff)
parenteaa1bd3b8e12934aed06413d75e7482ac58d805a (diff)
Merge branch 'the-spice-must-flow' into 'main'
Add SpiceDB Authorization See merge request gitlab-org/software-supply-chain-security/authorization/sparkled!19
Diffstat (limited to 'vendor/github.com/charmbracelet')
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/.golangci-soft.yml40
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/.golangci.yml28
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/.goreleaser.yml6
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/LICENSE21
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/README.md103
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/env.go287
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/env_other.go8
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/env_windows.go45
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/profile.go399
-rw-r--r--vendor/github.com/charmbracelet/colorprofile/writer.go166
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/.gitignore2
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/.golangci.yml41
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/.goreleaser.yml5
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/LICENSE21
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/README.md815
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/Taskfile.yaml19
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/align.go83
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/ansi_unix.go7
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/ansi_windows.go22
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/borders.go490
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/color.go172
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/get.go556
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/join.go175
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/position.go154
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/ranges.go48
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/renderer.go181
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/runes.go43
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/set.go799
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/size.go41
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/style.go588
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/unset.go331
-rw-r--r--vendor/github.com/charmbracelet/lipgloss/whitespace.go83
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/LICENSE21
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/ansi.go11
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/ascii.go8
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/background.go169
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/c0.go79
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/c1.go72
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/charset.go55
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/clipboard.go75
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/color.go196
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/ctrl.go137
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/cursor.go633
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/cwd.go26
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/doc.go7
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/focus.go9
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/graphics.go199
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/hyperlink.go28
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/iterm2.go18
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/keypad.go28
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/kitty.go90
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/kitty/decoder.go85
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/kitty/encoder.go64
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/kitty/graphics.go414
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/kitty/options.go367
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/method.go172
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/mode.go763
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/modes.go71
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/mouse.go172
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/notification.go13
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/parser.go417
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/parser/const.go78
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/parser/seq.go136
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go273
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/parser_decode.go524
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/parser_handler.go60
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/parser_sync.go29
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/passthrough.go63
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/paste.go7
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/reset.go11
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/screen.go410
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/sgr.go95
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/status.go144
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/style.go660
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/termcap.go41
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/title.go32
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/truncate.go282
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/util.go106
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/width.go113
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/winop.go53
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/wrap.go467
-rw-r--r--vendor/github.com/charmbracelet/x/ansi/xterm.go138
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/LICENSE21
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/buffer.go473
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/cell.go503
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/errors.go6
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/geom.go21
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/hardscroll.go272
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/hashmap.go301
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/link.go14
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/screen.go1457
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/sequence.go131
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/style.go31
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/tabstop.go137
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/utils.go38
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/wrap.go178
-rw-r--r--vendor/github.com/charmbracelet/x/cellbuf/writer.go339
-rw-r--r--vendor/github.com/charmbracelet/x/term/LICENSE21
-rw-r--r--vendor/github.com/charmbracelet/x/term/term.go49
-rw-r--r--vendor/github.com/charmbracelet/x/term/term_other.go39
-rw-r--r--vendor/github.com/charmbracelet/x/term/term_unix.go96
-rw-r--r--vendor/github.com/charmbracelet/x/term/term_unix_bsd.go11
-rw-r--r--vendor/github.com/charmbracelet/x/term/term_unix_other.go11
-rw-r--r--vendor/github.com/charmbracelet/x/term/term_windows.go87
-rw-r--r--vendor/github.com/charmbracelet/x/term/terminal.go12
-rw-r--r--vendor/github.com/charmbracelet/x/term/util.go47
106 files changed, 18195 insertions, 0 deletions
diff --git a/vendor/github.com/charmbracelet/colorprofile/.golangci-soft.yml b/vendor/github.com/charmbracelet/colorprofile/.golangci-soft.yml
new file mode 100644
index 0000000..d325d4f
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/.golangci-soft.yml
@@ -0,0 +1,40 @@
+run:
+ tests: false
+ issues-exit-code: 0
+
+issues:
+ include:
+ - EXC0001
+ - EXC0005
+ - EXC0011
+ - EXC0012
+ - EXC0013
+
+ max-issues-per-linter: 0
+ max-same-issues: 0
+
+linters:
+ enable:
+ - exhaustive
+ - goconst
+ - godot
+ - godox
+ - mnd
+ - gomoddirectives
+ - goprintffuncname
+ - misspell
+ - nakedret
+ - nestif
+ - noctx
+ - nolintlint
+ - prealloc
+ - wrapcheck
+
+ # disable default linters, they are already enabled in .golangci.yml
+ disable:
+ - errcheck
+ - gosimple
+ - govet
+ - ineffassign
+ - staticcheck
+ - unused
diff --git a/vendor/github.com/charmbracelet/colorprofile/.golangci.yml b/vendor/github.com/charmbracelet/colorprofile/.golangci.yml
new file mode 100644
index 0000000..d6789e0
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/.golangci.yml
@@ -0,0 +1,28 @@
+run:
+ tests: false
+
+issues:
+ include:
+ - EXC0001
+ - EXC0005
+ - EXC0011
+ - EXC0012
+ - EXC0013
+
+ max-issues-per-linter: 0
+ max-same-issues: 0
+
+linters:
+ enable:
+ - bodyclose
+ - gofumpt
+ - goimports
+ - gosec
+ - nilerr
+ - revive
+ - rowserrcheck
+ - sqlclosecheck
+ - tparallel
+ - unconvert
+ - unparam
+ - whitespace
diff --git a/vendor/github.com/charmbracelet/colorprofile/.goreleaser.yml b/vendor/github.com/charmbracelet/colorprofile/.goreleaser.yml
new file mode 100644
index 0000000..40d9f29
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/.goreleaser.yml
@@ -0,0 +1,6 @@
+includes:
+ - from_url:
+ url: charmbracelet/meta/main/goreleaser-lib.yaml
+
+# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
+
diff --git a/vendor/github.com/charmbracelet/colorprofile/LICENSE b/vendor/github.com/charmbracelet/colorprofile/LICENSE
new file mode 100644
index 0000000..b7974b0
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020-2024 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.
diff --git a/vendor/github.com/charmbracelet/colorprofile/README.md b/vendor/github.com/charmbracelet/colorprofile/README.md
new file mode 100644
index 0000000..c72b2f4
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/README.md
@@ -0,0 +1,103 @@
+# Colorprofile
+
+<p>
+ <a href="https://github.com/charmbracelet/colorprofile/releases"><img src="https://img.shields.io/github/release/charmbracelet/colorprofile.svg" alt="Latest Release"></a>
+ <a href="https://pkg.go.dev/github.com/charmbracelet/colorprofile?tab=doc"><img src="https://godoc.org/github.com/charmbracelet/colorprofile?status.svg" alt="GoDoc"></a>
+ <a href="https://github.com/charmbracelet/colorprofile/actions"><img src="https://github.com/charmbracelet/colorprofile/actions/workflows/build.yml/badge.svg" alt="Build Status"></a>
+</p>
+
+A simple, powerful—and at times magical—package for detecting terminal color
+profiles and performing color (and CSI) degradation.
+
+## Detecting the terminal’s color profile
+
+Detecting the terminal’s color profile is easy.
+
+```go
+import "github.com/charmbracelet/colorprofile"
+
+// Detect the color profile. If you’re planning on writing to stderr you'd want
+// to use os.Stderr instead.
+p := colorprofile.Detect(os.Stdout, os.Environ())
+
+// Comment on the profile.
+fmt.Printf("You know, your colors are quite %s.", func() string {
+ switch p {
+ case colorprofile.TrueColor:
+ return "fancy"
+ case colorprofile.ANSI256:
+ return "1990s fancy"
+ case colorprofile.ANSI:
+ return "normcore"
+ case colorprofile.Ascii:
+ return "ancient"
+ case colorprofile.NoTTY:
+ return "naughty!"
+ }
+ return "...IDK" // this should never happen
+}())
+```
+
+## Downsampling colors
+
+When necessary, colors can be downsampled to a given profile, or manually
+downsampled to a specific profile.
+
+```go
+p := colorprofile.Detect(os.Stdout, os.Environ())
+c := color.RGBA{0x6b, 0x50, 0xff, 0xff} // #6b50ff
+
+// Downsample to the detected profile, when necessary.
+convertedColor := p.Convert(c)
+
+// Or manually convert to a given profile.
+ansi256Color := colorprofile.ANSI256.Convert(c)
+ansiColor := colorprofile.ANSI.Convert(c)
+noColor := colorprofile.Ascii.Convert(c)
+noANSI := colorprofile.NoTTY.Convert(c)
+```
+
+## Automatic downsampling with a Writer
+
+You can also magically downsample colors in ANSI output, when necessary. If
+output is not a TTY ANSI will be dropped entirely.
+
+```go
+myFancyANSI := "\x1b[38;2;107;80;255mCute \x1b[1;3mpuppy!!\x1b[m"
+
+// Automatically downsample for the terminal at stdout.
+w := colorprofile.NewWriter(os.Stdout, os.Environ())
+fmt.Fprintf(w, myFancyANSI)
+
+// Downsample to 4-bit ANSI.
+w.Profile = colorprofile.ANSI
+fmt.Fprintf(w, myFancyANSI)
+
+// Ascii-fy, no colors.
+w.Profile = colorprofile.Ascii
+fmt.Fprintf(w, myFancyANSI)
+
+// Strip ANSI altogether.
+w.Profile = colorprofile.NoTTY
+fmt.Fprintf(w, myFancyANSI) // not as fancy
+```
+
+## 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/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 • نحنُ نحب المصادر المفتوحة
diff --git a/vendor/github.com/charmbracelet/colorprofile/env.go b/vendor/github.com/charmbracelet/colorprofile/env.go
new file mode 100644
index 0000000..8df3d8f
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/env.go
@@ -0,0 +1,287 @@
+package colorprofile
+
+import (
+ "bytes"
+ "io"
+ "os/exec"
+ "runtime"
+ "strconv"
+ "strings"
+
+ "github.com/charmbracelet/x/term"
+ "github.com/xo/terminfo"
+)
+
+// Detect returns the color profile based on the terminal output, and
+// environment variables. This respects NO_COLOR, CLICOLOR, and CLICOLOR_FORCE
+// environment variables.
+//
+// The rules as follows:
+// - TERM=dumb is always treated as NoTTY unless CLICOLOR_FORCE=1 is set.
+// - If COLORTERM=truecolor, and the profile is not NoTTY, it gest upgraded to TrueColor.
+// - Using any 256 color terminal (e.g. TERM=xterm-256color) will set the profile to ANSI256.
+// - Using any color terminal (e.g. TERM=xterm-color) will set the profile to ANSI.
+// - Using CLICOLOR=1 without TERM defined should be treated as ANSI if the
+// output is a terminal.
+// - NO_COLOR takes precedence over CLICOLOR/CLICOLOR_FORCE, and will disable
+// colors but not text decoration, i.e. bold, italic, faint, etc.
+//
+// See https://no-color.org/ and https://bixense.com/clicolors/ for more information.
+func Detect(output io.Writer, env []string) Profile {
+ out, ok := output.(term.File)
+ isatty := ok && term.IsTerminal(out.Fd())
+ environ := newEnviron(env)
+ term := environ.get("TERM")
+ isDumb := term == "dumb"
+ envp := colorProfile(isatty, environ)
+ if envp == TrueColor || envNoColor(environ) {
+ // We already know we have TrueColor, or NO_COLOR is set.
+ return envp
+ }
+
+ if isatty && !isDumb {
+ tip := Terminfo(term)
+ tmuxp := tmux(environ)
+
+ // Color profile is the maximum of env, terminfo, and tmux.
+ return max(envp, max(tip, tmuxp))
+ }
+
+ return envp
+}
+
+// Env returns the color profile based on the terminal environment variables.
+// This respects NO_COLOR, CLICOLOR, and CLICOLOR_FORCE environment variables.
+//
+// The rules as follows:
+// - TERM=dumb is always treated as NoTTY unless CLICOLOR_FORCE=1 is set.
+// - If COLORTERM=truecolor, and the profile is not NoTTY, it gest upgraded to TrueColor.
+// - Using any 256 color terminal (e.g. TERM=xterm-256color) will set the profile to ANSI256.
+// - Using any color terminal (e.g. TERM=xterm-color) will set the profile to ANSI.
+// - Using CLICOLOR=1 without TERM defined should be treated as ANSI if the
+// output is a terminal.
+// - NO_COLOR takes precedence over CLICOLOR/CLICOLOR_FORCE, and will disable
+// colors but not text decoration, i.e. bold, italic, faint, etc.
+//
+// See https://no-color.org/ and https://bixense.com/clicolors/ for more information.
+func Env(env []string) (p Profile) {
+ return colorProfile(true, newEnviron(env))
+}
+
+func colorProfile(isatty bool, env environ) (p Profile) {
+ isDumb := env.get("TERM") == "dumb"
+ envp := envColorProfile(env)
+ if !isatty || isDumb {
+ // Check if the output is a terminal.
+ // Treat dumb terminals as NoTTY
+ p = NoTTY
+ } else {
+ p = envp
+ }
+
+ if envNoColor(env) && isatty {
+ if p > Ascii {
+ p = Ascii
+ }
+ return
+ }
+
+ if cliColorForced(env) {
+ if p < ANSI {
+ p = ANSI
+ }
+ if envp > p {
+ p = envp
+ }
+
+ return
+ }
+
+ if cliColor(env) {
+ if isatty && !isDumb && p < ANSI {
+ p = ANSI
+ }
+ }
+
+ return p
+}
+
+// envNoColor returns true if the environment variables explicitly disable color output
+// by setting NO_COLOR (https://no-color.org/).
+func envNoColor(env environ) bool {
+ noColor, _ := strconv.ParseBool(env.get("NO_COLOR"))
+ return noColor
+}
+
+func cliColor(env environ) bool {
+ cliColor, _ := strconv.ParseBool(env.get("CLICOLOR"))
+ return cliColor
+}
+
+func cliColorForced(env environ) bool {
+ cliColorForce, _ := strconv.ParseBool(env.get("CLICOLOR_FORCE"))
+ return cliColorForce
+}
+
+func colorTerm(env environ) bool {
+ colorTerm := strings.ToLower(env.get("COLORTERM"))
+ return colorTerm == "truecolor" || colorTerm == "24bit" ||
+ colorTerm == "yes" || colorTerm == "true"
+}
+
+// envColorProfile returns infers the color profile from the environment.
+func envColorProfile(env environ) (p Profile) {
+ term, ok := env.lookup("TERM")
+ if !ok || len(term) == 0 || term == "dumb" {
+ p = NoTTY
+ if runtime.GOOS == "windows" {
+ // Use Windows API to detect color profile. Windows Terminal and
+ // cmd.exe don't define $TERM.
+ if wcp, ok := windowsColorProfile(env); ok {
+ p = wcp
+ }
+ }
+ } else {
+ p = ANSI
+ }
+
+ parts := strings.Split(term, "-")
+ switch parts[0] {
+ case "alacritty",
+ "contour",
+ "foot",
+ "ghostty",
+ "kitty",
+ "rio",
+ "st",
+ "wezterm":
+ return TrueColor
+ case "xterm":
+ if len(parts) > 1 {
+ switch parts[1] {
+ case "ghostty", "kitty":
+ // These terminals can be defined as xterm-TERMNAME
+ return TrueColor
+ }
+ }
+ case "tmux", "screen":
+ if p < ANSI256 {
+ p = ANSI256
+ }
+ }
+
+ if isCloudShell, _ := strconv.ParseBool(env.get("GOOGLE_CLOUD_SHELL")); isCloudShell {
+ return TrueColor
+ }
+
+ // GNU Screen doesn't support TrueColor
+ // Tmux doesn't support $COLORTERM
+ if colorTerm(env) && !strings.HasPrefix(term, "screen") && !strings.HasPrefix(term, "tmux") {
+ return TrueColor
+ }
+
+ if strings.HasSuffix(term, "256color") && p < ANSI256 {
+ p = ANSI256
+ }
+
+ return
+}
+
+// Terminfo returns the color profile based on the terminal's terminfo
+// database. This relies on the Tc and RGB capabilities to determine if the
+// terminal supports TrueColor.
+// If term is empty or "dumb", it returns NoTTY.
+func Terminfo(term string) (p Profile) {
+ if len(term) == 0 || term == "dumb" {
+ return NoTTY
+ }
+
+ p = ANSI
+ ti, err := terminfo.Load(term)
+ if err != nil {
+ return
+ }
+
+ extbools := ti.ExtBoolCapsShort()
+ if _, ok := extbools["Tc"]; ok {
+ return TrueColor
+ }
+
+ if _, ok := extbools["RGB"]; ok {
+ return TrueColor
+ }
+
+ return
+}
+
+// Tmux returns the color profile based on `tmux info` output. Tmux supports
+// overriding the terminal's color capabilities, so this function will return
+// the color profile based on the tmux configuration.
+func Tmux(env []string) Profile {
+ return tmux(newEnviron(env))
+}
+
+// tmux returns the color profile based on the tmux environment variables.
+func tmux(env environ) (p Profile) {
+ if tmux, ok := env.lookup("TMUX"); !ok || len(tmux) == 0 {
+ // Not in tmux
+ return NoTTY
+ }
+
+ // Check if tmux has either Tc or RGB capabilities. Otherwise, return
+ // ANSI256.
+ p = ANSI256
+ cmd := exec.Command("tmux", "info")
+ out, err := cmd.Output()
+ if err != nil {
+ return
+ }
+
+ for _, line := range bytes.Split(out, []byte("\n")) {
+ if (bytes.Contains(line, []byte("Tc")) || bytes.Contains(line, []byte("RGB"))) &&
+ bytes.Contains(line, []byte("true")) {
+ return TrueColor
+ }
+ }
+
+ return
+}
+
+// environ is a map of environment variables.
+type environ map[string]string
+
+// newEnviron returns a new environment map from a slice of environment
+// variables.
+func newEnviron(environ []string) environ {
+ m := make(map[string]string, len(environ))
+ for _, e := range environ {
+ parts := strings.SplitN(e, "=", 2)
+ var value string
+ if len(parts) == 2 {
+ value = parts[1]
+ }
+ m[parts[0]] = value
+ }
+ return m
+}
+
+// lookup returns the value of an environment variable and a boolean indicating
+// if it exists.
+func (e environ) lookup(key string) (string, bool) {
+ v, ok := e[key]
+ return v, ok
+}
+
+// get returns the value of an environment variable and empty string if it
+// doesn't exist.
+func (e environ) get(key string) string {
+ v, _ := e.lookup(key)
+ return v
+}
+
+func max[T ~byte | ~int](a, b T) T {
+ if a > b {
+ return a
+ }
+ return b
+}
diff --git a/vendor/github.com/charmbracelet/colorprofile/env_other.go b/vendor/github.com/charmbracelet/colorprofile/env_other.go
new file mode 100644
index 0000000..080994b
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/env_other.go
@@ -0,0 +1,8 @@
+//go:build !windows
+// +build !windows
+
+package colorprofile
+
+func windowsColorProfile(map[string]string) (Profile, bool) {
+ return 0, false
+}
diff --git a/vendor/github.com/charmbracelet/colorprofile/env_windows.go b/vendor/github.com/charmbracelet/colorprofile/env_windows.go
new file mode 100644
index 0000000..3b9c28f
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/env_windows.go
@@ -0,0 +1,45 @@
+//go:build windows
+// +build windows
+
+package colorprofile
+
+import (
+ "strconv"
+
+ "golang.org/x/sys/windows"
+)
+
+func windowsColorProfile(env map[string]string) (Profile, bool) {
+ if env["ConEmuANSI"] == "ON" {
+ return TrueColor, true
+ }
+
+ if len(env["WT_SESSION"]) > 0 {
+ // Windows Terminal supports TrueColor
+ return TrueColor, true
+ }
+
+ major, _, build := windows.RtlGetNtVersionNumbers()
+ if build < 10586 || major < 10 {
+ // No ANSI support before WindowsNT 10 build 10586
+ if len(env["ANSICON"]) > 0 {
+ ansiconVer := env["ANSICON_VER"]
+ cv, err := strconv.Atoi(ansiconVer)
+ if err != nil || cv < 181 {
+ // No 8 bit color support before ANSICON 1.81
+ return ANSI, true
+ }
+
+ return ANSI256, true
+ }
+
+ return NoTTY, true
+ }
+
+ if build < 14931 {
+ // No true color support before build 14931
+ return ANSI256, true
+ }
+
+ return TrueColor, true
+}
diff --git a/vendor/github.com/charmbracelet/colorprofile/profile.go b/vendor/github.com/charmbracelet/colorprofile/profile.go
new file mode 100644
index 0000000..97e37ac
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/profile.go
@@ -0,0 +1,399 @@
+package colorprofile
+
+import (
+ "image/color"
+ "math"
+
+ "github.com/charmbracelet/x/ansi"
+ "github.com/lucasb-eyer/go-colorful"
+)
+
+// Profile is a color profile: NoTTY, Ascii, ANSI, ANSI256, or TrueColor.
+type Profile byte
+
+const (
+ // NoTTY, not a terminal profile.
+ NoTTY Profile = iota
+ // Ascii, uncolored profile.
+ Ascii //nolint:revive
+ // ANSI, 4-bit color profile.
+ ANSI
+ // ANSI256, 8-bit color profile.
+ ANSI256
+ // TrueColor, 24-bit color profile.
+ TrueColor
+)
+
+// String returns the string representation of a Profile.
+func (p Profile) String() string {
+ switch p {
+ case TrueColor:
+ return "TrueColor"
+ case ANSI256:
+ return "ANSI256"
+ case ANSI:
+ return "ANSI"
+ case Ascii:
+ return "Ascii"
+ case NoTTY:
+ return "NoTTY"
+ }
+ return "Unknown"
+}
+
+// Convert transforms a given Color to a Color supported within the Profile.
+func (p Profile) Convert(c color.Color) color.Color {
+ if p <= Ascii {
+ return nil
+ }
+
+ switch c := c.(type) {
+ case ansi.BasicColor:
+ return c
+
+ case ansi.ExtendedColor:
+ if p == ANSI {
+ return ansi256ToANSIColor(c)
+ }
+ return c
+
+ case ansi.TrueColor, color.Color:
+ h, ok := colorful.MakeColor(c)
+ if !ok {
+ return nil
+ }
+ if p != TrueColor {
+ ac := hexToANSI256Color(h)
+ if p == ANSI {
+ return ansi256ToANSIColor(ac)
+ }
+ return ac
+ }
+ return c
+ }
+
+ return c
+}
+
+func hexToANSI256Color(c colorful.Color) ansi.ExtendedColor {
+ v2ci := func(v float64) int {
+ if v < 48 {
+ return 0
+ }
+ if v < 115 {
+ return 1
+ }
+ return int((v - 35) / 40)
+ }
+
+ // Calculate the nearest 0-based color index at 16..231
+ r := v2ci(c.R * 255.0) // 0..5 each
+ g := v2ci(c.G * 255.0)
+ b := v2ci(c.B * 255.0)
+ ci := 36*r + 6*g + b /* 0..215 */
+
+ // Calculate the represented colors back from the index
+ i2cv := [6]int{0, 0x5f, 0x87, 0xaf, 0xd7, 0xff}
+ cr := i2cv[r] // r/g/b, 0..255 each
+ cg := i2cv[g]
+ cb := i2cv[b]
+
+ // Calculate the nearest 0-based gray index at 232..255
+ var grayIdx int
+ average := (cr + cg + cb) / 3
+ if average > 238 {
+ grayIdx = 23
+ } else {
+ grayIdx = (average - 3) / 10 // 0..23
+ }
+ gv := 8 + 10*grayIdx // same value for r/g/b, 0..255
+
+ // Return the one which is nearer to the original input rgb value
+ c2 := colorful.Color{R: float64(cr) / 255.0, G: float64(cg) / 255.0, B: float64(cb) / 255.0}
+ g2 := colorful.Color{R: float64(gv) / 255.0, G: float64(gv) / 255.0, B: float64(gv) / 255.0}
+ colorDist := c.DistanceHSLuv(c2)
+ grayDist := c.DistanceHSLuv(g2)
+
+ if colorDist <= grayDist {
+ return ansi.ExtendedColor(16 + ci) //nolint:gosec
+ }
+ return ansi.ExtendedColor(232 + grayIdx) //nolint:gosec
+}
+
+func ansi256ToANSIColor(c ansi.ExtendedColor) ansi.BasicColor {
+ var r int
+ md := math.MaxFloat64
+
+ h, _ := colorful.Hex(ansiHex[c])
+ for i := 0; i <= 15; i++ {
+ hb, _ := colorful.Hex(ansiHex[i])
+ d := h.DistanceHSLuv(hb)
+
+ if d < md {
+ md = d
+ r = i
+ }
+ }
+
+ return ansi.BasicColor(r) //nolint:gosec
+}
+
+// RGB values of ANSI colors (0-255).
+var ansiHex = []string{
+ "#000000",
+ "#800000",
+ "#008000",
+ "#808000",
+ "#000080",
+ "#800080",
+ "#008080",
+ "#c0c0c0",
+ "#808080",
+ "#ff0000",
+ "#00ff00",
+ "#ffff00",
+ "#0000ff",
+ "#ff00ff",
+ "#00ffff",
+ "#ffffff",
+ "#000000",
+ "#00005f",
+ "#000087",
+ "#0000af",
+ "#0000d7",
+ "#0000ff",
+ "#005f00",
+ "#005f5f",
+ "#005f87",
+ "#005faf",
+ "#005fd7",
+ "#005fff",
+ "#008700",
+ "#00875f",
+ "#008787",
+ "#0087af",
+ "#0087d7",
+ "#0087ff",
+ "#00af00",
+ "#00af5f",
+ "#00af87",
+ "#00afaf",
+ "#00afd7",
+ "#00afff",
+ "#00d700",
+ "#00d75f",
+ "#00d787",
+ "#00d7af",
+ "#00d7d7",
+ "#00d7ff",
+ "#00ff00",
+ "#00ff5f",
+ "#00ff87",
+ "#00ffaf",
+ "#00ffd7",
+ "#00ffff",
+ "#5f0000",
+ "#5f005f",
+ "#5f0087",
+ "#5f00af",
+ "#5f00d7",
+ "#5f00ff",
+ "#5f5f00",
+ "#5f5f5f",
+ "#5f5f87",
+ "#5f5faf",
+ "#5f5fd7",
+ "#5f5fff",
+ "#5f8700",
+ "#5f875f",
+ "#5f8787",
+ "#5f87af",
+ "#5f87d7",
+ "#5f87ff",
+ "#5faf00",
+ "#5faf5f",
+ "#5faf87",
+ "#5fafaf",
+ "#5fafd7",
+ "#5fafff",
+ "#5fd700",
+ "#5fd75f",
+ "#5fd787",
+ "#5fd7af",
+ "#5fd7d7",
+ "#5fd7ff",
+ "#5fff00",
+ "#5fff5f",
+ "#5fff87",
+ "#5fffaf",
+ "#5fffd7",
+ "#5fffff",
+ "#870000",
+ "#87005f",
+ "#870087",
+ "#8700af",
+ "#8700d7",
+ "#8700ff",
+ "#875f00",
+ "#875f5f",
+ "#875f87",
+ "#875faf",
+ "#875fd7",
+ "#875fff",
+ "#878700",
+ "#87875f",
+ "#878787",
+ "#8787af",
+ "#8787d7",
+ "#8787ff",
+ "#87af00",
+ "#87af5f",
+ "#87af87",
+ "#87afaf",
+ "#87afd7",
+ "#87afff",
+ "#87d700",
+ "#87d75f",
+ "#87d787",
+ "#87d7af",
+ "#87d7d7",
+ "#87d7ff",
+ "#87ff00",
+ "#87ff5f",
+ "#87ff87",
+ "#87ffaf",
+ "#87ffd7",
+ "#87ffff",
+ "#af0000",
+ "#af005f",
+ "#af0087",
+ "#af00af",
+ "#af00d7",
+ "#af00ff",
+ "#af5f00",
+ "#af5f5f",
+ "#af5f87",
+ "#af5faf",
+ "#af5fd7",
+ "#af5fff",
+ "#af8700",
+ "#af875f",
+ "#af8787",
+ "#af87af",
+ "#af87d7",
+ "#af87ff",
+ "#afaf00",
+ "#afaf5f",
+ "#afaf87",
+ "#afafaf",
+ "#afafd7",
+ "#afafff",
+ "#afd700",
+ "#afd75f",
+ "#afd787",
+ "#afd7af",
+ "#afd7d7",
+ "#afd7ff",
+ "#afff00",
+ "#afff5f",
+ "#afff87",
+ "#afffaf",
+ "#afffd7",
+ "#afffff",
+ "#d70000",
+ "#d7005f",
+ "#d70087",
+ "#d700af",
+ "#d700d7",
+ "#d700ff",
+ "#d75f00",
+ "#d75f5f",
+ "#d75f87",
+ "#d75faf",
+ "#d75fd7",
+ "#d75fff",
+ "#d78700",
+ "#d7875f",
+ "#d78787",
+ "#d787af",
+ "#d787d7",
+ "#d787ff",
+ "#d7af00",
+ "#d7af5f",
+ "#d7af87",
+ "#d7afaf",
+ "#d7afd7",
+ "#d7afff",
+ "#d7d700",
+ "#d7d75f",
+ "#d7d787",
+ "#d7d7af",
+ "#d7d7d7",
+ "#d7d7ff",
+ "#d7ff00",
+ "#d7ff5f",
+ "#d7ff87",
+ "#d7ffaf",
+ "#d7ffd7",
+ "#d7ffff",
+ "#ff0000",
+ "#ff005f",
+ "#ff0087",
+ "#ff00af",
+ "#ff00d7",
+ "#ff00ff",
+ "#ff5f00",
+ "#ff5f5f",
+ "#ff5f87",
+ "#ff5faf",
+ "#ff5fd7",
+ "#ff5fff",
+ "#ff8700",
+ "#ff875f",
+ "#ff8787",
+ "#ff87af",
+ "#ff87d7",
+ "#ff87ff",
+ "#ffaf00",
+ "#ffaf5f",
+ "#ffaf87",
+ "#ffafaf",
+ "#ffafd7",
+ "#ffafff",
+ "#ffd700",
+ "#ffd75f",
+ "#ffd787",
+ "#ffd7af",
+ "#ffd7d7",
+ "#ffd7ff",
+ "#ffff00",
+ "#ffff5f",
+ "#ffff87",
+ "#ffffaf",
+ "#ffffd7",
+ "#ffffff",
+ "#080808",
+ "#121212",
+ "#1c1c1c",
+ "#262626",
+ "#303030",
+ "#3a3a3a",
+ "#444444",
+ "#4e4e4e",
+ "#585858",
+ "#626262",
+ "#6c6c6c",
+ "#767676",
+ "#808080",
+ "#8a8a8a",
+ "#949494",
+ "#9e9e9e",
+ "#a8a8a8",
+ "#b2b2b2",
+ "#bcbcbc",
+ "#c6c6c6",
+ "#d0d0d0",
+ "#dadada",
+ "#e4e4e4",
+ "#eeeeee",
+}
diff --git a/vendor/github.com/charmbracelet/colorprofile/writer.go b/vendor/github.com/charmbracelet/colorprofile/writer.go
new file mode 100644
index 0000000..d04b3b9
--- /dev/null
+++ b/vendor/github.com/charmbracelet/colorprofile/writer.go
@@ -0,0 +1,166 @@
+package colorprofile
+
+import (
+ "bytes"
+ "image/color"
+ "io"
+ "strconv"
+
+ "github.com/charmbracelet/x/ansi"
+)
+
+// NewWriter creates a new color profile writer that downgrades color sequences
+// based on the detected color profile.
+//
+// If environ is nil, it will use os.Environ() to get the environment variables.
+//
+// It queries the given writer to determine if it supports ANSI escape codes.
+// If it does, along with the given environment variables, it will determine
+// the appropriate color profile to use for color formatting.
+//
+// This respects the NO_COLOR, CLICOLOR, and CLICOLOR_FORCE environment variables.
+func NewWriter(w io.Writer, environ []string) *Writer {
+ return &Writer{
+ Forward: w,
+ Profile: Detect(w, environ),
+ }
+}
+
+// Writer represents a color profile writer that writes ANSI sequences to the
+// underlying writer.
+type Writer struct {
+ Forward io.Writer
+ Profile Profile
+}
+
+// Write writes the given text to the underlying writer.
+func (w *Writer) Write(p []byte) (int, error) {
+ switch w.Profile {
+ case TrueColor:
+ return w.Forward.Write(p)
+ case NoTTY:
+ return io.WriteString(w.Forward, ansi.Strip(string(p)))
+ default:
+ return w.downsample(p)
+ }
+}
+
+// downsample downgrades the given text to the appropriate color profile.
+func (w *Writer) downsample(p []byte) (int, error) {
+ var buf bytes.Buffer
+ var state byte
+
+ parser := ansi.GetParser()
+ defer ansi.PutParser(parser)
+
+ for len(p) > 0 {
+ parser.Reset()
+ seq, _, read, newState := ansi.DecodeSequence(p, state, parser)
+
+ switch {
+ case ansi.HasCsiPrefix(seq) && parser.Command() == 'm':
+ handleSgr(w, parser, &buf)
+ default:
+ // If we're not a style SGR sequence, just write the bytes.
+ if n, err := buf.Write(seq); err != nil {
+ return n, err
+ }
+ }
+
+ p = p[read:]
+ state = newState
+ }
+
+ return w.Forward.Write(buf.Bytes())
+}
+
+// WriteString writes the given text to the underlying writer.
+func (w *Writer) WriteString(s string) (n int, err error) {
+ return w.Write([]byte(s))
+}
+
+func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
+ var style ansi.Style
+ params := p.Params()
+ for i := 0; i < len(params); i++ {
+ param := params[i]
+
+ switch param := param.Param(0); param {
+ case 0:
+ // SGR default parameter is 0. We use an empty string to reduce the
+ // number of bytes written to the buffer.
+ style = append(style, "")
+ case 30, 31, 32, 33, 34, 35, 36, 37: // 8-bit foreground color
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.ForegroundColor(
+ w.Profile.Convert(ansi.BasicColor(param - 30))) //nolint:gosec
+ case 38: // 16 or 24-bit foreground color
+ var c color.Color
+ if n := ansi.ReadStyleColor(params[i:], &c); n > 0 {
+ i += n - 1
+ }
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.ForegroundColor(w.Profile.Convert(c))
+ case 39: // default foreground color
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.DefaultForegroundColor()
+ case 40, 41, 42, 43, 44, 45, 46, 47: // 8-bit background color
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.BackgroundColor(
+ w.Profile.Convert(ansi.BasicColor(param - 40))) //nolint:gosec
+ case 48: // 16 or 24-bit background color
+ var c color.Color
+ if n := ansi.ReadStyleColor(params[i:], &c); n > 0 {
+ i += n - 1
+ }
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.BackgroundColor(w.Profile.Convert(c))
+ case 49: // default background color
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.DefaultBackgroundColor()
+ case 58: // 16 or 24-bit underline color
+ var c color.Color
+ if n := ansi.ReadStyleColor(params[i:], &c); n > 0 {
+ i += n - 1
+ }
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.UnderlineColor(w.Profile.Convert(c))
+ case 59: // default underline color
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.DefaultUnderlineColor()
+ case 90, 91, 92, 93, 94, 95, 96, 97: // 8-bit bright foreground color
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.ForegroundColor(
+ w.Profile.Convert(ansi.BasicColor(param - 90 + 8))) //nolint:gosec
+ case 100, 101, 102, 103, 104, 105, 106, 107: // 8-bit bright background color
+ if w.Profile < ANSI {
+ continue
+ }
+ style = style.BackgroundColor(
+ w.Profile.Convert(ansi.BasicColor(param - 100 + 8))) //nolint:gosec
+ default:
+ // If this is not a color attribute, just append it to the style.
+ style = append(style, strconv.Itoa(param))
+ }
+ }
+
+ _, _ = buf.WriteString(style.String())
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/.gitignore b/vendor/github.com/charmbracelet/lipgloss/.gitignore
new file mode 100644
index 0000000..db48201
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/.gitignore
@@ -0,0 +1,2 @@
+ssh_example_ed25519*
+dist/
diff --git a/vendor/github.com/charmbracelet/lipgloss/.golangci.yml b/vendor/github.com/charmbracelet/lipgloss/.golangci.yml
new file mode 100644
index 0000000..90c5c08
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/.golangci.yml
@@ -0,0 +1,41 @@
+run:
+ tests: false
+
+issues:
+ include:
+ - EXC0001
+ - EXC0005
+ - EXC0011
+ - EXC0012
+ - EXC0013
+
+ max-issues-per-linter: 0
+ max-same-issues: 0
+
+linters:
+ enable:
+ - bodyclose
+ - exhaustive
+ - goconst
+ - godot
+ - godox
+ - gofumpt
+ - goimports
+ - gomoddirectives
+ - goprintffuncname
+ - gosec
+ - misspell
+ - nakedret
+ - nestif
+ - nilerr
+ - noctx
+ - nolintlint
+ - prealloc
+ - revive
+ - rowserrcheck
+ - sqlclosecheck
+ - tparallel
+ - unconvert
+ - unparam
+ - whitespace
+ - wrapcheck
diff --git a/vendor/github.com/charmbracelet/lipgloss/.goreleaser.yml b/vendor/github.com/charmbracelet/lipgloss/.goreleaser.yml
new file mode 100644
index 0000000..3353d02
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/.goreleaser.yml
@@ -0,0 +1,5 @@
+# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
+version: 2
+includes:
+ - from_url:
+ url: charmbracelet/meta/main/goreleaser-lib.yaml
diff --git a/vendor/github.com/charmbracelet/lipgloss/LICENSE b/vendor/github.com/charmbracelet/lipgloss/LICENSE
new file mode 100644
index 0000000..6f5b1fa
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021-2023 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.
diff --git a/vendor/github.com/charmbracelet/lipgloss/README.md b/vendor/github.com/charmbracelet/lipgloss/README.md
new file mode 100644
index 0000000..cee2371
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/README.md
@@ -0,0 +1,815 @@
+# Lip Gloss
+
+<p>
+ <a href="https://stuff.charm.sh/lipgloss/lipgloss-mascot-2k.png"><img width="340" alt="Lip Gloss title treatment" src="https://github.com/charmbracelet/lipgloss/assets/25087/147cadb1-4254-43ec-ae6b-8d6ca7b029a1"></a><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>
+ <a href="https://www.phorm.ai/query?projectId=a0e324b6-b706-4546-b951-6671ea60c13f"><img src="https://stuff.charm.sh/misc/phorm-badge.svg" alt="phorm.ai"></a>
+</p>
+
+Style definitions for nice terminal layouts. Built with TUIs in mind.
+
+![Lip Gloss example](https://github.com/user-attachments/assets/7950b1c1-e0e3-427e-8e7d-6f7f6ad17ca7)
+
+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 True Color, ANSI256, and ANSI color
+profiles.
+
+```go
+lipgloss.CompleteColor{TrueColor: "#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 double border to the top and left 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 assignment:
+
+```go
+style := lipgloss.NewStyle().Foreground(lipgloss.Color("219"))
+
+copiedStyle := style // this is a true copy
+
+wildStyle := style.Blink(true) // this is also true copy, with blink added
+
+```
+
+Since `Style` data structures contains only primitive types, assigning a style
+to another effectively creates a new copy of the style without mutating the
+original.
+
+## 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")
+```
+
+## Tabs
+
+The tab character (`\t`) is rendered differently in different terminals (often
+as 8 spaces, sometimes 4). Because of this inconsistency, Lip Gloss converts
+tabs to 4 spaces at render time. This behavior can be changed on a per-style
+basis, however:
+
+```go
+style := lipgloss.NewStyle() // tabs will render as 4 spaces, the default
+style = style.TabWidth(2) // render tabs as 2 spaces
+style = style.TabWidth(0) // remove tabs entirely
+style = style.TabWidth(lipgloss.NoTabConversion) // leave tabs intact
+```
+
+## 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].
+
+## Rendering Tables
+
+Lip Gloss ships with a table rendering sub-package.
+
+```go
+import "github.com/charmbracelet/lipgloss/table"
+```
+
+Define some rows of data.
+
+```go
+rows := [][]string{
+ {"Chinese", "您好", "你好"},
+ {"Japanese", "こんにちは", "やあ"},
+ {"Arabic", "أهلين", "أهلا"},
+ {"Russian", "Здравствуйте", "Привет"},
+ {"Spanish", "Hola", "¿Qué tal?"},
+}
+```
+
+Use the table package to style and render the table.
+
+```go
+var (
+ purple = lipgloss.Color("99")
+ gray = lipgloss.Color("245")
+ lightGray = lipgloss.Color("241")
+
+ headerStyle = lipgloss.NewStyle().Foreground(purple).Bold(true).Align(lipgloss.Center)
+ cellStyle = lipgloss.NewStyle().Padding(0, 1).Width(14)
+ oddRowStyle = cellStyle.Foreground(gray)
+ evenRowStyle = cellStyle.Foreground(lightGray)
+)
+
+t := table.New().
+ Border(lipgloss.NormalBorder()).
+ BorderStyle(lipgloss.NewStyle().Foreground(purple)).
+ StyleFunc(func(row, col int) lipgloss.Style {
+ switch {
+ case row == table.HeaderRow:
+ return headerStyle
+ case row%2 == 0:
+ return evenRowStyle
+ default:
+ return oddRowStyle
+ }
+ }).
+ Headers("LANGUAGE", "FORMAL", "INFORMAL").
+ Rows(rows...)
+
+// You can also add tables row-by-row
+t.Row("English", "You look absolutely fabulous.", "How's it going?")
+```
+
+Print the table.
+
+```go
+fmt.Println(t)
+```
+
+![Table Example](https://github.com/charmbracelet/lipgloss/assets/42545625/6e4b70c4-f494-45da-a467-bdd27df30d5d)
+
+> [!WARNING]
+> Table `Rows` need to be declared before `Offset` otherwise it does nothing.
+
+### Table Borders
+
+There are helpers to generate tables in markdown or ASCII style:
+
+#### Markdown Table
+
+```go
+table.New().Border(lipgloss.MarkdownBorder()).BorderTop(false).BorderBottom(false)
+```
+
+```
+| LANGUAGE | FORMAL | INFORMAL |
+|----------|--------------|-----------|
+| Chinese | Nǐn hǎo | Nǐ hǎo |
+| French | Bonjour | Salut |
+| Russian | Zdravstvuyte | Privet |
+| Spanish | Hola | ¿Qué tal? |
+```
+
+#### ASCII Table
+
+```go
+table.New().Border(lipgloss.ASCIIBorder())
+```
+
+```
++----------+--------------+-----------+
+| LANGUAGE | FORMAL | INFORMAL |
++----------+--------------+-----------+
+| Chinese | Nǐn hǎo | Nǐ hǎo |
+| French | Bonjour | Salut |
+| Russian | Zdravstvuyte | Privet |
+| Spanish | Hola | ¿Qué tal? |
++----------+--------------+-----------+
+```
+
+For more on tables see [the docs](https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc) and [examples](https://github.com/charmbracelet/lipgloss/tree/master/examples/table).
+
+## Rendering Lists
+
+Lip Gloss ships with a list rendering sub-package.
+
+```go
+import "github.com/charmbracelet/lipgloss/list"
+```
+
+Define a new list.
+
+```go
+l := list.New("A", "B", "C")
+```
+
+Print the list.
+
+```go
+fmt.Println(l)
+
+// • A
+// • B
+// • C
+```
+
+Lists have the ability to nest.
+
+```go
+l := list.New(
+ "A", list.New("Artichoke"),
+ "B", list.New("Baking Flour", "Bananas", "Barley", "Bean Sprouts"),
+ "C", list.New("Cashew Apple", "Cashews", "Coconut Milk", "Curry Paste", "Currywurst"),
+ "D", list.New("Dill", "Dragonfruit", "Dried Shrimp"),
+ "E", list.New("Eggs"),
+ "F", list.New("Fish Cake", "Furikake"),
+ "J", list.New("Jicama"),
+ "K", list.New("Kohlrabi"),
+ "L", list.New("Leeks", "Lentils", "Licorice Root"),
+)
+```
+
+Print the list.
+
+```go
+fmt.Println(l)
+```
+
+<p align="center">
+<img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/0dc9f440-0748-4151-a3b0-7dcf29dfcdb0">
+</p>
+
+Lists can be customized via their enumeration function as well as using
+`lipgloss.Style`s.
+
+```go
+enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
+itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1)
+
+l := list.New(
+ "Glossier",
+ "Claire’s Boutique",
+ "Nyx",
+ "Mac",
+ "Milk",
+ ).
+ Enumerator(list.Roman).
+ EnumeratorStyle(enumeratorStyle).
+ ItemStyle(itemStyle)
+```
+
+Print the list.
+
+<p align="center">
+<img width="600" alt="List example" src="https://github.com/charmbracelet/lipgloss/assets/42545625/360494f1-57fb-4e13-bc19-0006efe01561">
+</p>
+
+In addition to the predefined enumerators (`Arabic`, `Alphabet`, `Roman`, `Bullet`, `Tree`),
+you may also define your own custom enumerator:
+
+```go
+l := list.New("Duck", "Duck", "Duck", "Duck", "Goose", "Duck", "Duck")
+
+func DuckDuckGooseEnumerator(l list.Items, i int) string {
+ if l.At(i).Value() == "Goose" {
+ return "Honk →"
+ }
+ return ""
+}
+
+l = l.Enumerator(DuckDuckGooseEnumerator)
+```
+
+Print the list:
+
+<p align="center">
+<img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/157aaf30-140d-4948-9bb4-dfba46e5b87e">
+</p>
+
+If you need, you can also build lists incrementally:
+
+```go
+l := list.New()
+
+for i := 0; i < repeat; i++ {
+ l.Item("Lip Gloss")
+}
+```
+
+## Rendering Trees
+
+Lip Gloss ships with a tree rendering sub-package.
+
+```go
+import "github.com/charmbracelet/lipgloss/tree"
+```
+
+Define a new tree.
+
+```go
+t := tree.Root(".").
+ Child("A", "B", "C")
+```
+
+Print the tree.
+
+```go
+fmt.Println(t)
+
+// .
+// ├── A
+// ├── B
+// └── C
+```
+
+Trees have the ability to nest.
+
+```go
+t := tree.Root(".").
+ Child("macOS").
+ Child(
+ tree.New().
+ Root("Linux").
+ Child("NixOS").
+ Child("Arch Linux (btw)").
+ Child("Void Linux"),
+ ).
+ Child(
+ tree.New().
+ Root("BSD").
+ Child("FreeBSD").
+ Child("OpenBSD"),
+ )
+```
+
+Print the tree.
+
+```go
+fmt.Println(t)
+```
+
+<p align="center">
+<img width="663" alt="Tree Example (simple)" src="https://github.com/user-attachments/assets/5ef14eb8-a5d4-4f94-8834-e15d1e714f89">
+</p>
+
+Trees can be customized via their enumeration function as well as using
+`lipgloss.Style`s.
+
+```go
+enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("63")).MarginRight(1)
+rootStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("35"))
+itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212"))
+
+t := tree.
+ Root("⁜ Makeup").
+ Child(
+ "Glossier",
+ "Fenty Beauty",
+ tree.New().Child(
+ "Gloss Bomb Universal Lip Luminizer",
+ "Hot Cheeks Velour Blushlighter",
+ ),
+ "Nyx",
+ "Mac",
+ "Milk",
+ ).
+ Enumerator(tree.RoundedEnumerator).
+ EnumeratorStyle(enumeratorStyle).
+ RootStyle(rootStyle).
+ ItemStyle(itemStyle)
+```
+
+Print the tree.
+
+<p align="center">
+<img width="663" alt="Tree Example (makeup)" src="https://github.com/user-attachments/assets/06d12d87-744a-4c89-bd98-45de9094a97e">
+</p>
+
+The predefined enumerators for trees are `DefaultEnumerator` and `RoundedEnumerator`.
+
+If you need, you can also build trees incrementally:
+
+```go
+t := tree.New()
+
+for i := 0; i < repeat; i++ {
+ t.Child("Lip Gloss")
+}
+```
+
+---
+
+## FAQ
+
+<details>
+<summary>
+Why are things misaligning? Why are borders at the wrong widths?
+</summary>
+<p>This is most likely due to your locale and encoding, particularly with
+regard to Chinese, Japanese, and Korean (for example, <code>zh_CN.UTF-8</code>
+or <code>ja_JP.UTF-8</code>). The most direct way to fix this is to set
+<code>RUNEWIDTH_EASTASIAN=0</code> in your environment.</p>
+
+<p>For details see <a href="https://github.com/charmbracelet/lipgloss/issues/40">https://github.com/charmbracelet/lipgloss/issues/40.</a></p>
+</details>
+
+<details>
+<summary>
+Why isn't Lip Gloss displaying colors?
+</summary>
+<p>Lip Gloss automatically degrades colors to the best available option in the
+given terminal, and if output's not a TTY it will remove color output entirely.
+This is common when running tests, CI, or when piping output elsewhere.</p>
+
+<p>If necessary, you can force a color profile in your tests with
+<a href="https://pkg.go.dev/github.com/charmbracelet/lipgloss#SetColorProfile"><code>SetColorProfile</code></a>.</p>
+
+```go
+import (
+ "github.com/charmbracelet/lipgloss"
+ "github.com/muesli/termenv"
+)
+
+lipgloss.SetColorProfile(termenv.TrueColor)
+```
+
+_Note:_ this option limits the flexibility of your application and can cause
+ANSI escape codes to be output in cases where that might not be desired. Take
+careful note of your use case and environment before choosing to force a color
+profile.
+
+</details>
+
+## 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
+
+## Contributing
+
+See [contributing][contribute].
+
+[contribute]: https://github.com/charmbracelet/lipgloss/contribute
+
+## 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
diff --git a/vendor/github.com/charmbracelet/lipgloss/Taskfile.yaml b/vendor/github.com/charmbracelet/lipgloss/Taskfile.yaml
new file mode 100644
index 0000000..0b4a771
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/Taskfile.yaml
@@ -0,0 +1,19 @@
+# https://taskfile.dev
+
+version: '3'
+
+tasks:
+ lint:
+ desc: Run base linters
+ cmds:
+ - golangci-lint run
+
+ test:
+ desc: Run tests
+ cmds:
+ - go test ./... {{.CLI_ARGS}}
+
+ test:table:
+ desc: Run table tests
+ cmds:
+ - go test ./table {{.CLI_ARGS}}
diff --git a/vendor/github.com/charmbracelet/lipgloss/align.go b/vendor/github.com/charmbracelet/lipgloss/align.go
new file mode 100644
index 0000000..ce654b2
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/align.go
@@ -0,0 +1,83 @@
+package lipgloss
+
+import (
+ "strings"
+
+ "github.com/charmbracelet/x/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.StringWidth(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 { //nolint:exhaustive
+ case Right:
+ s := strings.Repeat(" ", shortAmount)
+ if style != nil {
+ s = style.Styled(s)
+ }
+ l = s + l
+ case Center:
+ // Note: remainder goes on the right.
+ left := shortAmount / 2 //nolint:mnd
+ right := left + shortAmount%2 //nolint:mnd
+
+ 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:
+ topPadding, bottomPadding := (height-strHeight)/2, (height-strHeight)/2 //nolint:mnd
+ 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
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/ansi_unix.go b/vendor/github.com/charmbracelet/lipgloss/ansi_unix.go
new file mode 100644
index 0000000..d416b8c
--- /dev/null
+++ b/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() {}
diff --git a/vendor/github.com/charmbracelet/lipgloss/ansi_windows.go b/vendor/github.com/charmbracelet/lipgloss/ansi_windows.go
new file mode 100644
index 0000000..0cf56e4
--- /dev/null
+++ b/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()
+ })
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/borders.go b/vendor/github.com/charmbracelet/lipgloss/borders.go
new file mode 100644
index 0000000..b36f874
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/borders.go
@@ -0,0 +1,490 @@
+package lipgloss
+
+import (
+ "strings"
+
+ "github.com/charmbracelet/x/ansi"
+ "github.com/muesli/termenv"
+ "github.com/rivo/uniseg"
+)
+
+// 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
+ BottomLeft string
+ BottomRight string
+ MiddleLeft string
+ MiddleRight string
+ Middle string
+ MiddleTop string
+ MiddleBottom 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.Right, 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.BottomLeft)
+}
+
+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: "┘",
+ MiddleLeft: "├",
+ MiddleRight: "┤",
+ Middle: "┼",
+ MiddleTop: "┬",
+ MiddleBottom: "┴",
+ }
+
+ roundedBorder = Border{
+ Top: "─",
+ Bottom: "─",
+ Left: "│",
+ Right: "│",
+ TopLeft: "╭",
+ TopRight: "╮",
+ BottomLeft: "╰",
+ BottomRight: "╯",
+ MiddleLeft: "├",
+ MiddleRight: "┤",
+ Middle: "┼",
+ MiddleTop: "┬",
+ MiddleBottom: "┴",
+ }
+
+ blockBorder = Border{
+ Top: "█",
+ Bottom: "█",
+ Left: "█",
+ Right: "█",
+ TopLeft: "█",
+ TopRight: "█",
+ BottomLeft: "█",
+ BottomRight: "█",
+ MiddleLeft: "█",
+ MiddleRight: "█",
+ Middle: "█",
+ MiddleTop: "█",
+ MiddleBottom: "█",
+ }
+
+ 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: "┛",
+ MiddleLeft: "┣",
+ MiddleRight: "┫",
+ Middle: "╋",
+ MiddleTop: "┳",
+ MiddleBottom: "┻",
+ }
+
+ doubleBorder = Border{
+ Top: "═",
+ Bottom: "═",
+ Left: "║",
+ Right: "║",
+ TopLeft: "╔",
+ TopRight: "╗",
+ BottomLeft: "╚",
+ BottomRight: "╝",
+ MiddleLeft: "╠",
+ MiddleRight: "╣",
+ Middle: "╬",
+ MiddleTop: "╦",
+ MiddleBottom: "╩",
+ }
+
+ hiddenBorder = Border{
+ Top: " ",
+ Bottom: " ",
+ Left: " ",
+ Right: " ",
+ TopLeft: " ",
+ TopRight: " ",
+ BottomLeft: " ",
+ BottomRight: " ",
+ MiddleLeft: " ",
+ MiddleRight: " ",
+ Middle: " ",
+ MiddleTop: " ",
+ MiddleBottom: " ",
+ }
+
+ markdownBorder = Border{
+ Top: "-",
+ Bottom: "-",
+ Left: "|",
+ Right: "|",
+ TopLeft: "|",
+ TopRight: "|",
+ BottomLeft: "|",
+ BottomRight: "|",
+ MiddleLeft: "|",
+ MiddleRight: "|",
+ Middle: "|",
+ MiddleTop: "|",
+ MiddleBottom: "|",
+ }
+
+ asciiBorder = Border{
+ Top: "-",
+ Bottom: "-",
+ Left: "|",
+ Right: "|",
+ TopLeft: "+",
+ TopRight: "+",
+ BottomLeft: "+",
+ BottomRight: "+",
+ MiddleLeft: "+",
+ MiddleRight: "+",
+ Middle: "+",
+ MiddleTop: "+",
+ MiddleBottom: "+",
+ }
+)
+
+// 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
+}
+
+// MarkdownBorder return a table border in markdown style.
+//
+// Make sure to disable top and bottom border for the best result. This will
+// ensure that the output is valid markdown.
+//
+// table.New().Border(lipgloss.MarkdownBorder()).BorderTop(false).BorderBottom(false)
+func MarkdownBorder() Border {
+ return markdownBorder
+}
+
+// ASCIIBorder returns a table border with ASCII characters.
+func ASCIIBorder() Border {
+ return asciiBorder
+}
+
+func (s Style) applyBorder(str string) string {
+ var (
+ 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 s.implicitBorders() {
+ 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 middle == "" {
+ middle = " "
+ }
+
+ leftWidth := ansi.StringWidth(left)
+ rightWidth := ansi.StringWidth(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.StringWidth(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
+ }
+
+ 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) int {
+ var width int
+
+ state := -1
+ for len(str) > 0 {
+ var w int
+ _, str, w, state = uniseg.FirstGraphemeClusterInString(str, state)
+ if w > width {
+ width = w
+ }
+ }
+
+ return width
+}
+
+func getFirstRuneAsString(str string) string {
+ if str == "" {
+ return str
+ }
+ r := []rune(str)
+ return string(r[0])
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/color.go b/vendor/github.com/charmbracelet/lipgloss/color.go
new file mode 100644
index 0000000..6caf3a3
--- /dev/null
+++ b/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.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 //nolint:mnd
+}
+
+// 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 { //nolint:exhaustive
+ 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()
+}
+
+// CompleteAdaptiveColor 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()
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/get.go b/vendor/github.com/charmbracelet/lipgloss/get.go
new file mode 100644
index 0000000..422b4ce
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/get.go
@@ -0,0 +1,556 @@
+package lipgloss
+
+import (
+ "strings"
+
+ "github.com/charmbracelet/x/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 background 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 margins. 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 left 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 left 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) && !s.implicitBorders() {
+ 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) && !s.implicitBorders() {
+ 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) && !s.implicitBorders() {
+ 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) && !s.implicitBorders() {
+ return 0
+ }
+ return s.getBorderStyle().GetRightSize()
+}
+
+// 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 {
+ return s.GetBorderLeftSize() + s.GetBorderRightSize()
+}
+
+// GetVerticalBorderSize returns the width of the vertical borders. If
+// borders contain runes of varying widths, the widest rune is returned. If no
+// border exists on the vertical edges, 0 is returned.
+func (s Style) GetVerticalBorderSize() int {
+ return s.GetBorderTopSize() + s.GetBorderBottomSize()
+}
+
+// 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 height setting. If no value is set 0 is
+// returned.
+func (s Style) GetMaxHeight() int {
+ return s.getAsInt(maxHeightKey)
+}
+
+// GetTabWidth returns the style's tab width setting. If no value is set 4 is
+// returned which is the implicit default.
+func (s Style) GetTabWidth() int {
+ return s.getAsInt(tabWidthKey)
+}
+
+// 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 strikethrough
+// 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 vertical 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()
+}
+
+// GetTransform returns the transform set on the style. If no transform is set
+// nil is returned.
+func (s Style) GetTransform() func(string) string {
+ return s.getAsTransform(transformKey)
+}
+
+// Returns whether or not the given property is set.
+func (s Style) isSet(k propKey) bool {
+ return s.props.has(k)
+}
+
+func (s Style) getAsBool(k propKey, defaultVal bool) bool {
+ if !s.isSet(k) {
+ return defaultVal
+ }
+ return s.attrs&int(k) != 0
+}
+
+func (s Style) getAsColor(k propKey) TerminalColor {
+ if !s.isSet(k) {
+ return noColor
+ }
+
+ var c TerminalColor
+ switch k { //nolint:exhaustive
+ case foregroundKey:
+ c = s.fgColor
+ case backgroundKey:
+ c = s.bgColor
+ case marginBackgroundKey:
+ c = s.marginBgColor
+ case borderTopForegroundKey:
+ c = s.borderTopFgColor
+ case borderRightForegroundKey:
+ c = s.borderRightFgColor
+ case borderBottomForegroundKey:
+ c = s.borderBottomFgColor
+ case borderLeftForegroundKey:
+ c = s.borderLeftFgColor
+ case borderTopBackgroundKey:
+ c = s.borderTopBgColor
+ case borderRightBackgroundKey:
+ c = s.borderRightBgColor
+ case borderBottomBackgroundKey:
+ c = s.borderBottomBgColor
+ case borderLeftBackgroundKey:
+ c = s.borderLeftBgColor
+ }
+
+ if c != nil {
+ return c
+ }
+
+ return noColor
+}
+
+func (s Style) getAsInt(k propKey) int {
+ if !s.isSet(k) {
+ return 0
+ }
+ switch k { //nolint:exhaustive
+ case widthKey:
+ return s.width
+ case heightKey:
+ return s.height
+ case paddingTopKey:
+ return s.paddingTop
+ case paddingRightKey:
+ return s.paddingRight
+ case paddingBottomKey:
+ return s.paddingBottom
+ case paddingLeftKey:
+ return s.paddingLeft
+ case marginTopKey:
+ return s.marginTop
+ case marginRightKey:
+ return s.marginRight
+ case marginBottomKey:
+ return s.marginBottom
+ case marginLeftKey:
+ return s.marginLeft
+ case maxWidthKey:
+ return s.maxWidth
+ case maxHeightKey:
+ return s.maxHeight
+ case tabWidthKey:
+ return s.tabWidth
+ }
+ return 0
+}
+
+func (s Style) getAsPosition(k propKey) Position {
+ if !s.isSet(k) {
+ return Position(0)
+ }
+ switch k { //nolint:exhaustive
+ case alignHorizontalKey:
+ return s.alignHorizontal
+ case alignVerticalKey:
+ return s.alignVertical
+ }
+ return Position(0)
+}
+
+func (s Style) getBorderStyle() Border {
+ if !s.isSet(borderStyleKey) {
+ return noBorder
+ }
+ return s.borderStyle
+}
+
+// Returns whether or not the style has implicit borders. This happens when
+// a border style has been set but no border sides have been explicitly turned
+// on or off.
+func (s Style) implicitBorders() bool {
+ var (
+ borderStyle = s.getBorderStyle()
+ topSet = s.isSet(borderTopKey)
+ rightSet = s.isSet(borderRightKey)
+ bottomSet = s.isSet(borderBottomKey)
+ leftSet = s.isSet(borderLeftKey)
+ )
+ return borderStyle != noBorder && !(topSet || rightSet || bottomSet || leftSet)
+}
+
+func (s Style) getAsTransform(propKey) func(string) string {
+ if !s.isSet(transformKey) {
+ return nil
+ }
+ return s.transform
+}
+
+// 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.StringWidth(l)
+ if widest < w {
+ widest = w
+ }
+ }
+
+ return lines, widest
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/join.go b/vendor/github.com/charmbracelet/lipgloss/join.go
new file mode 100644
index 0000000..b0a23a5
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/join.go
@@ -0,0 +1,175 @@
+package lipgloss
+
+import (
+ "math"
+ "strings"
+
+ "github.com/charmbracelet/x/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 top, center or bottom 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 { //nolint:exhaustive
+ 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.StringWidth(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.StringWidth(line)
+
+ switch pos { //nolint:exhaustive
+ 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()
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/position.go b/vendor/github.com/charmbracelet/lipgloss/position.go
new file mode 100644
index 0000000..185f5af
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/position.go
@@ -0,0 +1,154 @@
+package lipgloss
+
+import (
+ "math"
+ "strings"
+
+ "github.com/charmbracelet/x/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 its 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.StringWidth(l))
+
+ switch pos { //nolint:exhaustive
+ 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 its 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 { //nolint:exhaustive
+ 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()
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/ranges.go b/vendor/github.com/charmbracelet/lipgloss/ranges.go
new file mode 100644
index 0000000..d171699
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/ranges.go
@@ -0,0 +1,48 @@
+package lipgloss
+
+import (
+ "strings"
+
+ "github.com/charmbracelet/x/ansi"
+)
+
+// StyleRanges allows to, given a string, style ranges of it differently.
+// The function will take into account existing styles.
+// Ranges should not overlap.
+func StyleRanges(s string, ranges ...Range) string {
+ if len(ranges) == 0 {
+ return s
+ }
+
+ var buf strings.Builder
+ lastIdx := 0
+ stripped := ansi.Strip(s)
+
+ // Use Truncate and TruncateLeft to style match.MatchedIndexes without
+ // losing the original option style:
+ for _, rng := range ranges {
+ // Add the text before this match
+ if rng.Start > lastIdx {
+ buf.WriteString(ansi.Cut(s, lastIdx, rng.Start))
+ }
+ // Add the matched range with its highlight
+ buf.WriteString(rng.Style.Render(ansi.Cut(stripped, rng.Start, rng.End)))
+ lastIdx = rng.End
+ }
+
+ // Add any remaining text after the last match
+ buf.WriteString(ansi.TruncateLeft(s, lastIdx, ""))
+
+ return buf.String()
+}
+
+// NewRange returns a range that can be used with [StyleRanges].
+func NewRange(start, end int, style Style) Range {
+ return Range{start, end, style}
+}
+
+// Range to be used with [StyleRanges].
+type Range struct {
+ Start, End int
+ Style Style
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/renderer.go b/vendor/github.com/charmbracelet/lipgloss/renderer.go
new file mode 100644
index 0000000..233aa7c
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/renderer.go
@@ -0,0 +1,181 @@
+package lipgloss
+
+import (
+ "io"
+ "sync"
+
+ "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
+ colorProfile termenv.Profile
+ hasDarkBackground bool
+
+ getColorProfile sync.Once
+ explicitColorProfile bool
+
+ getBackgroundColor sync.Once
+ explicitBackgroundColor bool
+
+ mtx sync.RWMutex
+}
+
+// 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 {
+ r.mtx.RLock()
+ defer r.mtx.RUnlock()
+ return r.output
+}
+
+// SetOutput sets the termenv output.
+func (r *Renderer) SetOutput(o *termenv.Output) {
+ r.mtx.Lock()
+ defer r.mtx.Unlock()
+ r.output = o
+}
+
+// ColorProfile returns the detected termenv color profile.
+func (r *Renderer) ColorProfile() termenv.Profile {
+ r.mtx.RLock()
+ defer r.mtx.RUnlock()
+
+ if !r.explicitColorProfile {
+ r.getColorProfile.Do(func() {
+ // NOTE: we don't need to lock here because sync.Once provides its
+ // own locking mechanism.
+ r.colorProfile = r.output.EnvColorProfile()
+ })
+ }
+
+ return r.colorProfile
+}
+
+// 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.mtx.Lock()
+ defer r.mtx.Unlock()
+
+ r.colorProfile = p
+ r.explicitColorProfile = true
+}
+
+// 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 {
+ r.mtx.RLock()
+ defer r.mtx.RUnlock()
+
+ if !r.explicitBackgroundColor {
+ r.getBackgroundColor.Do(func() {
+ // NOTE: we don't need to lock here because sync.Once provides its
+ // own locking mechanism.
+ r.hasDarkBackground = r.output.HasDarkBackground()
+ })
+ }
+
+ return r.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.mtx.Lock()
+ defer r.mtx.Unlock()
+
+ r.hasDarkBackground = b
+ r.explicitBackgroundColor = true
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/runes.go b/vendor/github.com/charmbracelet/lipgloss/runes.go
new file mode 100644
index 0000000..7a49e32
--- /dev/null
+++ b/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()
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/set.go b/vendor/github.com/charmbracelet/lipgloss/set.go
new file mode 100644
index 0000000..fde38fa
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/set.go
@@ -0,0 +1,799 @@
+package lipgloss
+
+// Set a value on the underlying rules map.
+func (s *Style) set(key propKey, value interface{}) {
+ // We don't allow negative integers on any of our other 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.
+ switch key { //nolint:exhaustive
+ case foregroundKey:
+ s.fgColor = colorOrNil(value)
+ case backgroundKey:
+ s.bgColor = colorOrNil(value)
+ case widthKey:
+ s.width = max(0, value.(int))
+ case heightKey:
+ s.height = max(0, value.(int))
+ case alignHorizontalKey:
+ s.alignHorizontal = value.(Position)
+ case alignVerticalKey:
+ s.alignVertical = value.(Position)
+ case paddingTopKey:
+ s.paddingTop = max(0, value.(int))
+ case paddingRightKey:
+ s.paddingRight = max(0, value.(int))
+ case paddingBottomKey:
+ s.paddingBottom = max(0, value.(int))
+ case paddingLeftKey:
+ s.paddingLeft = max(0, value.(int))
+ case marginTopKey:
+ s.marginTop = max(0, value.(int))
+ case marginRightKey:
+ s.marginRight = max(0, value.(int))
+ case marginBottomKey:
+ s.marginBottom = max(0, value.(int))
+ case marginLeftKey:
+ s.marginLeft = max(0, value.(int))
+ case marginBackgroundKey:
+ s.marginBgColor = colorOrNil(value)
+ case borderStyleKey:
+ s.borderStyle = value.(Border)
+ case borderTopForegroundKey:
+ s.borderTopFgColor = colorOrNil(value)
+ case borderRightForegroundKey:
+ s.borderRightFgColor = colorOrNil(value)
+ case borderBottomForegroundKey:
+ s.borderBottomFgColor = colorOrNil(value)
+ case borderLeftForegroundKey:
+ s.borderLeftFgColor = colorOrNil(value)
+ case borderTopBackgroundKey:
+ s.borderTopBgColor = colorOrNil(value)
+ case borderRightBackgroundKey:
+ s.borderRightBgColor = colorOrNil(value)
+ case borderBottomBackgroundKey:
+ s.borderBottomBgColor = colorOrNil(value)
+ case borderLeftBackgroundKey:
+ s.borderLeftBgColor = colorOrNil(value)
+ case maxWidthKey:
+ s.maxWidth = max(0, value.(int))
+ case maxHeightKey:
+ s.maxHeight = max(0, value.(int))
+ case tabWidthKey:
+ // TabWidth is the only property that may have a negative value (and
+ // that negative value can be no less than -1).
+ s.tabWidth = value.(int)
+ case transformKey:
+ s.transform = value.(func(string) string)
+ default:
+ if v, ok := value.(bool); ok { //nolint:nestif
+ if v {
+ s.attrs |= int(key)
+ } else {
+ s.attrs &^= int(key)
+ }
+ } else if attrs, ok := value.(int); ok {
+ // bool attrs
+ if attrs&int(key) != 0 {
+ s.attrs |= int(key)
+ } else {
+ s.attrs &^= int(key)
+ }
+ }
+ }
+
+ // Set the prop on
+ s.props = s.props.set(key)
+}
+
+// setFrom sets the property from another style.
+func (s *Style) setFrom(key propKey, i Style) {
+ switch key { //nolint:exhaustive
+ case foregroundKey:
+ s.set(foregroundKey, i.fgColor)
+ case backgroundKey:
+ s.set(backgroundKey, i.bgColor)
+ case widthKey:
+ s.set(widthKey, i.width)
+ case heightKey:
+ s.set(heightKey, i.height)
+ case alignHorizontalKey:
+ s.set(alignHorizontalKey, i.alignHorizontal)
+ case alignVerticalKey:
+ s.set(alignVerticalKey, i.alignVertical)
+ case paddingTopKey:
+ s.set(paddingTopKey, i.paddingTop)
+ case paddingRightKey:
+ s.set(paddingRightKey, i.paddingRight)
+ case paddingBottomKey:
+ s.set(paddingBottomKey, i.paddingBottom)
+ case paddingLeftKey:
+ s.set(paddingLeftKey, i.paddingLeft)
+ case marginTopKey:
+ s.set(marginTopKey, i.marginTop)
+ case marginRightKey:
+ s.set(marginRightKey, i.marginRight)
+ case marginBottomKey:
+ s.set(marginBottomKey, i.marginBottom)
+ case marginLeftKey:
+ s.set(marginLeftKey, i.marginLeft)
+ case marginBackgroundKey:
+ s.set(marginBackgroundKey, i.marginBgColor)
+ case borderStyleKey:
+ s.set(borderStyleKey, i.borderStyle)
+ case borderTopForegroundKey:
+ s.set(borderTopForegroundKey, i.borderTopFgColor)
+ case borderRightForegroundKey:
+ s.set(borderRightForegroundKey, i.borderRightFgColor)
+ case borderBottomForegroundKey:
+ s.set(borderBottomForegroundKey, i.borderBottomFgColor)
+ case borderLeftForegroundKey:
+ s.set(borderLeftForegroundKey, i.borderLeftFgColor)
+ case borderTopBackgroundKey:
+ s.set(borderTopBackgroundKey, i.borderTopBgColor)
+ case borderRightBackgroundKey:
+ s.set(borderRightBackgroundKey, i.borderRightBgColor)
+ case borderBottomBackgroundKey:
+ s.set(borderBottomBackgroundKey, i.borderBottomBgColor)
+ case borderLeftBackgroundKey:
+ s.set(borderLeftBackgroundKey, i.borderLeftBgColor)
+ case maxWidthKey:
+ s.set(maxWidthKey, i.maxWidth)
+ case maxHeightKey:
+ s.set(maxHeightKey, i.maxHeight)
+ case tabWidthKey:
+ s.set(tabWidthKey, i.tabWidth)
+ case transformKey:
+ s.set(transformKey, i.transform)
+ default:
+ // Set attributes for set bool properties
+ s.set(key, i.attrs)
+ }
+}
+
+func colorOrNil(c interface{}) TerminalColor {
+ if c, ok := c.(TerminalColor); ok {
+ return c
+ }
+ return nil
+}
+
+// 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 horizontal and vertical
+// 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 vertical 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.
+//
+// Deprecated: Just use margins and padding.
+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 returns a copy.
+func (s Style) MaxHeight(n int) Style {
+ o := s // copy
+ o.set(maxHeightKey, n)
+ return o
+}
+
+// NoTabConversion can be passed to [Style.TabWidth] to disable the replacement
+// of tabs with spaces at render time.
+const NoTabConversion = -1
+
+// TabWidth sets the number of spaces that a tab (/t) should be rendered as.
+// When set to 0, tabs will be removed. To disable the replacement of tabs with
+// spaces entirely, set this to [NoTabConversion].
+//
+// By default, tabs will be replaced with 4 spaces.
+func (s Style) TabWidth(n int) Style {
+ if n <= -1 {
+ n = -1
+ }
+ s.set(tabWidthKey, n)
+ return s
+}
+
+// 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
+}
+
+// Transform applies a given function to a string at render time, allowing for
+// the string being rendered to be manipuated.
+//
+// Example:
+//
+// s := NewStyle().Transform(strings.ToUpper)
+// fmt.Println(s.Render("raow!") // "RAOW!"
+func (s Style) Transform(fn func(string) string) Style {
+ s.set(transformKey, fn)
+ 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: //nolint:mnd
+ top = i[0]
+ bottom = i[0]
+ left = i[1]
+ right = i[1]
+ ok = true
+ case 3: //nolint:mnd
+ top = i[0]
+ left = i[1]
+ right = i[1]
+ bottom = i[2]
+ ok = true
+ case 4: //nolint:mnd
+ 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: //nolint:mnd
+ top = i[0]
+ bottom = i[0]
+ left = i[1]
+ right = i[1]
+ ok = true
+ case 3: //nolint:mnd
+ top = i[0]
+ left = i[1]
+ right = i[1]
+ bottom = i[2]
+ ok = true
+ case 4: //nolint:mnd
+ 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: //nolint:mnd
+ top = i[0]
+ bottom = i[0]
+ left = i[1]
+ right = i[1]
+ ok = true
+ case 3: //nolint:mnd
+ top = i[0]
+ left = i[1]
+ right = i[1]
+ bottom = i[2]
+ ok = true
+ case 4: //nolint:mnd
+ top = i[0]
+ right = i[1]
+ bottom = i[2]
+ left = i[3]
+ ok = true
+ }
+ return top, right, bottom, left, ok
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/size.go b/vendor/github.com/charmbracelet/lipgloss/size.go
new file mode 100644
index 0000000..e169ff5
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/size.go
@@ -0,0 +1,41 @@
+package lipgloss
+
+import (
+ "strings"
+
+ "github.com/charmbracelet/x/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.StringWidth(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
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/style.go b/vendor/github.com/charmbracelet/lipgloss/style.go
new file mode 100644
index 0000000..59fa3ab
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/style.go
@@ -0,0 +1,588 @@
+package lipgloss
+
+import (
+ "strings"
+ "unicode"
+
+ "github.com/charmbracelet/x/ansi"
+ "github.com/charmbracelet/x/cellbuf"
+ "github.com/muesli/termenv"
+)
+
+const tabWidthDefault = 4
+
+// Property for a key.
+type propKey int64
+
+// Available properties.
+const (
+ // Boolean props come first.
+ boldKey propKey = 1 << iota
+ italicKey
+ underlineKey
+ strikethroughKey
+ reverseKey
+ blinkKey
+ faintKey
+ underlineSpacesKey
+ strikethroughSpacesKey
+ colorWhitespaceKey
+
+ // Non-boolean props.
+ foregroundKey
+ backgroundKey
+ widthKey
+ heightKey
+ alignHorizontalKey
+ alignVerticalKey
+
+ // Padding.
+ paddingTopKey
+ paddingRightKey
+ paddingBottomKey
+ paddingLeftKey
+
+ // 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
+ tabWidthKey
+
+ transformKey
+)
+
+// props is a set of properties.
+type props int64
+
+// set sets a property.
+func (p props) set(k propKey) props {
+ return p | props(k)
+}
+
+// unset unsets a property.
+func (p props) unset(k propKey) props {
+ return p &^ props(k)
+}
+
+// has checks if a property is set.
+func (p props) has(k propKey) bool {
+ return p&props(k) != 0
+}
+
+// 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
+ props props
+ value string
+
+ // we store bool props values here
+ attrs int
+
+ // props that have values
+ fgColor TerminalColor
+ bgColor TerminalColor
+
+ width int
+ height int
+
+ alignHorizontal Position
+ alignVertical Position
+
+ paddingTop int
+ paddingRight int
+ paddingBottom int
+ paddingLeft int
+
+ marginTop int
+ marginRight int
+ marginBottom int
+ marginLeft int
+ marginBgColor TerminalColor
+
+ borderStyle Border
+ borderTopFgColor TerminalColor
+ borderRightFgColor TerminalColor
+ borderBottomFgColor TerminalColor
+ borderLeftFgColor TerminalColor
+ borderTopBgColor TerminalColor
+ borderRightBgColor TerminalColor
+ borderBottomBgColor TerminalColor
+ borderLeftBgColor TerminalColor
+
+ maxWidth int
+ maxHeight int
+ tabWidth int
+
+ transform func(string) 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.
+//
+// Deprecated: to copy just use assignment (i.e. a := b). All methods also
+// return a new style.
+func (s Style) Copy() Style {
+ return s
+}
+
+// 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 {
+ for k := boldKey; k <= transformKey; k <<= 1 {
+ if !i.isSet(k) {
+ continue
+ }
+
+ switch k { //nolint:exhaustive
+ 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.set(marginBackgroundKey, i.bgColor)
+ }
+ }
+
+ if s.isSet(k) {
+ continue
+ }
+
+ s.setFrom(k, i)
+ }
+ 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...)
+
+ p = s.r.ColorProfile()
+ te = p.String()
+ teSpace = p.String()
+ teWhitespace = p.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 = s.getAsBool(underlineSpacesKey, false) || (underline && s.getAsBool(underlineSpacesKey, true))
+ strikethroughSpaces = s.getAsBool(strikethroughSpacesKey, false) || (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 = (underline && !underlineSpaces) || (strikethrough && !strikethroughSpaces) || underlineSpaces || strikethroughSpaces
+
+ transform = s.getAsTransform(transformKey)
+ )
+
+ if transform != nil {
+ str = transform(str)
+ }
+
+ if s.props == 0 {
+ return s.maybeConvertTabs(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 {
+ 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()
+ }
+
+ // Potentially convert tabs to spaces
+ str = s.maybeConvertTabs(str)
+ // carriage returns can cause strange behaviour when rendering.
+ str = strings.ReplaceAll(str, "\r\n", "\n")
+
+ // Strip newlines in single line mode
+ if inline {
+ str = strings.ReplaceAll(str, "\n", "")
+ }
+
+ // Word wrap
+ if !inline && width > 0 {
+ wrapAt := width - leftPadding - rightPadding
+ str = cellbuf.Wrap(str, wrapAt, "")
+ }
+
+ // 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 { //nolint:nestif
+ 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] = ansi.Truncate(lines[i], maxWidth, "")
+ }
+
+ str = strings.Join(lines, "\n")
+ }
+
+ // Truncate according to MaxHeight
+ if maxHeight > 0 {
+ lines := strings.Split(str, "\n")
+ height := min(maxHeight, len(lines))
+ if len(lines) > 0 {
+ str = strings.Join(lines[:height], "\n")
+ }
+ }
+
+ return str
+}
+
+func (s Style) maybeConvertTabs(str string) string {
+ tw := tabWidthDefault
+ if s.isSet(tabWidthKey) {
+ tw = s.getAsInt(tabWidthKey)
+ }
+ switch tw {
+ case -1:
+ return str
+ case 0:
+ return strings.ReplaceAll(str, "\t", "")
+ default:
+ return strings.ReplaceAll(str, "\t", strings.Repeat(" ", tw))
+ }
+}
+
+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 {
+ return pad(str, -n, style)
+}
+
+// Apply right padding.
+func padRight(str string, n int, style *termenv.Style) string {
+ return pad(str, n, style)
+}
+
+// pad adds padding to either the left or right side of a string.
+// Positive values add to the right side while negative values
+// add to the left side.
+func pad(str string, n int, style *termenv.Style) string {
+ if n == 0 {
+ return str
+ }
+
+ sp := strings.Repeat(" ", abs(n))
+ if style != nil {
+ sp = style.Styled(sp)
+ }
+
+ b := strings.Builder{}
+ l := strings.Split(str, "\n")
+
+ for i := range l {
+ switch {
+ // pad right
+ case n > 0:
+ b.WriteString(l[i])
+ b.WriteString(sp)
+ // pad left
+ default:
+ b.WriteString(sp)
+ b.WriteString(l[i])
+ }
+
+ if i != len(l)-1 {
+ b.WriteRune('\n')
+ }
+ }
+
+ return b.String()
+}
+
+func max(a, b int) int { //nolint:unparam,predeclared
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min(a, b int) int { //nolint:predeclared
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func abs(a int) int {
+ if a < 0 {
+ return -a
+ }
+
+ return a
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/unset.go b/vendor/github.com/charmbracelet/lipgloss/unset.go
new file mode 100644
index 0000000..1086e72
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/unset.go
@@ -0,0 +1,331 @@
+package lipgloss
+
+// unset unsets a property from a style.
+func (s *Style) unset(key propKey) {
+ s.props = s.props.unset(key)
+}
+
+// UnsetBold removes the bold style rule, if set.
+func (s Style) UnsetBold() Style {
+ s.unset(boldKey)
+ return s
+}
+
+// UnsetItalic removes the italic style rule, if set.
+func (s Style) UnsetItalic() Style {
+ s.unset(italicKey)
+ return s
+}
+
+// UnsetUnderline removes the underline style rule, if set.
+func (s Style) UnsetUnderline() Style {
+ s.unset(underlineKey)
+ return s
+}
+
+// UnsetStrikethrough removes the strikethrough style rule, if set.
+func (s Style) UnsetStrikethrough() Style {
+ s.unset(strikethroughKey)
+ return s
+}
+
+// UnsetReverse removes the reverse style rule, if set.
+func (s Style) UnsetReverse() Style {
+ s.unset(reverseKey)
+ return s
+}
+
+// UnsetBlink removes the blink style rule, if set.
+func (s Style) UnsetBlink() Style {
+ s.unset(blinkKey)
+ return s
+}
+
+// UnsetFaint removes the faint style rule, if set.
+func (s Style) UnsetFaint() Style {
+ s.unset(faintKey)
+ return s
+}
+
+// UnsetForeground removes the foreground style rule, if set.
+func (s Style) UnsetForeground() Style {
+ s.unset(foregroundKey)
+ return s
+}
+
+// UnsetBackground removes the background style rule, if set.
+func (s Style) UnsetBackground() Style {
+ s.unset(backgroundKey)
+ return s
+}
+
+// UnsetWidth removes the width style rule, if set.
+func (s Style) UnsetWidth() Style {
+ s.unset(widthKey)
+ return s
+}
+
+// UnsetHeight removes the height style rule, if set.
+func (s Style) UnsetHeight() Style {
+ s.unset(heightKey)
+ return s
+}
+
+// UnsetAlign removes the horizontal and vertical text alignment style rule, if set.
+func (s Style) UnsetAlign() Style {
+ s.unset(alignHorizontalKey)
+ s.unset(alignVerticalKey)
+ return s
+}
+
+// UnsetAlignHorizontal removes the horizontal text alignment style rule, if set.
+func (s Style) UnsetAlignHorizontal() Style {
+ s.unset(alignHorizontalKey)
+ return s
+}
+
+// UnsetAlignVertical removes the vertical text alignment style rule, if set.
+func (s Style) UnsetAlignVertical() Style {
+ s.unset(alignVerticalKey)
+ return s
+}
+
+// UnsetPadding removes all padding style rules.
+func (s Style) UnsetPadding() Style {
+ s.unset(paddingLeftKey)
+ s.unset(paddingRightKey)
+ s.unset(paddingTopKey)
+ s.unset(paddingBottomKey)
+ return s
+}
+
+// UnsetPaddingLeft removes the left padding style rule, if set.
+func (s Style) UnsetPaddingLeft() Style {
+ s.unset(paddingLeftKey)
+ return s
+}
+
+// UnsetPaddingRight removes the right padding style rule, if set.
+func (s Style) UnsetPaddingRight() Style {
+ s.unset(paddingRightKey)
+ return s
+}
+
+// UnsetPaddingTop removes the top padding style rule, if set.
+func (s Style) UnsetPaddingTop() Style {
+ s.unset(paddingTopKey)
+ return s
+}
+
+// UnsetPaddingBottom removes the bottom padding style rule, if set.
+func (s Style) UnsetPaddingBottom() Style {
+ s.unset(paddingBottomKey)
+ return s
+}
+
+// UnsetColorWhitespace removes the rule for coloring padding, if set.
+func (s Style) UnsetColorWhitespace() Style {
+ s.unset(colorWhitespaceKey)
+ return s
+}
+
+// UnsetMargins removes all margin style rules.
+func (s Style) UnsetMargins() Style {
+ s.unset(marginLeftKey)
+ s.unset(marginRightKey)
+ s.unset(marginTopKey)
+ s.unset(marginBottomKey)
+ return s
+}
+
+// UnsetMarginLeft removes the left margin style rule, if set.
+func (s Style) UnsetMarginLeft() Style {
+ s.unset(marginLeftKey)
+ return s
+}
+
+// UnsetMarginRight removes the right margin style rule, if set.
+func (s Style) UnsetMarginRight() Style {
+ s.unset(marginRightKey)
+ return s
+}
+
+// UnsetMarginTop removes the top margin style rule, if set.
+func (s Style) UnsetMarginTop() Style {
+ s.unset(marginTopKey)
+ return s
+}
+
+// UnsetMarginBottom removes the bottom margin style rule, if set.
+func (s Style) UnsetMarginBottom() Style {
+ s.unset(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 {
+ s.unset(marginBackgroundKey)
+ return s
+}
+
+// UnsetBorderStyle removes the border style rule, if set.
+func (s Style) UnsetBorderStyle() Style {
+ s.unset(borderStyleKey)
+ return s
+}
+
+// UnsetBorderTop removes the border top style rule, if set.
+func (s Style) UnsetBorderTop() Style {
+ s.unset(borderTopKey)
+ return s
+}
+
+// UnsetBorderRight removes the border right style rule, if set.
+func (s Style) UnsetBorderRight() Style {
+ s.unset(borderRightKey)
+ return s
+}
+
+// UnsetBorderBottom removes the border bottom style rule, if set.
+func (s Style) UnsetBorderBottom() Style {
+ s.unset(borderBottomKey)
+ return s
+}
+
+// UnsetBorderLeft removes the border left style rule, if set.
+func (s Style) UnsetBorderLeft() Style {
+ s.unset(borderLeftKey)
+ return s
+}
+
+// UnsetBorderForeground removes all border foreground color styles, if set.
+func (s Style) UnsetBorderForeground() Style {
+ s.unset(borderTopForegroundKey)
+ s.unset(borderRightForegroundKey)
+ s.unset(borderBottomForegroundKey)
+ s.unset(borderLeftForegroundKey)
+ return s
+}
+
+// UnsetBorderTopForeground removes the top border foreground color rule,
+// if set.
+func (s Style) UnsetBorderTopForeground() Style {
+ s.unset(borderTopForegroundKey)
+ return s
+}
+
+// UnsetBorderRightForeground removes the right border foreground color rule,
+// if set.
+func (s Style) UnsetBorderRightForeground() Style {
+ s.unset(borderRightForegroundKey)
+ return s
+}
+
+// UnsetBorderBottomForeground removes the bottom border foreground color
+// rule, if set.
+func (s Style) UnsetBorderBottomForeground() Style {
+ s.unset(borderBottomForegroundKey)
+ return s
+}
+
+// UnsetBorderLeftForeground removes the left border foreground color rule,
+// if set.
+func (s Style) UnsetBorderLeftForeground() Style {
+ s.unset(borderLeftForegroundKey)
+ return s
+}
+
+// UnsetBorderBackground removes all border background color styles, if
+// set.
+func (s Style) UnsetBorderBackground() Style {
+ s.unset(borderTopBackgroundKey)
+ s.unset(borderRightBackgroundKey)
+ s.unset(borderBottomBackgroundKey)
+ s.unset(borderLeftBackgroundKey)
+ return s
+}
+
+// UnsetBorderTopBackgroundColor removes the top border background color rule,
+// if set.
+//
+// Deprecated: This function simply calls Style.UnsetBorderTopBackground.
+func (s Style) UnsetBorderTopBackgroundColor() Style {
+ return s.UnsetBorderTopBackground()
+}
+
+// UnsetBorderTopBackground removes the top border background color rule,
+// if set.
+func (s Style) UnsetBorderTopBackground() Style {
+ s.unset(borderTopBackgroundKey)
+ return s
+}
+
+// UnsetBorderRightBackground removes the right border background color
+// rule, if set.
+func (s Style) UnsetBorderRightBackground() Style {
+ s.unset(borderRightBackgroundKey)
+ return s
+}
+
+// UnsetBorderBottomBackground removes the bottom border background color
+// rule, if set.
+func (s Style) UnsetBorderBottomBackground() Style {
+ s.unset(borderBottomBackgroundKey)
+ return s
+}
+
+// UnsetBorderLeftBackground removes the left border color rule, if set.
+func (s Style) UnsetBorderLeftBackground() Style {
+ s.unset(borderLeftBackgroundKey)
+ return s
+}
+
+// UnsetInline removes the inline style rule, if set.
+func (s Style) UnsetInline() Style {
+ s.unset(inlineKey)
+ return s
+}
+
+// UnsetMaxWidth removes the max width style rule, if set.
+func (s Style) UnsetMaxWidth() Style {
+ s.unset(maxWidthKey)
+ return s
+}
+
+// UnsetMaxHeight removes the max height style rule, if set.
+func (s Style) UnsetMaxHeight() Style {
+ s.unset(maxHeightKey)
+ return s
+}
+
+// UnsetTabWidth removes the tab width style rule, if set.
+func (s Style) UnsetTabWidth() Style {
+ s.unset(tabWidthKey)
+ return s
+}
+
+// UnsetUnderlineSpaces removes the value set by UnderlineSpaces.
+func (s Style) UnsetUnderlineSpaces() Style {
+ s.unset(underlineSpacesKey)
+ return s
+}
+
+// UnsetStrikethroughSpaces removes the value set by StrikethroughSpaces.
+func (s Style) UnsetStrikethroughSpaces() Style {
+ s.unset(strikethroughSpacesKey)
+ return s
+}
+
+// UnsetTransform removes the value set by Transform.
+func (s Style) UnsetTransform() Style {
+ s.unset(transformKey)
+ return s
+}
+
+// UnsetString sets the underlying string value to the empty string.
+func (s Style) UnsetString() Style {
+ s.value = ""
+ return s
+}
diff --git a/vendor/github.com/charmbracelet/lipgloss/whitespace.go b/vendor/github.com/charmbracelet/lipgloss/whitespace.go
new file mode 100644
index 0000000..040dc98
--- /dev/null
+++ b/vendor/github.com/charmbracelet/lipgloss/whitespace.go
@@ -0,0 +1,83 @@
+package lipgloss
+
+import (
+ "strings"
+
+ "github.com/charmbracelet/x/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, if you're 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.StringWidth(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.StringWidth(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
+ }
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/LICENSE b/vendor/github.com/charmbracelet/x/ansi/LICENSE
new file mode 100644
index 0000000..65a5654
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 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.
diff --git a/vendor/github.com/charmbracelet/x/ansi/ansi.go b/vendor/github.com/charmbracelet/x/ansi/ansi.go
new file mode 100644
index 0000000..48d873c
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/ansi.go
@@ -0,0 +1,11 @@
+package ansi
+
+import "io"
+
+// Execute is a function that "execute" the given escape sequence by writing it
+// to the provided output writter.
+//
+// This is a syntactic sugar over [io.WriteString].
+func Execute(w io.Writer, s string) (int, error) {
+ return io.WriteString(w, s)
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/ascii.go b/vendor/github.com/charmbracelet/x/ansi/ascii.go
new file mode 100644
index 0000000..188582f
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/ascii.go
@@ -0,0 +1,8 @@
+package ansi
+
+const (
+ // SP is the space character (Char: \x20).
+ SP = 0x20
+ // DEL is the delete character (Caret: ^?, Char: \x7f).
+ DEL = 0x7F
+)
diff --git a/vendor/github.com/charmbracelet/x/ansi/background.go b/vendor/github.com/charmbracelet/x/ansi/background.go
new file mode 100644
index 0000000..2383cf0
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/background.go
@@ -0,0 +1,169 @@
+package ansi
+
+import (
+ "fmt"
+ "image/color"
+)
+
+// Colorizer is a [color.Color] interface that can be formatted as a string.
+type Colorizer interface {
+ color.Color
+ fmt.Stringer
+}
+
+// HexColorizer is a [color.Color] that can be formatted as a hex string.
+type HexColorizer struct{ color.Color }
+
+var _ Colorizer = HexColorizer{}
+
+// String returns the color as a hex string. If the color is nil, an empty
+// string is returned.
+func (h HexColorizer) String() string {
+ if h.Color == nil {
+ return ""
+ }
+ r, g, b, _ := h.RGBA()
+ // Get the lower 8 bits
+ r &= 0xff
+ g &= 0xff
+ b &= 0xff
+ return fmt.Sprintf("#%02x%02x%02x", uint8(r), uint8(g), uint8(b)) //nolint:gosec
+}
+
+// XRGBColorizer is a [color.Color] that can be formatted as an XParseColor
+// rgb: string.
+//
+// See: https://linux.die.net/man/3/xparsecolor
+type XRGBColorizer struct{ color.Color }
+
+var _ Colorizer = XRGBColorizer{}
+
+// String returns the color as an XParseColor rgb: string. If the color is nil,
+// an empty string is returned.
+func (x XRGBColorizer) String() string {
+ if x.Color == nil {
+ return ""
+ }
+ r, g, b, _ := x.RGBA()
+ // Get the lower 8 bits
+ return fmt.Sprintf("rgb:%04x/%04x/%04x", r, g, b)
+}
+
+// XRGBAColorizer is a [color.Color] that can be formatted as an XParseColor
+// rgba: string.
+//
+// See: https://linux.die.net/man/3/xparsecolor
+type XRGBAColorizer struct{ color.Color }
+
+var _ Colorizer = XRGBAColorizer{}
+
+// String returns the color as an XParseColor rgba: string. If the color is nil,
+// an empty string is returned.
+func (x XRGBAColorizer) String() string {
+ if x.Color == nil {
+ return ""
+ }
+ r, g, b, a := x.RGBA()
+ // Get the lower 8 bits
+ return fmt.Sprintf("rgba:%04x/%04x/%04x/%04x", r, g, b, a)
+}
+
+// SetForegroundColor returns a sequence that sets the default terminal
+// foreground color.
+//
+// OSC 10 ; color ST
+// OSC 10 ; color BEL
+//
+// Where color is the encoded color number.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+func SetForegroundColor(c color.Color) string {
+ var s string
+ switch c := c.(type) {
+ case Colorizer:
+ s = c.String()
+ case fmt.Stringer:
+ s = c.String()
+ default:
+ s = HexColorizer{c}.String()
+ }
+ return "\x1b]10;" + s + "\x07"
+}
+
+// RequestForegroundColor is a sequence that requests the current default
+// terminal foreground color.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+const RequestForegroundColor = "\x1b]10;?\x07"
+
+// ResetForegroundColor is a sequence that resets the default terminal
+// foreground color.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+const ResetForegroundColor = "\x1b]110\x07"
+
+// SetBackgroundColor returns a sequence that sets the default terminal
+// background color.
+//
+// OSC 11 ; color ST
+// OSC 11 ; color BEL
+//
+// Where color is the encoded color number.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+func SetBackgroundColor(c color.Color) string {
+ var s string
+ switch c := c.(type) {
+ case Colorizer:
+ s = c.String()
+ case fmt.Stringer:
+ s = c.String()
+ default:
+ s = HexColorizer{c}.String()
+ }
+ return "\x1b]11;" + s + "\x07"
+}
+
+// RequestBackgroundColor is a sequence that requests the current default
+// terminal background color.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+const RequestBackgroundColor = "\x1b]11;?\x07"
+
+// ResetBackgroundColor is a sequence that resets the default terminal
+// background color.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+const ResetBackgroundColor = "\x1b]111\x07"
+
+// SetCursorColor returns a sequence that sets the terminal cursor color.
+//
+// OSC 12 ; color ST
+// OSC 12 ; color BEL
+//
+// Where color is the encoded color number.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+func SetCursorColor(c color.Color) string {
+ var s string
+ switch c := c.(type) {
+ case Colorizer:
+ s = c.String()
+ case fmt.Stringer:
+ s = c.String()
+ default:
+ s = HexColorizer{c}.String()
+ }
+ return "\x1b]12;" + s + "\x07"
+}
+
+// RequestCursorColor is a sequence that requests the current terminal cursor
+// color.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+const RequestCursorColor = "\x1b]12;?\x07"
+
+// ResetCursorColor is a sequence that resets the terminal cursor color.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+const ResetCursorColor = "\x1b]112\x07"
diff --git a/vendor/github.com/charmbracelet/x/ansi/c0.go b/vendor/github.com/charmbracelet/x/ansi/c0.go
new file mode 100644
index 0000000..28ff7c2
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/c0.go
@@ -0,0 +1,79 @@
+package ansi
+
+// C0 control characters.
+//
+// These range from (0x00-0x1F) as defined in ISO 646 (ASCII).
+// See: https://en.wikipedia.org/wiki/C0_and_C1_control_codes
+const (
+ // NUL is the null character (Caret: ^@, Char: \0).
+ NUL = 0x00
+ // SOH is the start of heading character (Caret: ^A).
+ SOH = 0x01
+ // STX is the start of text character (Caret: ^B).
+ STX = 0x02
+ // ETX is the end of text character (Caret: ^C).
+ ETX = 0x03
+ // EOT is the end of transmission character (Caret: ^D).
+ EOT = 0x04
+ // ENQ is the enquiry character (Caret: ^E).
+ ENQ = 0x05
+ // ACK is the acknowledge character (Caret: ^F).
+ ACK = 0x06
+ // BEL is the bell character (Caret: ^G, Char: \a).
+ BEL = 0x07
+ // BS is the backspace character (Caret: ^H, Char: \b).
+ BS = 0x08
+ // HT is the horizontal tab character (Caret: ^I, Char: \t).
+ HT = 0x09
+ // LF is the line feed character (Caret: ^J, Char: \n).
+ LF = 0x0A
+ // VT is the vertical tab character (Caret: ^K, Char: \v).
+ VT = 0x0B
+ // FF is the form feed character (Caret: ^L, Char: \f).
+ FF = 0x0C
+ // CR is the carriage return character (Caret: ^M, Char: \r).
+ CR = 0x0D
+ // SO is the shift out character (Caret: ^N).
+ SO = 0x0E
+ // SI is the shift in character (Caret: ^O).
+ SI = 0x0F
+ // DLE is the data link escape character (Caret: ^P).
+ DLE = 0x10
+ // DC1 is the device control 1 character (Caret: ^Q).
+ DC1 = 0x11
+ // DC2 is the device control 2 character (Caret: ^R).
+ DC2 = 0x12
+ // DC3 is the device control 3 character (Caret: ^S).
+ DC3 = 0x13
+ // DC4 is the device control 4 character (Caret: ^T).
+ DC4 = 0x14
+ // NAK is the negative acknowledge character (Caret: ^U).
+ NAK = 0x15
+ // SYN is the synchronous idle character (Caret: ^V).
+ SYN = 0x16
+ // ETB is the end of transmission block character (Caret: ^W).
+ ETB = 0x17
+ // CAN is the cancel character (Caret: ^X).
+ CAN = 0x18
+ // EM is the end of medium character (Caret: ^Y).
+ EM = 0x19
+ // SUB is the substitute character (Caret: ^Z).
+ SUB = 0x1A
+ // ESC is the escape character (Caret: ^[, Char: \e).
+ ESC = 0x1B
+ // FS is the file separator character (Caret: ^\).
+ FS = 0x1C
+ // GS is the group separator character (Caret: ^]).
+ GS = 0x1D
+ // RS is the record separator character (Caret: ^^).
+ RS = 0x1E
+ // US is the unit separator character (Caret: ^_).
+ US = 0x1F
+
+ // LS0 is the locking shift 0 character.
+ // This is an alias for [SI].
+ LS0 = SI
+ // LS1 is the locking shift 1 character.
+ // This is an alias for [SO].
+ LS1 = SO
+)
diff --git a/vendor/github.com/charmbracelet/x/ansi/c1.go b/vendor/github.com/charmbracelet/x/ansi/c1.go
new file mode 100644
index 0000000..71058f5
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/c1.go
@@ -0,0 +1,72 @@
+package ansi
+
+// C1 control characters.
+//
+// These range from (0x80-0x9F) as defined in ISO 6429 (ECMA-48).
+// See: https://en.wikipedia.org/wiki/C0_and_C1_control_codes
+const (
+ // PAD is the padding character.
+ PAD = 0x80
+ // HOP is the high octet preset character.
+ HOP = 0x81
+ // BPH is the break permitted here character.
+ BPH = 0x82
+ // NBH is the no break here character.
+ NBH = 0x83
+ // IND is the index character.
+ IND = 0x84
+ // NEL is the next line character.
+ NEL = 0x85
+ // SSA is the start of selected area character.
+ SSA = 0x86
+ // ESA is the end of selected area character.
+ ESA = 0x87
+ // HTS is the horizontal tab set character.
+ HTS = 0x88
+ // HTJ is the horizontal tab with justification character.
+ HTJ = 0x89
+ // VTS is the vertical tab set character.
+ VTS = 0x8A
+ // PLD is the partial line forward character.
+ PLD = 0x8B
+ // PLU is the partial line backward character.
+ PLU = 0x8C
+ // RI is the reverse index character.
+ RI = 0x8D
+ // SS2 is the single shift 2 character.
+ SS2 = 0x8E
+ // SS3 is the single shift 3 character.
+ SS3 = 0x8F
+ // DCS is the device control string character.
+ DCS = 0x90
+ // PU1 is the private use 1 character.
+ PU1 = 0x91
+ // PU2 is the private use 2 character.
+ PU2 = 0x92
+ // STS is the set transmit state character.
+ STS = 0x93
+ // CCH is the cancel character.
+ CCH = 0x94
+ // MW is the message waiting character.
+ MW = 0x95
+ // SPA is the start of guarded area character.
+ SPA = 0x96
+ // EPA is the end of guarded area character.
+ EPA = 0x97
+ // SOS is the start of string character.
+ SOS = 0x98
+ // SGCI is the single graphic character introducer character.
+ SGCI = 0x99
+ // SCI is the single character introducer character.
+ SCI = 0x9A
+ // CSI is the control sequence introducer character.
+ CSI = 0x9B
+ // ST is the string terminator character.
+ ST = 0x9C
+ // OSC is the operating system command character.
+ OSC = 0x9D
+ // PM is the privacy message character.
+ PM = 0x9E
+ // APC is the application program command character.
+ APC = 0x9F
+)
diff --git a/vendor/github.com/charmbracelet/x/ansi/charset.go b/vendor/github.com/charmbracelet/x/ansi/charset.go
new file mode 100644
index 0000000..50fff51
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/charset.go
@@ -0,0 +1,55 @@
+package ansi
+
+// SelectCharacterSet sets the G-set character designator to the specified
+// character set.
+//
+// ESC Ps Pd
+//
+// Where Ps is the G-set character designator, and Pd is the identifier.
+// For 94-character sets, the designator can be one of:
+// - ( G0
+// - ) G1
+// - * G2
+// - + G3
+//
+// For 96-character sets, the designator can be one of:
+// - - G1
+// - . G2
+// - / G3
+//
+// Some common 94-character sets are:
+// - 0 DEC Special Drawing Set
+// - A United Kingdom (UK)
+// - B United States (USASCII)
+//
+// Examples:
+//
+// ESC ( B Select character set G0 = United States (USASCII)
+// ESC ( 0 Select character set G0 = Special Character and Line Drawing Set
+// ESC ) 0 Select character set G1 = Special Character and Line Drawing Set
+// ESC * A Select character set G2 = United Kingdom (UK)
+//
+// See: https://vt100.net/docs/vt510-rm/SCS.html
+func SelectCharacterSet(gset byte, charset byte) string {
+ return "\x1b" + string(gset) + string(charset)
+}
+
+// SCS is an alias for SelectCharacterSet.
+func SCS(gset byte, charset byte) string {
+ return SelectCharacterSet(gset, charset)
+}
+
+// Locking Shift 1 Right (LS1R) shifts G1 into GR character set.
+const LS1R = "\x1b~"
+
+// Locking Shift 2 (LS2) shifts G2 into GL character set.
+const LS2 = "\x1bn"
+
+// Locking Shift 2 Right (LS2R) shifts G2 into GR character set.
+const LS2R = "\x1b}"
+
+// Locking Shift 3 (LS3) shifts G3 into GL character set.
+const LS3 = "\x1bo"
+
+// Locking Shift 3 Right (LS3R) shifts G3 into GR character set.
+const LS3R = "\x1b|"
diff --git a/vendor/github.com/charmbracelet/x/ansi/clipboard.go b/vendor/github.com/charmbracelet/x/ansi/clipboard.go
new file mode 100644
index 0000000..94d26c3
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/clipboard.go
@@ -0,0 +1,75 @@
+package ansi
+
+import "encoding/base64"
+
+// Clipboard names.
+const (
+ SystemClipboard = 'c'
+ PrimaryClipboard = 'p'
+)
+
+// SetClipboard returns a sequence for manipulating the clipboard.
+//
+// OSC 52 ; Pc ; Pd ST
+// OSC 52 ; Pc ; Pd BEL
+//
+// Where Pc is the clipboard name and Pd is the base64 encoded data.
+// Empty data or invalid base64 data will reset the clipboard.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+func SetClipboard(c byte, d string) string {
+ if d != "" {
+ d = base64.StdEncoding.EncodeToString([]byte(d))
+ }
+ return "\x1b]52;" + string(c) + ";" + d + "\x07"
+}
+
+// SetSystemClipboard returns a sequence for setting the system clipboard.
+//
+// This is equivalent to SetClipboard(SystemClipboard, d).
+func SetSystemClipboard(d string) string {
+ return SetClipboard(SystemClipboard, d)
+}
+
+// SetPrimaryClipboard returns a sequence for setting the primary clipboard.
+//
+// This is equivalent to SetClipboard(PrimaryClipboard, d).
+func SetPrimaryClipboard(d string) string {
+ return SetClipboard(PrimaryClipboard, d)
+}
+
+// ResetClipboard returns a sequence for resetting the clipboard.
+//
+// This is equivalent to SetClipboard(c, "").
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+func ResetClipboard(c byte) string {
+ return SetClipboard(c, "")
+}
+
+// ResetSystemClipboard is a sequence for resetting the system clipboard.
+//
+// This is equivalent to ResetClipboard(SystemClipboard).
+const ResetSystemClipboard = "\x1b]52;c;\x07"
+
+// ResetPrimaryClipboard is a sequence for resetting the primary clipboard.
+//
+// This is equivalent to ResetClipboard(PrimaryClipboard).
+const ResetPrimaryClipboard = "\x1b]52;p;\x07"
+
+// RequestClipboard returns a sequence for requesting the clipboard.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+func RequestClipboard(c byte) string {
+ return "\x1b]52;" + string(c) + ";?\x07"
+}
+
+// RequestSystemClipboard is a sequence for requesting the system clipboard.
+//
+// This is equivalent to RequestClipboard(SystemClipboard).
+const RequestSystemClipboard = "\x1b]52;c;?\x07"
+
+// RequestPrimaryClipboard is a sequence for requesting the primary clipboard.
+//
+// This is equivalent to RequestClipboard(PrimaryClipboard).
+const RequestPrimaryClipboard = "\x1b]52;p;?\x07"
diff --git a/vendor/github.com/charmbracelet/x/ansi/color.go b/vendor/github.com/charmbracelet/x/ansi/color.go
new file mode 100644
index 0000000..77f8a08
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/color.go
@@ -0,0 +1,196 @@
+package ansi
+
+import (
+ "image/color"
+)
+
+// Technically speaking, the 16 basic ANSI colors are arbitrary and can be
+// customized at the terminal level. Given that, we're returning what we feel
+// are good defaults.
+//
+// This could also be a slice, but we use a map to make the mappings very
+// explicit.
+//
+// See: https://www.ditig.com/publications/256-colors-cheat-sheet
+var lowANSI = map[uint32]uint32{
+ 0: 0x000000, // black
+ 1: 0x800000, // red
+ 2: 0x008000, // green
+ 3: 0x808000, // yellow
+ 4: 0x000080, // blue
+ 5: 0x800080, // magenta
+ 6: 0x008080, // cyan
+ 7: 0xc0c0c0, // white
+ 8: 0x808080, // bright black
+ 9: 0xff0000, // bright red
+ 10: 0x00ff00, // bright green
+ 11: 0xffff00, // bright yellow
+ 12: 0x0000ff, // bright blue
+ 13: 0xff00ff, // bright magenta
+ 14: 0x00ffff, // bright cyan
+ 15: 0xffffff, // bright white
+}
+
+// Color is a color that can be used in a terminal. ANSI (including
+// ANSI256) and 24-bit "true colors" fall under this category.
+type Color interface {
+ color.Color
+}
+
+// BasicColor is an ANSI 3-bit or 4-bit color with a value from 0 to 15.
+type BasicColor uint8
+
+var _ Color = BasicColor(0)
+
+const (
+ // Black is the ANSI black color.
+ Black BasicColor = iota
+
+ // Red is the ANSI red color.
+ Red
+
+ // Green is the ANSI green color.
+ Green
+
+ // Yellow is the ANSI yellow color.
+ Yellow
+
+ // Blue is the ANSI blue color.
+ Blue
+
+ // Magenta is the ANSI magenta color.
+ Magenta
+
+ // Cyan is the ANSI cyan color.
+ Cyan
+
+ // White is the ANSI white color.
+ White
+
+ // BrightBlack is the ANSI bright black color.
+ BrightBlack
+
+ // BrightRed is the ANSI bright red color.
+ BrightRed
+
+ // BrightGreen is the ANSI bright green color.
+ BrightGreen
+
+ // BrightYellow is the ANSI bright yellow color.
+ BrightYellow
+
+ // BrightBlue is the ANSI bright blue color.
+ BrightBlue
+
+ // BrightMagenta is the ANSI bright magenta color.
+ BrightMagenta
+
+ // BrightCyan is the ANSI bright cyan color.
+ BrightCyan
+
+ // BrightWhite is the ANSI bright white color.
+ BrightWhite
+)
+
+// RGBA returns the red, green, blue and alpha components of the color. It
+// satisfies the color.Color interface.
+func (c BasicColor) RGBA() (uint32, uint32, uint32, uint32) {
+ ansi := uint32(c)
+ if ansi > 15 {
+ return 0, 0, 0, 0xffff
+ }
+
+ r, g, b := ansiToRGB(ansi)
+ return toRGBA(r, g, b)
+}
+
+// ExtendedColor is an ANSI 256 (8-bit) color with a value from 0 to 255.
+type ExtendedColor uint8
+
+var _ Color = ExtendedColor(0)
+
+// RGBA returns the red, green, blue and alpha components of the color. It
+// satisfies the color.Color interface.
+func (c ExtendedColor) RGBA() (uint32, uint32, uint32, uint32) {
+ r, g, b := ansiToRGB(uint32(c))
+ return toRGBA(r, g, b)
+}
+
+// TrueColor is a 24-bit color that can be used in the terminal.
+// This can be used to represent RGB colors.
+//
+// For example, the color red can be represented as:
+//
+// TrueColor(0xff0000)
+type TrueColor uint32
+
+var _ Color = TrueColor(0)
+
+// RGBA returns the red, green, blue and alpha components of the color. It
+// satisfies the color.Color interface.
+func (c TrueColor) RGBA() (uint32, uint32, uint32, uint32) {
+ r, g, b := hexToRGB(uint32(c))
+ return toRGBA(r, g, b)
+}
+
+// ansiToRGB converts an ANSI color to a 24-bit RGB color.
+//
+// r, g, b := ansiToRGB(57)
+func ansiToRGB(ansi uint32) (uint32, uint32, uint32) {
+ // For out-of-range values return black.
+ if ansi > 255 {
+ return 0, 0, 0
+ }
+
+ // Low ANSI.
+ if ansi < 16 {
+ h, ok := lowANSI[ansi]
+ if !ok {
+ return 0, 0, 0
+ }
+ r, g, b := hexToRGB(h)
+ return r, g, b
+ }
+
+ // Grays.
+ if ansi > 231 {
+ s := (ansi-232)*10 + 8
+ return s, s, s
+ }
+
+ // ANSI256.
+ n := ansi - 16
+ b := n % 6
+ g := (n - b) / 6 % 6
+ r := (n - b - g*6) / 36 % 6
+ for _, v := range []*uint32{&r, &g, &b} {
+ if *v > 0 {
+ c := *v*40 + 55
+ *v = c
+ }
+ }
+
+ return r, g, b
+}
+
+// hexToRGB converts a number in hexadecimal format to red, green, and blue
+// values.
+//
+// r, g, b := hexToRGB(0x0000FF)
+func hexToRGB(hex uint32) (uint32, uint32, uint32) {
+ return hex >> 16 & 0xff, hex >> 8 & 0xff, hex & 0xff
+}
+
+// toRGBA converts an RGB 8-bit color values to 32-bit color values suitable
+// for color.Color.
+//
+// color.Color requires 16-bit color values, so we duplicate the 8-bit values
+// to fill the 16-bit values.
+//
+// This always returns 0xffff (opaque) for the alpha channel.
+func toRGBA(r, g, b uint32) (uint32, uint32, uint32, uint32) {
+ r |= r << 8
+ g |= g << 8
+ b |= b << 8
+ return r, g, b, 0xffff
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/ctrl.go b/vendor/github.com/charmbracelet/x/ansi/ctrl.go
new file mode 100644
index 0000000..8ca744c
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/ctrl.go
@@ -0,0 +1,137 @@
+package ansi
+
+import (
+ "strconv"
+ "strings"
+)
+
+// RequestNameVersion (XTVERSION) is a control sequence that requests the
+// terminal's name and version. It responds with a DSR sequence identifying the
+// terminal.
+//
+// CSI > 0 q
+// DCS > | text ST
+//
+// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys
+const (
+ RequestNameVersion = "\x1b[>q"
+ XTVERSION = RequestNameVersion
+)
+
+// RequestXTVersion is a control sequence that requests the terminal's XTVERSION. It responds with a DSR sequence identifying the version.
+//
+// CSI > Ps q
+// DCS > | text ST
+//
+// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys
+//
+// Deprecated: use [RequestNameVersion] instead.
+const RequestXTVersion = RequestNameVersion
+
+// PrimaryDeviceAttributes (DA1) is a control sequence that reports the
+// terminal's primary device attributes.
+//
+// CSI c
+// CSI 0 c
+// CSI ? Ps ; ... c
+//
+// If no attributes are given, or if the attribute is 0, this function returns
+// the request sequence. Otherwise, it returns the response sequence.
+//
+// See https://vt100.net/docs/vt510-rm/DA1.html
+func PrimaryDeviceAttributes(attrs ...int) string {
+ if len(attrs) == 0 {
+ return RequestPrimaryDeviceAttributes
+ } else if len(attrs) == 1 && attrs[0] == 0 {
+ return "\x1b[0c"
+ }
+
+ as := make([]string, len(attrs))
+ for i, a := range attrs {
+ as[i] = strconv.Itoa(a)
+ }
+ return "\x1b[?" + strings.Join(as, ";") + "c"
+}
+
+// DA1 is an alias for [PrimaryDeviceAttributes].
+func DA1(attrs ...int) string {
+ return PrimaryDeviceAttributes(attrs...)
+}
+
+// RequestPrimaryDeviceAttributes is a control sequence that requests the
+// terminal's primary device attributes (DA1).
+//
+// CSI c
+//
+// See https://vt100.net/docs/vt510-rm/DA1.html
+const RequestPrimaryDeviceAttributes = "\x1b[c"
+
+// SecondaryDeviceAttributes (DA2) is a control sequence that reports the
+// terminal's secondary device attributes.
+//
+// CSI > c
+// CSI > 0 c
+// CSI > Ps ; ... c
+//
+// See https://vt100.net/docs/vt510-rm/DA2.html
+func SecondaryDeviceAttributes(attrs ...int) string {
+ if len(attrs) == 0 {
+ return RequestSecondaryDeviceAttributes
+ }
+
+ as := make([]string, len(attrs))
+ for i, a := range attrs {
+ as[i] = strconv.Itoa(a)
+ }
+ return "\x1b[>" + strings.Join(as, ";") + "c"
+}
+
+// DA2 is an alias for [SecondaryDeviceAttributes].
+func DA2(attrs ...int) string {
+ return SecondaryDeviceAttributes(attrs...)
+}
+
+// RequestSecondaryDeviceAttributes is a control sequence that requests the
+// terminal's secondary device attributes (DA2).
+//
+// CSI > c
+//
+// See https://vt100.net/docs/vt510-rm/DA2.html
+const RequestSecondaryDeviceAttributes = "\x1b[>c"
+
+// TertiaryDeviceAttributes (DA3) is a control sequence that reports the
+// terminal's tertiary device attributes.
+//
+// CSI = c
+// CSI = 0 c
+// DCS ! | Text ST
+//
+// Where Text is the unit ID for the terminal.
+//
+// If no unit ID is given, or if the unit ID is 0, this function returns the
+// request sequence. Otherwise, it returns the response sequence.
+//
+// See https://vt100.net/docs/vt510-rm/DA3.html
+func TertiaryDeviceAttributes(unitID string) string {
+ switch unitID {
+ case "":
+ return RequestTertiaryDeviceAttributes
+ case "0":
+ return "\x1b[=0c"
+ }
+
+ return "\x1bP!|" + unitID + "\x1b\\"
+}
+
+// DA3 is an alias for [TertiaryDeviceAttributes].
+func DA3(unitID string) string {
+ return TertiaryDeviceAttributes(unitID)
+}
+
+// RequestTertiaryDeviceAttributes is a control sequence that requests the
+// terminal's tertiary device attributes (DA3).
+//
+// CSI = c
+//
+// See https://vt100.net/docs/vt510-rm/DA3.html
+const RequestTertiaryDeviceAttributes = "\x1b[=c"
diff --git a/vendor/github.com/charmbracelet/x/ansi/cursor.go b/vendor/github.com/charmbracelet/x/ansi/cursor.go
new file mode 100644
index 0000000..0c364d6
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/cursor.go
@@ -0,0 +1,633 @@
+package ansi
+
+import "strconv"
+
+// SaveCursor (DECSC) is an escape sequence that saves the current cursor
+// position.
+//
+// ESC 7
+//
+// See: https://vt100.net/docs/vt510-rm/DECSC.html
+const (
+ SaveCursor = "\x1b7"
+ DECSC = SaveCursor
+)
+
+// RestoreCursor (DECRC) is an escape sequence that restores the cursor
+// position.
+//
+// ESC 8
+//
+// See: https://vt100.net/docs/vt510-rm/DECRC.html
+const (
+ RestoreCursor = "\x1b8"
+ DECRC = RestoreCursor
+)
+
+// RequestCursorPosition is an escape sequence that requests the current cursor
+// position.
+//
+// CSI 6 n
+//
+// The terminal will report the cursor position as a CSI sequence in the
+// following format:
+//
+// CSI Pl ; Pc R
+//
+// Where Pl is the line number and Pc is the column number.
+// See: https://vt100.net/docs/vt510-rm/CPR.html
+//
+// Deprecated: use [RequestCursorPositionReport] instead.
+const RequestCursorPosition = "\x1b[6n"
+
+// RequestExtendedCursorPosition (DECXCPR) is a sequence for requesting the
+// cursor position report including the current page number.
+//
+// CSI ? 6 n
+//
+// The terminal will report the cursor position as a CSI sequence in the
+// following format:
+//
+// CSI ? Pl ; Pc ; Pp R
+//
+// Where Pl is the line number, Pc is the column number, and Pp is the page
+// number.
+// See: https://vt100.net/docs/vt510-rm/DECXCPR.html
+//
+// Deprecated: use [RequestExtendedCursorPositionReport] instead.
+const RequestExtendedCursorPosition = "\x1b[?6n"
+
+// CursorUp (CUU) returns a sequence for moving the cursor up n cells.
+//
+// CSI n A
+//
+// See: https://vt100.net/docs/vt510-rm/CUU.html
+func CursorUp(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "A"
+}
+
+// CUU is an alias for [CursorUp].
+func CUU(n int) string {
+ return CursorUp(n)
+}
+
+// CUU1 is a sequence for moving the cursor up one cell.
+const CUU1 = "\x1b[A"
+
+// CursorUp1 is a sequence for moving the cursor up one cell.
+//
+// This is equivalent to CursorUp(1).
+//
+// Deprecated: use [CUU1] instead.
+const CursorUp1 = "\x1b[A"
+
+// CursorDown (CUD) returns a sequence for moving the cursor down n cells.
+//
+// CSI n B
+//
+// See: https://vt100.net/docs/vt510-rm/CUD.html
+func CursorDown(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "B"
+}
+
+// CUD is an alias for [CursorDown].
+func CUD(n int) string {
+ return CursorDown(n)
+}
+
+// CUD1 is a sequence for moving the cursor down one cell.
+const CUD1 = "\x1b[B"
+
+// CursorDown1 is a sequence for moving the cursor down one cell.
+//
+// This is equivalent to CursorDown(1).
+//
+// Deprecated: use [CUD1] instead.
+const CursorDown1 = "\x1b[B"
+
+// CursorForward (CUF) returns a sequence for moving the cursor right n cells.
+//
+// # CSI n C
+//
+// See: https://vt100.net/docs/vt510-rm/CUF.html
+func CursorForward(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "C"
+}
+
+// CUF is an alias for [CursorForward].
+func CUF(n int) string {
+ return CursorForward(n)
+}
+
+// CUF1 is a sequence for moving the cursor right one cell.
+const CUF1 = "\x1b[C"
+
+// CursorRight (CUF) returns a sequence for moving the cursor right n cells.
+//
+// CSI n C
+//
+// See: https://vt100.net/docs/vt510-rm/CUF.html
+//
+// Deprecated: use [CursorForward] instead.
+func CursorRight(n int) string {
+ return CursorForward(n)
+}
+
+// CursorRight1 is a sequence for moving the cursor right one cell.
+//
+// This is equivalent to CursorRight(1).
+//
+// Deprecated: use [CUF1] instead.
+const CursorRight1 = CUF1
+
+// CursorBackward (CUB) returns a sequence for moving the cursor left n cells.
+//
+// # CSI n D
+//
+// See: https://vt100.net/docs/vt510-rm/CUB.html
+func CursorBackward(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "D"
+}
+
+// CUB is an alias for [CursorBackward].
+func CUB(n int) string {
+ return CursorBackward(n)
+}
+
+// CUB1 is a sequence for moving the cursor left one cell.
+const CUB1 = "\x1b[D"
+
+// CursorLeft (CUB) returns a sequence for moving the cursor left n cells.
+//
+// CSI n D
+//
+// See: https://vt100.net/docs/vt510-rm/CUB.html
+//
+// Deprecated: use [CursorBackward] instead.
+func CursorLeft(n int) string {
+ return CursorBackward(n)
+}
+
+// CursorLeft1 is a sequence for moving the cursor left one cell.
+//
+// This is equivalent to CursorLeft(1).
+//
+// Deprecated: use [CUB1] instead.
+const CursorLeft1 = CUB1
+
+// CursorNextLine (CNL) returns a sequence for moving the cursor to the
+// beginning of the next line n times.
+//
+// CSI n E
+//
+// See: https://vt100.net/docs/vt510-rm/CNL.html
+func CursorNextLine(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "E"
+}
+
+// CNL is an alias for [CursorNextLine].
+func CNL(n int) string {
+ return CursorNextLine(n)
+}
+
+// CursorPreviousLine (CPL) returns a sequence for moving the cursor to the
+// beginning of the previous line n times.
+//
+// CSI n F
+//
+// See: https://vt100.net/docs/vt510-rm/CPL.html
+func CursorPreviousLine(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "F"
+}
+
+// CPL is an alias for [CursorPreviousLine].
+func CPL(n int) string {
+ return CursorPreviousLine(n)
+}
+
+// CursorHorizontalAbsolute (CHA) returns a sequence for moving the cursor to
+// the given column.
+//
+// Default is 1.
+//
+// CSI n G
+//
+// See: https://vt100.net/docs/vt510-rm/CHA.html
+func CursorHorizontalAbsolute(col int) string {
+ var s string
+ if col > 0 {
+ s = strconv.Itoa(col)
+ }
+ return "\x1b[" + s + "G"
+}
+
+// CHA is an alias for [CursorHorizontalAbsolute].
+func CHA(col int) string {
+ return CursorHorizontalAbsolute(col)
+}
+
+// CursorPosition (CUP) returns a sequence for setting the cursor to the
+// given row and column.
+//
+// Default is 1,1.
+//
+// CSI n ; m H
+//
+// See: https://vt100.net/docs/vt510-rm/CUP.html
+func CursorPosition(col, row int) string {
+ if row <= 0 && col <= 0 {
+ return HomeCursorPosition
+ }
+
+ var r, c string
+ if row > 0 {
+ r = strconv.Itoa(row)
+ }
+ if col > 0 {
+ c = strconv.Itoa(col)
+ }
+ return "\x1b[" + r + ";" + c + "H"
+}
+
+// CUP is an alias for [CursorPosition].
+func CUP(col, row int) string {
+ return CursorPosition(col, row)
+}
+
+// CursorHomePosition is a sequence for moving the cursor to the upper left
+// corner of the scrolling region. This is equivalent to `CursorPosition(1, 1)`.
+const CursorHomePosition = "\x1b[H"
+
+// SetCursorPosition (CUP) returns a sequence for setting the cursor to the
+// given row and column.
+//
+// CSI n ; m H
+//
+// See: https://vt100.net/docs/vt510-rm/CUP.html
+//
+// Deprecated: use [CursorPosition] instead.
+func SetCursorPosition(col, row int) string {
+ if row <= 0 && col <= 0 {
+ return HomeCursorPosition
+ }
+
+ var r, c string
+ if row > 0 {
+ r = strconv.Itoa(row)
+ }
+ if col > 0 {
+ c = strconv.Itoa(col)
+ }
+ return "\x1b[" + r + ";" + c + "H"
+}
+
+// HomeCursorPosition is a sequence for moving the cursor to the upper left
+// corner of the scrolling region. This is equivalent to `SetCursorPosition(1, 1)`.
+//
+// Deprecated: use [CursorHomePosition] instead.
+const HomeCursorPosition = CursorHomePosition
+
+// MoveCursor (CUP) returns a sequence for setting the cursor to the
+// given row and column.
+//
+// CSI n ; m H
+//
+// See: https://vt100.net/docs/vt510-rm/CUP.html
+//
+// Deprecated: use [CursorPosition] instead.
+func MoveCursor(col, row int) string {
+ return SetCursorPosition(col, row)
+}
+
+// CursorOrigin is a sequence for moving the cursor to the upper left corner of
+// the display. This is equivalent to `SetCursorPosition(1, 1)`.
+//
+// Deprecated: use [CursorHomePosition] instead.
+const CursorOrigin = "\x1b[1;1H"
+
+// MoveCursorOrigin is a sequence for moving the cursor to the upper left
+// corner of the display. This is equivalent to `SetCursorPosition(1, 1)`.
+//
+// Deprecated: use [CursorHomePosition] instead.
+const MoveCursorOrigin = CursorOrigin
+
+// CursorHorizontalForwardTab (CHT) returns a sequence for moving the cursor to
+// the next tab stop n times.
+//
+// Default is 1.
+//
+// CSI n I
+//
+// See: https://vt100.net/docs/vt510-rm/CHT.html
+func CursorHorizontalForwardTab(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "I"
+}
+
+// CHT is an alias for [CursorHorizontalForwardTab].
+func CHT(n int) string {
+ return CursorHorizontalForwardTab(n)
+}
+
+// EraseCharacter (ECH) returns a sequence for erasing n characters and moving
+// the cursor to the right. This doesn't affect other cell attributes.
+//
+// Default is 1.
+//
+// CSI n X
+//
+// See: https://vt100.net/docs/vt510-rm/ECH.html
+func EraseCharacter(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "X"
+}
+
+// ECH is an alias for [EraseCharacter].
+func ECH(n int) string {
+ return EraseCharacter(n)
+}
+
+// CursorBackwardTab (CBT) returns a sequence for moving the cursor to the
+// previous tab stop n times.
+//
+// Default is 1.
+//
+// CSI n Z
+//
+// See: https://vt100.net/docs/vt510-rm/CBT.html
+func CursorBackwardTab(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "Z"
+}
+
+// CBT is an alias for [CursorBackwardTab].
+func CBT(n int) string {
+ return CursorBackwardTab(n)
+}
+
+// VerticalPositionAbsolute (VPA) returns a sequence for moving the cursor to
+// the given row.
+//
+// Default is 1.
+//
+// CSI n d
+//
+// See: https://vt100.net/docs/vt510-rm/VPA.html
+func VerticalPositionAbsolute(row int) string {
+ var s string
+ if row > 0 {
+ s = strconv.Itoa(row)
+ }
+ return "\x1b[" + s + "d"
+}
+
+// VPA is an alias for [VerticalPositionAbsolute].
+func VPA(row int) string {
+ return VerticalPositionAbsolute(row)
+}
+
+// VerticalPositionRelative (VPR) returns a sequence for moving the cursor down
+// n rows relative to the current position.
+//
+// Default is 1.
+//
+// CSI n e
+//
+// See: https://vt100.net/docs/vt510-rm/VPR.html
+func VerticalPositionRelative(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "e"
+}
+
+// VPR is an alias for [VerticalPositionRelative].
+func VPR(n int) string {
+ return VerticalPositionRelative(n)
+}
+
+// HorizontalVerticalPosition (HVP) returns a sequence for moving the cursor to
+// the given row and column.
+//
+// Default is 1,1.
+//
+// CSI n ; m f
+//
+// This has the same effect as [CursorPosition].
+//
+// See: https://vt100.net/docs/vt510-rm/HVP.html
+func HorizontalVerticalPosition(col, row int) string {
+ var r, c string
+ if row > 0 {
+ r = strconv.Itoa(row)
+ }
+ if col > 0 {
+ c = strconv.Itoa(col)
+ }
+ return "\x1b[" + r + ";" + c + "f"
+}
+
+// HVP is an alias for [HorizontalVerticalPosition].
+func HVP(col, row int) string {
+ return HorizontalVerticalPosition(col, row)
+}
+
+// HorizontalVerticalHomePosition is a sequence for moving the cursor to the
+// upper left corner of the scrolling region. This is equivalent to
+// `HorizontalVerticalPosition(1, 1)`.
+const HorizontalVerticalHomePosition = "\x1b[f"
+
+// SaveCurrentCursorPosition (SCOSC) is a sequence for saving the current cursor
+// position for SCO console mode.
+//
+// CSI s
+//
+// This acts like [DECSC], except the page number where the cursor is located
+// is not saved.
+//
+// See: https://vt100.net/docs/vt510-rm/SCOSC.html
+const (
+ SaveCurrentCursorPosition = "\x1b[s"
+ SCOSC = SaveCurrentCursorPosition
+)
+
+// SaveCursorPosition (SCP or SCOSC) is a sequence for saving the cursor
+// position.
+//
+// CSI s
+//
+// This acts like Save, except the page number where the cursor is located is
+// not saved.
+//
+// See: https://vt100.net/docs/vt510-rm/SCOSC.html
+//
+// Deprecated: use [SaveCurrentCursorPosition] instead.
+const SaveCursorPosition = "\x1b[s"
+
+// RestoreCurrentCursorPosition (SCORC) is a sequence for restoring the current
+// cursor position for SCO console mode.
+//
+// CSI u
+//
+// This acts like [DECRC], except the page number where the cursor was saved is
+// not restored.
+//
+// See: https://vt100.net/docs/vt510-rm/SCORC.html
+const (
+ RestoreCurrentCursorPosition = "\x1b[u"
+ SCORC = RestoreCurrentCursorPosition
+)
+
+// RestoreCursorPosition (RCP or SCORC) is a sequence for restoring the cursor
+// position.
+//
+// CSI u
+//
+// This acts like Restore, except the cursor stays on the same page where the
+// cursor was saved.
+//
+// See: https://vt100.net/docs/vt510-rm/SCORC.html
+//
+// Deprecated: use [RestoreCurrentCursorPosition] instead.
+const RestoreCursorPosition = "\x1b[u"
+
+// SetCursorStyle (DECSCUSR) returns a sequence for changing the cursor style.
+//
+// Default is 1.
+//
+// CSI Ps SP q
+//
+// Where Ps is the cursor style:
+//
+// 0: Blinking block
+// 1: Blinking block (default)
+// 2: Steady block
+// 3: Blinking underline
+// 4: Steady underline
+// 5: Blinking bar (xterm)
+// 6: Steady bar (xterm)
+//
+// See: https://vt100.net/docs/vt510-rm/DECSCUSR.html
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81
+func SetCursorStyle(style int) string {
+ if style < 0 {
+ style = 0
+ }
+ return "\x1b[" + strconv.Itoa(style) + " q"
+}
+
+// DECSCUSR is an alias for [SetCursorStyle].
+func DECSCUSR(style int) string {
+ return SetCursorStyle(style)
+}
+
+// SetPointerShape returns a sequence for changing the mouse pointer cursor
+// shape. Use "default" for the default pointer shape.
+//
+// OSC 22 ; Pt ST
+// OSC 22 ; Pt BEL
+//
+// Where Pt is the pointer shape name. The name can be anything that the
+// operating system can understand. Some common names are:
+//
+// - copy
+// - crosshair
+// - default
+// - ew-resize
+// - n-resize
+// - text
+// - wait
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands
+func SetPointerShape(shape string) string {
+ return "\x1b]22;" + shape + "\x07"
+}
+
+// ReverseIndex (RI) is an escape sequence for moving the cursor up one line in
+// the same column. If the cursor is at the top margin, the screen scrolls
+// down.
+//
+// This has the same effect as [RI].
+const ReverseIndex = "\x1bM"
+
+// HorizontalPositionAbsolute (HPA) returns a sequence for moving the cursor to
+// the given column. This has the same effect as [CUP].
+//
+// Default is 1.
+//
+// CSI n `
+//
+// See: https://vt100.net/docs/vt510-rm/HPA.html
+func HorizontalPositionAbsolute(col int) string {
+ var s string
+ if col > 0 {
+ s = strconv.Itoa(col)
+ }
+ return "\x1b[" + s + "`"
+}
+
+// HPA is an alias for [HorizontalPositionAbsolute].
+func HPA(col int) string {
+ return HorizontalPositionAbsolute(col)
+}
+
+// HorizontalPositionRelative (HPR) returns a sequence for moving the cursor
+// right n columns relative to the current position. This has the same effect
+// as [CUP].
+//
+// Default is 1.
+//
+// CSI n a
+//
+// See: https://vt100.net/docs/vt510-rm/HPR.html
+func HorizontalPositionRelative(n int) string {
+ var s string
+ if n > 0 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "a"
+}
+
+// HPR is an alias for [HorizontalPositionRelative].
+func HPR(n int) string {
+ return HorizontalPositionRelative(n)
+}
+
+// Index (IND) is an escape sequence for moving the cursor down one line in the
+// same column. If the cursor is at the bottom margin, the screen scrolls up.
+// This has the same effect as [IND].
+const Index = "\x1bD"
diff --git a/vendor/github.com/charmbracelet/x/ansi/cwd.go b/vendor/github.com/charmbracelet/x/ansi/cwd.go
new file mode 100644
index 0000000..b03ac1b
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/cwd.go
@@ -0,0 +1,26 @@
+package ansi
+
+import (
+ "net/url"
+ "path"
+)
+
+// NotifyWorkingDirectory returns a sequence that notifies the terminal
+// of the current working directory.
+//
+// OSC 7 ; Pt BEL
+//
+// Where Pt is a URL in the format "file://[host]/[path]".
+// Set host to "localhost" if this is a path on the local computer.
+//
+// See: https://wezfurlong.org/wezterm/shell-integration.html#osc-7-escape-sequence-to-set-the-working-directory
+// See: https://iterm2.com/documentation-escape-codes.html#:~:text=RemoteHost%20and%20CurrentDir%3A-,OSC%207,-%3B%20%5BPs%5D%20ST
+func NotifyWorkingDirectory(host string, paths ...string) string {
+ path := path.Join(paths...)
+ u := &url.URL{
+ Scheme: "file",
+ Host: host,
+ Path: path,
+ }
+ return "\x1b]7;" + u.String() + "\x07"
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/doc.go b/vendor/github.com/charmbracelet/x/ansi/doc.go
new file mode 100644
index 0000000..e955e9f
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/doc.go
@@ -0,0 +1,7 @@
+// Package ansi defines common ANSI escape sequences based on the ECMA-48
+// specs.
+//
+// All sequences use 7-bit C1 control codes, which are supported by most
+// terminal emulators. OSC sequences are terminated by a BEL for wider
+// compatibility with terminals.
+package ansi
diff --git a/vendor/github.com/charmbracelet/x/ansi/focus.go b/vendor/github.com/charmbracelet/x/ansi/focus.go
new file mode 100644
index 0000000..4e0207c
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/focus.go
@@ -0,0 +1,9 @@
+package ansi
+
+// Focus is an escape sequence to notify the terminal that it has focus.
+// This is used with [FocusEventMode].
+const Focus = "\x1b[I"
+
+// Blur is an escape sequence to notify the terminal that it has lost focus.
+// This is used with [FocusEventMode].
+const Blur = "\x1b[O"
diff --git a/vendor/github.com/charmbracelet/x/ansi/graphics.go b/vendor/github.com/charmbracelet/x/ansi/graphics.go
new file mode 100644
index 0000000..604fef4
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/graphics.go
@@ -0,0 +1,199 @@
+package ansi
+
+import (
+ "bytes"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "image"
+ "io"
+ "os"
+ "strings"
+
+ "github.com/charmbracelet/x/ansi/kitty"
+)
+
+// KittyGraphics returns a sequence that encodes the given image in the Kitty
+// graphics protocol.
+//
+// APC G [comma separated options] ; [base64 encoded payload] ST
+//
+// See https://sw.kovidgoyal.net/kitty/graphics-protocol/
+func KittyGraphics(payload []byte, opts ...string) string {
+ var buf bytes.Buffer
+ buf.WriteString("\x1b_G")
+ buf.WriteString(strings.Join(opts, ","))
+ if len(payload) > 0 {
+ buf.WriteString(";")
+ buf.Write(payload)
+ }
+ buf.WriteString("\x1b\\")
+ return buf.String()
+}
+
+var (
+ // KittyGraphicsTempDir is the directory where temporary files are stored.
+ // This is used in [WriteKittyGraphics] along with [os.CreateTemp].
+ KittyGraphicsTempDir = ""
+
+ // KittyGraphicsTempPattern is the pattern used to create temporary files.
+ // This is used in [WriteKittyGraphics] along with [os.CreateTemp].
+ // The Kitty Graphics protocol requires the file path to contain the
+ // substring "tty-graphics-protocol".
+ KittyGraphicsTempPattern = "tty-graphics-protocol-*"
+)
+
+// WriteKittyGraphics writes an image using the Kitty Graphics protocol with
+// the given options to w. It chunks the written data if o.Chunk is true.
+//
+// You can omit m and use nil when rendering an image from a file. In this
+// case, you must provide a file path in o.File and use o.Transmission =
+// [kitty.File]. You can also use o.Transmission = [kitty.TempFile] to write
+// the image to a temporary file. In that case, the file path is ignored, and
+// the image is written to a temporary file that is automatically deleted by
+// the terminal.
+//
+// See https://sw.kovidgoyal.net/kitty/graphics-protocol/
+func WriteKittyGraphics(w io.Writer, m image.Image, o *kitty.Options) error {
+ if o == nil {
+ o = &kitty.Options{}
+ }
+
+ if o.Transmission == 0 && len(o.File) != 0 {
+ o.Transmission = kitty.File
+ }
+
+ var data bytes.Buffer // the data to be encoded into base64
+ e := &kitty.Encoder{
+ Compress: o.Compression == kitty.Zlib,
+ Format: o.Format,
+ }
+
+ switch o.Transmission {
+ case kitty.Direct:
+ if err := e.Encode(&data, m); err != nil {
+ return fmt.Errorf("failed to encode direct image: %w", err)
+ }
+
+ case kitty.SharedMemory:
+ // TODO: Implement shared memory
+ return fmt.Errorf("shared memory transmission is not yet implemented")
+
+ case kitty.File:
+ if len(o.File) == 0 {
+ return kitty.ErrMissingFile
+ }
+
+ f, err := os.Open(o.File)
+ if err != nil {
+ return fmt.Errorf("failed to open file: %w", err)
+ }
+
+ defer f.Close() //nolint:errcheck
+
+ stat, err := f.Stat()
+ if err != nil {
+ return fmt.Errorf("failed to get file info: %w", err)
+ }
+
+ mode := stat.Mode()
+ if !mode.IsRegular() {
+ return fmt.Errorf("file is not a regular file")
+ }
+
+ // Write the file path to the buffer
+ if _, err := data.WriteString(f.Name()); err != nil {
+ return fmt.Errorf("failed to write file path to buffer: %w", err)
+ }
+
+ case kitty.TempFile:
+ f, err := os.CreateTemp(KittyGraphicsTempDir, KittyGraphicsTempPattern)
+ if err != nil {
+ return fmt.Errorf("failed to create file: %w", err)
+ }
+
+ defer f.Close() //nolint:errcheck
+
+ if err := e.Encode(f, m); err != nil {
+ return fmt.Errorf("failed to encode image to file: %w", err)
+ }
+
+ // Write the file path to the buffer
+ if _, err := data.WriteString(f.Name()); err != nil {
+ return fmt.Errorf("failed to write file path to buffer: %w", err)
+ }
+ }
+
+ // Encode image to base64
+ var payload bytes.Buffer // the base64 encoded image to be written to w
+ b64 := base64.NewEncoder(base64.StdEncoding, &payload)
+ if _, err := data.WriteTo(b64); err != nil {
+ return fmt.Errorf("failed to write base64 encoded image to payload: %w", err)
+ }
+ if err := b64.Close(); err != nil {
+ return err
+ }
+
+ // If not chunking, write all at once
+ if !o.Chunk {
+ _, err := io.WriteString(w, KittyGraphics(payload.Bytes(), o.Options()...))
+ return err
+ }
+
+ // Write in chunks
+ var (
+ err error
+ n int
+ )
+ chunk := make([]byte, kitty.MaxChunkSize)
+ isFirstChunk := true
+
+ for {
+ // Stop if we read less than the chunk size [kitty.MaxChunkSize].
+ n, err = io.ReadFull(&payload, chunk)
+ if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ return fmt.Errorf("failed to read chunk: %w", err)
+ }
+
+ opts := buildChunkOptions(o, isFirstChunk, false)
+ if _, err := io.WriteString(w, KittyGraphics(chunk[:n], opts...)); err != nil {
+ return err
+ }
+
+ isFirstChunk = false
+ }
+
+ // Write the last chunk
+ opts := buildChunkOptions(o, isFirstChunk, true)
+ _, err = io.WriteString(w, KittyGraphics(chunk[:n], opts...))
+ return err
+}
+
+// buildChunkOptions creates the options slice for a chunk
+func buildChunkOptions(o *kitty.Options, isFirstChunk, isLastChunk bool) []string {
+ var opts []string
+ if isFirstChunk {
+ opts = o.Options()
+ } else {
+ // These options are allowed in subsequent chunks
+ if o.Quite > 0 {
+ opts = append(opts, fmt.Sprintf("q=%d", o.Quite))
+ }
+ if o.Action == kitty.Frame {
+ opts = append(opts, "a=f")
+ }
+ }
+
+ if !isFirstChunk || !isLastChunk {
+ // We don't need to encode the (m=) option when we only have one chunk.
+ if isLastChunk {
+ opts = append(opts, "m=0")
+ } else {
+ opts = append(opts, "m=1")
+ }
+ }
+ return opts
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/hyperlink.go b/vendor/github.com/charmbracelet/x/ansi/hyperlink.go
new file mode 100644
index 0000000..323bfe9
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/hyperlink.go
@@ -0,0 +1,28 @@
+package ansi
+
+import "strings"
+
+// SetHyperlink returns a sequence for starting a hyperlink.
+//
+// OSC 8 ; Params ; Uri ST
+// OSC 8 ; Params ; Uri BEL
+//
+// To reset the hyperlink, omit the URI.
+//
+// See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+func SetHyperlink(uri string, params ...string) string {
+ var p string
+ if len(params) > 0 {
+ p = strings.Join(params, ":")
+ }
+ return "\x1b]8;" + p + ";" + uri + "\x07"
+}
+
+// ResetHyperlink returns a sequence for resetting the hyperlink.
+//
+// This is equivalent to SetHyperlink("", params...).
+//
+// See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+func ResetHyperlink(params ...string) string {
+ return SetHyperlink("", params...)
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/iterm2.go b/vendor/github.com/charmbracelet/x/ansi/iterm2.go
new file mode 100644
index 0000000..0ecb336
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/iterm2.go
@@ -0,0 +1,18 @@
+package ansi
+
+import "fmt"
+
+// ITerm2 returns a sequence that uses the iTerm2 proprietary protocol. Use the
+// iterm2 package for a more convenient API.
+//
+// OSC 1337 ; key = value ST
+//
+// Example:
+//
+// ITerm2(iterm2.File{...})
+//
+// See https://iterm2.com/documentation-escape-codes.html
+// See https://iterm2.com/documentation-images.html
+func ITerm2(data any) string {
+ return "\x1b]1337;" + fmt.Sprint(data) + "\x07"
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/keypad.go b/vendor/github.com/charmbracelet/x/ansi/keypad.go
new file mode 100644
index 0000000..9183c6a
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/keypad.go
@@ -0,0 +1,28 @@
+package ansi
+
+// Keypad Application Mode (DECKPAM) is a mode that determines whether the
+// keypad sends application sequences or ANSI sequences.
+//
+// This works like enabling [DECNKM].
+// Use [NumericKeypadMode] to set the numeric keypad mode.
+//
+// ESC =
+//
+// See: https://vt100.net/docs/vt510-rm/DECKPAM.html
+const (
+ KeypadApplicationMode = "\x1b="
+ DECKPAM = KeypadApplicationMode
+)
+
+// Keypad Numeric Mode (DECKPNM) is a mode that determines whether the keypad
+// sends application sequences or ANSI sequences.
+//
+// This works the same as disabling [DECNKM].
+//
+// ESC >
+//
+// See: https://vt100.net/docs/vt510-rm/DECKPNM.html
+const (
+ KeypadNumericMode = "\x1b>"
+ DECKPNM = KeypadNumericMode
+)
diff --git a/vendor/github.com/charmbracelet/x/ansi/kitty.go b/vendor/github.com/charmbracelet/x/ansi/kitty.go
new file mode 100644
index 0000000..124ab83
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/kitty.go
@@ -0,0 +1,90 @@
+package ansi
+
+import "strconv"
+
+// Kitty keyboard protocol progressive enhancement flags.
+// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
+const (
+ KittyDisambiguateEscapeCodes = 1 << iota
+ KittyReportEventTypes
+ KittyReportAlternateKeys
+ KittyReportAllKeysAsEscapeCodes
+ KittyReportAssociatedKeys
+
+ KittyAllFlags = KittyDisambiguateEscapeCodes | KittyReportEventTypes |
+ KittyReportAlternateKeys | KittyReportAllKeysAsEscapeCodes | KittyReportAssociatedKeys
+)
+
+// RequestKittyKeyboard is a sequence to request the terminal Kitty keyboard
+// protocol enabled flags.
+//
+// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
+const RequestKittyKeyboard = "\x1b[?u"
+
+// KittyKeyboard returns a sequence to request keyboard enhancements from the terminal.
+// The flags argument is a bitmask of the Kitty keyboard protocol flags. While
+// mode specifies how the flags should be interpreted.
+//
+// Possible values for flags mask:
+//
+// 1: Disambiguate escape codes
+// 2: Report event types
+// 4: Report alternate keys
+// 8: Report all keys as escape codes
+// 16: Report associated text
+//
+// Possible values for mode:
+//
+// 1: Set given flags and unset all others
+// 2: Set given flags and keep existing flags unchanged
+// 3: Unset given flags and keep existing flags unchanged
+//
+// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
+func KittyKeyboard(flags, mode int) string {
+ return "\x1b[=" + strconv.Itoa(flags) + ";" + strconv.Itoa(mode) + "u"
+}
+
+// PushKittyKeyboard returns a sequence to push the given flags to the terminal
+// Kitty Keyboard stack.
+//
+// Possible values for flags mask:
+//
+// 0: Disable all features
+// 1: Disambiguate escape codes
+// 2: Report event types
+// 4: Report alternate keys
+// 8: Report all keys as escape codes
+// 16: Report associated text
+//
+// CSI > flags u
+//
+// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
+func PushKittyKeyboard(flags int) string {
+ var f string
+ if flags > 0 {
+ f = strconv.Itoa(flags)
+ }
+
+ return "\x1b[>" + f + "u"
+}
+
+// DisableKittyKeyboard is a sequence to push zero into the terminal Kitty
+// Keyboard stack to disable the protocol.
+//
+// This is equivalent to PushKittyKeyboard(0).
+const DisableKittyKeyboard = "\x1b[>u"
+
+// PopKittyKeyboard returns a sequence to pop n number of flags from the
+// terminal Kitty Keyboard stack.
+//
+// CSI < flags u
+//
+// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
+func PopKittyKeyboard(n int) string {
+ var num string
+ if n > 0 {
+ num = strconv.Itoa(n)
+ }
+
+ return "\x1b[<" + num + "u"
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/kitty/decoder.go b/vendor/github.com/charmbracelet/x/ansi/kitty/decoder.go
new file mode 100644
index 0000000..fbd0844
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/kitty/decoder.go
@@ -0,0 +1,85 @@
+package kitty
+
+import (
+ "compress/zlib"
+ "fmt"
+ "image"
+ "image/color"
+ "image/png"
+ "io"
+)
+
+// Decoder is a decoder for the Kitty graphics protocol. It supports decoding
+// images in the 24-bit [RGB], 32-bit [RGBA], and [PNG] formats. It can also
+// decompress data using zlib.
+// The default format is 32-bit [RGBA].
+type Decoder struct {
+ // Uses zlib decompression.
+ Decompress bool
+
+ // Can be one of [RGB], [RGBA], or [PNG].
+ Format int
+
+ // Width of the image in pixels. This can be omitted if the image is [PNG]
+ // formatted.
+ Width int
+
+ // Height of the image in pixels. This can be omitted if the image is [PNG]
+ // formatted.
+ Height int
+}
+
+// Decode decodes the image data from r in the specified format.
+func (d *Decoder) Decode(r io.Reader) (image.Image, error) {
+ if d.Decompress {
+ zr, err := zlib.NewReader(r)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create zlib reader: %w", err)
+ }
+
+ defer zr.Close() //nolint:errcheck
+ r = zr
+ }
+
+ if d.Format == 0 {
+ d.Format = RGBA
+ }
+
+ switch d.Format {
+ case RGBA, RGB:
+ return d.decodeRGBA(r, d.Format == RGBA)
+
+ case PNG:
+ return png.Decode(r)
+
+ default:
+ return nil, fmt.Errorf("unsupported format: %d", d.Format)
+ }
+}
+
+// decodeRGBA decodes the image data in 32-bit RGBA or 24-bit RGB formats.
+func (d *Decoder) decodeRGBA(r io.Reader, alpha bool) (image.Image, error) {
+ m := image.NewRGBA(image.Rect(0, 0, d.Width, d.Height))
+
+ var buf []byte
+ if alpha {
+ buf = make([]byte, 4)
+ } else {
+ buf = make([]byte, 3)
+ }
+
+ for y := 0; y < d.Height; y++ {
+ for x := 0; x < d.Width; x++ {
+ if _, err := io.ReadFull(r, buf[:]); err != nil {
+ return nil, fmt.Errorf("failed to read pixel data: %w", err)
+ }
+ if alpha {
+ m.SetRGBA(x, y, color.RGBA{buf[0], buf[1], buf[2], buf[3]})
+ } else {
+ m.SetRGBA(x, y, color.RGBA{buf[0], buf[1], buf[2], 0xff})
+ }
+ }
+ }
+
+ return m, nil
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/kitty/encoder.go b/vendor/github.com/charmbracelet/x/ansi/kitty/encoder.go
new file mode 100644
index 0000000..f668b9e
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/kitty/encoder.go
@@ -0,0 +1,64 @@
+package kitty
+
+import (
+ "compress/zlib"
+ "fmt"
+ "image"
+ "image/png"
+ "io"
+)
+
+// Encoder is an encoder for the Kitty graphics protocol. It supports encoding
+// images in the 24-bit [RGB], 32-bit [RGBA], and [PNG] formats, and
+// compressing the data using zlib.
+// The default format is 32-bit [RGBA].
+type Encoder struct {
+ // Uses zlib compression.
+ Compress bool
+
+ // Can be one of [RGBA], [RGB], or [PNG].
+ Format int
+}
+
+// Encode encodes the image data in the specified format and writes it to w.
+func (e *Encoder) Encode(w io.Writer, m image.Image) error {
+ if m == nil {
+ return nil
+ }
+
+ if e.Compress {
+ zw := zlib.NewWriter(w)
+ defer zw.Close() //nolint:errcheck
+ w = zw
+ }
+
+ if e.Format == 0 {
+ e.Format = RGBA
+ }
+
+ switch e.Format {
+ case RGBA, RGB:
+ bounds := m.Bounds()
+ for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
+ for x := bounds.Min.X; x < bounds.Max.X; x++ {
+ r, g, b, a := m.At(x, y).RGBA()
+ switch e.Format {
+ case RGBA:
+ w.Write([]byte{byte(r >> 8), byte(g >> 8), byte(b >> 8), byte(a >> 8)}) //nolint:errcheck
+ case RGB:
+ w.Write([]byte{byte(r >> 8), byte(g >> 8), byte(b >> 8)}) //nolint:errcheck
+ }
+ }
+ }
+
+ case PNG:
+ if err := png.Encode(w, m); err != nil {
+ return fmt.Errorf("failed to encode PNG: %w", err)
+ }
+
+ default:
+ return fmt.Errorf("unsupported format: %d", e.Format)
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/kitty/graphics.go b/vendor/github.com/charmbracelet/x/ansi/kitty/graphics.go
new file mode 100644
index 0000000..490e7a8
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/kitty/graphics.go
@@ -0,0 +1,414 @@
+package kitty
+
+import "errors"
+
+// ErrMissingFile is returned when the file path is missing.
+var ErrMissingFile = errors.New("missing file path")
+
+// MaxChunkSize is the maximum chunk size for the image data.
+const MaxChunkSize = 1024 * 4
+
+// Placeholder is a special Unicode character that can be used as a placeholder
+// for an image.
+const Placeholder = '\U0010EEEE'
+
+// Graphics image format.
+const (
+ // 32-bit RGBA format.
+ RGBA = 32
+
+ // 24-bit RGB format.
+ RGB = 24
+
+ // PNG format.
+ PNG = 100
+)
+
+// Compression types.
+const (
+ Zlib = 'z'
+)
+
+// Transmission types.
+const (
+ // The data transmitted directly in the escape sequence.
+ Direct = 'd'
+
+ // The data transmitted in a regular file.
+ File = 'f'
+
+ // A temporary file is used and deleted after transmission.
+ TempFile = 't'
+
+ // A shared memory object.
+ // For POSIX see https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html
+ // For Windows see https://docs.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory
+ SharedMemory = 's'
+)
+
+// Action types.
+const (
+ // Transmit image data.
+ Transmit = 't'
+ // TransmitAndPut transmit image data and display (put) it.
+ TransmitAndPut = 'T'
+ // Query terminal for image info.
+ Query = 'q'
+ // Put (display) previously transmitted image.
+ Put = 'p'
+ // Delete image.
+ Delete = 'd'
+ // Frame transmits data for animation frames.
+ Frame = 'f'
+ // Animate controls animation.
+ Animate = 'a'
+ // Compose composes animation frames.
+ Compose = 'c'
+)
+
+// Delete types.
+const (
+ // Delete all placements visible on screen
+ DeleteAll = 'a'
+ // Delete all images with the specified id, specified using the i key. If
+ // you specify a p key for the placement id as well, then only the
+ // placement with the specified image id and placement id will be deleted.
+ DeleteID = 'i'
+ // Delete newest image with the specified number, specified using the I
+ // key. If you specify a p key for the placement id as well, then only the
+ // placement with the specified number and placement id will be deleted.
+ DeleteNumber = 'n'
+ // Delete all placements that intersect with the current cursor position.
+ DeleteCursor = 'c'
+ // Delete animation frames.
+ DeleteFrames = 'f'
+ // Delete all placements that intersect a specific cell, the cell is
+ // specified using the x and y keys
+ DeleteCell = 'p'
+ // Delete all placements that intersect a specific cell having a specific
+ // z-index. The cell and z-index is specified using the x, y and z keys.
+ DeleteCellZ = 'q'
+ // Delete all images whose id is greater than or equal to the value of the x
+ // key and less than or equal to the value of the y.
+ DeleteRange = 'r'
+ // Delete all placements that intersect the specified column, specified using
+ // the x key.
+ DeleteColumn = 'x'
+ // Delete all placements that intersect the specified row, specified using
+ // the y key.
+ DeleteRow = 'y'
+ // Delete all placements that have the specified z-index, specified using the
+ // z key.
+ DeleteZ = 'z'
+)
+
+// Diacritic returns the diacritic rune at the specified index. If the index is
+// out of bounds, the first diacritic rune is returned.
+func Diacritic(i int) rune {
+ if i < 0 || i >= len(diacritics) {
+ return diacritics[0]
+ }
+ return diacritics[i]
+}
+
+// From https://sw.kovidgoyal.net/kitty/_downloads/f0a0de9ec8d9ff4456206db8e0814937/rowcolumn-diacritics.txt
+// See https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders for further explanation.
+var diacritics = []rune{
+ '\u0305',
+ '\u030D',
+ '\u030E',
+ '\u0310',
+ '\u0312',
+ '\u033D',
+ '\u033E',
+ '\u033F',
+ '\u0346',
+ '\u034A',
+ '\u034B',
+ '\u034C',
+ '\u0350',
+ '\u0351',
+ '\u0352',
+ '\u0357',
+ '\u035B',
+ '\u0363',
+ '\u0364',
+ '\u0365',
+ '\u0366',
+ '\u0367',
+ '\u0368',
+ '\u0369',
+ '\u036A',
+ '\u036B',
+ '\u036C',
+ '\u036D',
+ '\u036E',
+ '\u036F',
+ '\u0483',
+ '\u0484',
+ '\u0485',
+ '\u0486',
+ '\u0487',
+ '\u0592',
+ '\u0593',
+ '\u0594',
+ '\u0595',
+ '\u0597',
+ '\u0598',
+ '\u0599',
+ '\u059C',
+ '\u059D',
+ '\u059E',
+ '\u059F',
+ '\u05A0',
+ '\u05A1',
+ '\u05A8',
+ '\u05A9',
+ '\u05AB',
+ '\u05AC',
+ '\u05AF',
+ '\u05C4',
+ '\u0610',
+ '\u0611',
+ '\u0612',
+ '\u0613',
+ '\u0614',
+ '\u0615',
+ '\u0616',
+ '\u0617',
+ '\u0657',
+ '\u0658',
+ '\u0659',
+ '\u065A',
+ '\u065B',
+ '\u065D',
+ '\u065E',
+ '\u06D6',
+ '\u06D7',
+ '\u06D8',
+ '\u06D9',
+ '\u06DA',
+ '\u06DB',
+ '\u06DC',
+ '\u06DF',
+ '\u06E0',
+ '\u06E1',
+ '\u06E2',
+ '\u06E4',
+ '\u06E7',
+ '\u06E8',
+ '\u06EB',
+ '\u06EC',
+ '\u0730',
+ '\u0732',
+ '\u0733',
+ '\u0735',
+ '\u0736',
+ '\u073A',
+ '\u073D',
+ '\u073F',
+ '\u0740',
+ '\u0741',
+ '\u0743',
+ '\u0745',
+ '\u0747',
+ '\u0749',
+ '\u074A',
+ '\u07EB',
+ '\u07EC',
+ '\u07ED',
+ '\u07EE',
+ '\u07EF',
+ '\u07F0',
+ '\u07F1',
+ '\u07F3',
+ '\u0816',
+ '\u0817',
+ '\u0818',
+ '\u0819',
+ '\u081B',
+ '\u081C',
+ '\u081D',
+ '\u081E',
+ '\u081F',
+ '\u0820',
+ '\u0821',
+ '\u0822',
+ '\u0823',
+ '\u0825',
+ '\u0826',
+ '\u0827',
+ '\u0829',
+ '\u082A',
+ '\u082B',
+ '\u082C',
+ '\u082D',
+ '\u0951',
+ '\u0953',
+ '\u0954',
+ '\u0F82',
+ '\u0F83',
+ '\u0F86',
+ '\u0F87',
+ '\u135D',
+ '\u135E',
+ '\u135F',
+ '\u17DD',
+ '\u193A',
+ '\u1A17',
+ '\u1A75',
+ '\u1A76',
+ '\u1A77',
+ '\u1A78',
+ '\u1A79',
+ '\u1A7A',
+ '\u1A7B',
+ '\u1A7C',
+ '\u1B6B',
+ '\u1B6D',
+ '\u1B6E',
+ '\u1B6F',
+ '\u1B70',
+ '\u1B71',
+ '\u1B72',
+ '\u1B73',
+ '\u1CD0',
+ '\u1CD1',
+ '\u1CD2',
+ '\u1CDA',
+ '\u1CDB',
+ '\u1CE0',
+ '\u1DC0',
+ '\u1DC1',
+ '\u1DC3',
+ '\u1DC4',
+ '\u1DC5',
+ '\u1DC6',
+ '\u1DC7',
+ '\u1DC8',
+ '\u1DC9',
+ '\u1DCB',
+ '\u1DCC',
+ '\u1DD1',
+ '\u1DD2',
+ '\u1DD3',
+ '\u1DD4',
+ '\u1DD5',
+ '\u1DD6',
+ '\u1DD7',
+ '\u1DD8',
+ '\u1DD9',
+ '\u1DDA',
+ '\u1DDB',
+ '\u1DDC',
+ '\u1DDD',
+ '\u1DDE',
+ '\u1DDF',
+ '\u1DE0',
+ '\u1DE1',
+ '\u1DE2',
+ '\u1DE3',
+ '\u1DE4',
+ '\u1DE5',
+ '\u1DE6',
+ '\u1DFE',
+ '\u20D0',
+ '\u20D1',
+ '\u20D4',
+ '\u20D5',
+ '\u20D6',
+ '\u20D7',
+ '\u20DB',
+ '\u20DC',
+ '\u20E1',
+ '\u20E7',
+ '\u20E9',
+ '\u20F0',
+ '\u2CEF',
+ '\u2CF0',
+ '\u2CF1',
+ '\u2DE0',
+ '\u2DE1',
+ '\u2DE2',
+ '\u2DE3',
+ '\u2DE4',
+ '\u2DE5',
+ '\u2DE6',
+ '\u2DE7',
+ '\u2DE8',
+ '\u2DE9',
+ '\u2DEA',
+ '\u2DEB',
+ '\u2DEC',
+ '\u2DED',
+ '\u2DEE',
+ '\u2DEF',
+ '\u2DF0',
+ '\u2DF1',
+ '\u2DF2',
+ '\u2DF3',
+ '\u2DF4',
+ '\u2DF5',
+ '\u2DF6',
+ '\u2DF7',
+ '\u2DF8',
+ '\u2DF9',
+ '\u2DFA',
+ '\u2DFB',
+ '\u2DFC',
+ '\u2DFD',
+ '\u2DFE',
+ '\u2DFF',
+ '\uA66F',
+ '\uA67C',
+ '\uA67D',
+ '\uA6F0',
+ '\uA6F1',
+ '\uA8E0',
+ '\uA8E1',
+ '\uA8E2',
+ '\uA8E3',
+ '\uA8E4',
+ '\uA8E5',
+ '\uA8E6',
+ '\uA8E7',
+ '\uA8E8',
+ '\uA8E9',
+ '\uA8EA',
+ '\uA8EB',
+ '\uA8EC',
+ '\uA8ED',
+ '\uA8EE',
+ '\uA8EF',
+ '\uA8F0',
+ '\uA8F1',
+ '\uAAB0',
+ '\uAAB2',
+ '\uAAB3',
+ '\uAAB7',
+ '\uAAB8',
+ '\uAABE',
+ '\uAABF',
+ '\uAAC1',
+ '\uFE20',
+ '\uFE21',
+ '\uFE22',
+ '\uFE23',
+ '\uFE24',
+ '\uFE25',
+ '\uFE26',
+ '\U00010A0F',
+ '\U00010A38',
+ '\U0001D185',
+ '\U0001D186',
+ '\U0001D187',
+ '\U0001D188',
+ '\U0001D189',
+ '\U0001D1AA',
+ '\U0001D1AB',
+ '\U0001D1AC',
+ '\U0001D1AD',
+ '\U0001D242',
+ '\U0001D243',
+ '\U0001D244',
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/kitty/options.go b/vendor/github.com/charmbracelet/x/ansi/kitty/options.go
new file mode 100644
index 0000000..a8d907b
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/kitty/options.go
@@ -0,0 +1,367 @@
+package kitty
+
+import (
+ "encoding"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+var (
+ _ encoding.TextMarshaler = Options{}
+ _ encoding.TextUnmarshaler = &Options{}
+)
+
+// Options represents a Kitty Graphics Protocol options.
+type Options struct {
+ // Common options.
+
+ // Action (a=t) is the action to be performed on the image. Can be one of
+ // [Transmit], [TransmitDisplay], [Query], [Put], [Delete], [Frame],
+ // [Animate], [Compose].
+ Action byte
+
+ // Quite mode (q=0) is the quiet mode. Can be either zero, one, or two
+ // where zero is the default, 1 suppresses OK responses, and 2 suppresses
+ // both OK and error responses.
+ Quite byte
+
+ // Transmission options.
+
+ // ID (i=) is the image ID. The ID is a unique identifier for the image.
+ // Must be a positive integer up to [math.MaxUint32].
+ ID int
+
+ // PlacementID (p=) is the placement ID. The placement ID is a unique
+ // identifier for the placement of the image. Must be a positive integer up
+ // to [math.MaxUint32].
+ PlacementID int
+
+ // Number (I=0) is the number of images to be transmitted.
+ Number int
+
+ // Format (f=32) is the image format. One of [RGBA], [RGB], [PNG].
+ Format int
+
+ // ImageWidth (s=0) is the transmitted image width.
+ ImageWidth int
+
+ // ImageHeight (v=0) is the transmitted image height.
+ ImageHeight int
+
+ // Compression (o=) is the image compression type. Can be [Zlib] or zero.
+ Compression byte
+
+ // Transmission (t=d) is the image transmission type. Can be [Direct], [File],
+ // [TempFile], or[SharedMemory].
+ Transmission byte
+
+ // File is the file path to be used when the transmission type is [File].
+ // If [Options.Transmission] is omitted i.e. zero and this is non-empty,
+ // the transmission type is set to [File].
+ File string
+
+ // Size (S=0) is the size to be read from the transmission medium.
+ Size int
+
+ // Offset (O=0) is the offset byte to start reading from the transmission
+ // medium.
+ Offset int
+
+ // Chunk (m=) whether the image is transmitted in chunks. Can be either
+ // zero or one. When true, the image is transmitted in chunks. Each chunk
+ // must be a multiple of 4, and up to [MaxChunkSize] bytes. Each chunk must
+ // have the m=1 option except for the last chunk which must have m=0.
+ Chunk bool
+
+ // Display options.
+
+ // X (x=0) is the pixel X coordinate of the image to start displaying.
+ X int
+
+ // Y (y=0) is the pixel Y coordinate of the image to start displaying.
+ Y int
+
+ // Z (z=0) is the Z coordinate of the image to display.
+ Z int
+
+ // Width (w=0) is the width of the image to display.
+ Width int
+
+ // Height (h=0) is the height of the image to display.
+ Height int
+
+ // OffsetX (X=0) is the OffsetX coordinate of the cursor cell to start
+ // displaying the image. OffsetX=0 is the leftmost cell. This must be
+ // smaller than the terminal cell width.
+ OffsetX int
+
+ // OffsetY (Y=0) is the OffsetY coordinate of the cursor cell to start
+ // displaying the image. OffsetY=0 is the topmost cell. This must be
+ // smaller than the terminal cell height.
+ OffsetY int
+
+ // Columns (c=0) is the number of columns to display the image. The image
+ // will be scaled to fit the number of columns.
+ Columns int
+
+ // Rows (r=0) is the number of rows to display the image. The image will be
+ // scaled to fit the number of rows.
+ Rows int
+
+ // VirtualPlacement (U=0) whether to use virtual placement. This is used
+ // with Unicode [Placeholder] to display images.
+ VirtualPlacement bool
+
+ // DoNotMoveCursor (C=0) whether to move the cursor after displaying the
+ // image.
+ DoNotMoveCursor bool
+
+ // ParentID (P=0) is the parent image ID. The parent ID is the ID of the
+ // image that is the parent of the current image. This is used with Unicode
+ // [Placeholder] to display images relative to the parent image.
+ ParentID int
+
+ // ParentPlacementID (Q=0) is the parent placement ID. The parent placement
+ // ID is the ID of the placement of the parent image. This is used with
+ // Unicode [Placeholder] to display images relative to the parent image.
+ ParentPlacementID int
+
+ // Delete options.
+
+ // Delete (d=a) is the delete action. Can be one of [DeleteAll],
+ // [DeleteID], [DeleteNumber], [DeleteCursor], [DeleteFrames],
+ // [DeleteCell], [DeleteCellZ], [DeleteRange], [DeleteColumn], [DeleteRow],
+ // [DeleteZ].
+ Delete byte
+
+ // DeleteResources indicates whether to delete the resources associated
+ // with the image.
+ DeleteResources bool
+}
+
+// Options returns the options as a slice of a key-value pairs.
+func (o *Options) Options() (opts []string) {
+ opts = []string{}
+ if o.Format == 0 {
+ o.Format = RGBA
+ }
+
+ if o.Action == 0 {
+ o.Action = Transmit
+ }
+
+ if o.Delete == 0 {
+ o.Delete = DeleteAll
+ }
+
+ if o.Transmission == 0 {
+ if len(o.File) > 0 {
+ o.Transmission = File
+ } else {
+ o.Transmission = Direct
+ }
+ }
+
+ if o.Format != RGBA {
+ opts = append(opts, fmt.Sprintf("f=%d", o.Format))
+ }
+
+ if o.Quite > 0 {
+ opts = append(opts, fmt.Sprintf("q=%d", o.Quite))
+ }
+
+ if o.ID > 0 {
+ opts = append(opts, fmt.Sprintf("i=%d", o.ID))
+ }
+
+ if o.PlacementID > 0 {
+ opts = append(opts, fmt.Sprintf("p=%d", o.PlacementID))
+ }
+
+ if o.Number > 0 {
+ opts = append(opts, fmt.Sprintf("I=%d", o.Number))
+ }
+
+ if o.ImageWidth > 0 {
+ opts = append(opts, fmt.Sprintf("s=%d", o.ImageWidth))
+ }
+
+ if o.ImageHeight > 0 {
+ opts = append(opts, fmt.Sprintf("v=%d", o.ImageHeight))
+ }
+
+ if o.Transmission != Direct {
+ opts = append(opts, fmt.Sprintf("t=%c", o.Transmission))
+ }
+
+ if o.Size > 0 {
+ opts = append(opts, fmt.Sprintf("S=%d", o.Size))
+ }
+
+ if o.Offset > 0 {
+ opts = append(opts, fmt.Sprintf("O=%d", o.Offset))
+ }
+
+ if o.Compression == Zlib {
+ opts = append(opts, fmt.Sprintf("o=%c", o.Compression))
+ }
+
+ if o.VirtualPlacement {
+ opts = append(opts, "U=1")
+ }
+
+ if o.DoNotMoveCursor {
+ opts = append(opts, "C=1")
+ }
+
+ if o.ParentID > 0 {
+ opts = append(opts, fmt.Sprintf("P=%d", o.ParentID))
+ }
+
+ if o.ParentPlacementID > 0 {
+ opts = append(opts, fmt.Sprintf("Q=%d", o.ParentPlacementID))
+ }
+
+ if o.X > 0 {
+ opts = append(opts, fmt.Sprintf("x=%d", o.X))
+ }
+
+ if o.Y > 0 {
+ opts = append(opts, fmt.Sprintf("y=%d", o.Y))
+ }
+
+ if o.Z > 0 {
+ opts = append(opts, fmt.Sprintf("z=%d", o.Z))
+ }
+
+ if o.Width > 0 {
+ opts = append(opts, fmt.Sprintf("w=%d", o.Width))
+ }
+
+ if o.Height > 0 {
+ opts = append(opts, fmt.Sprintf("h=%d", o.Height))
+ }
+
+ if o.OffsetX > 0 {
+ opts = append(opts, fmt.Sprintf("X=%d", o.OffsetX))
+ }
+
+ if o.OffsetY > 0 {
+ opts = append(opts, fmt.Sprintf("Y=%d", o.OffsetY))
+ }
+
+ if o.Columns > 0 {
+ opts = append(opts, fmt.Sprintf("c=%d", o.Columns))
+ }
+
+ if o.Rows > 0 {
+ opts = append(opts, fmt.Sprintf("r=%d", o.Rows))
+ }
+
+ if o.Delete != DeleteAll || o.DeleteResources {
+ da := o.Delete
+ if o.DeleteResources {
+ da = da - ' ' // to uppercase
+ }
+
+ opts = append(opts, fmt.Sprintf("d=%c", da))
+ }
+
+ if o.Action != Transmit {
+ opts = append(opts, fmt.Sprintf("a=%c", o.Action))
+ }
+
+ return
+}
+
+// String returns the string representation of the options.
+func (o Options) String() string {
+ return strings.Join(o.Options(), ",")
+}
+
+// MarshalText returns the string representation of the options.
+func (o Options) MarshalText() ([]byte, error) {
+ return []byte(o.String()), nil
+}
+
+// UnmarshalText parses the options from the given string.
+func (o *Options) UnmarshalText(text []byte) error {
+ opts := strings.Split(string(text), ",")
+ for _, opt := range opts {
+ ps := strings.SplitN(opt, "=", 2)
+ if len(ps) != 2 || len(ps[1]) == 0 {
+ continue
+ }
+
+ switch ps[0] {
+ case "a":
+ o.Action = ps[1][0]
+ case "o":
+ o.Compression = ps[1][0]
+ case "t":
+ o.Transmission = ps[1][0]
+ case "d":
+ d := ps[1][0]
+ if d >= 'A' && d <= 'Z' {
+ o.DeleteResources = true
+ d = d + ' ' // to lowercase
+ }
+ o.Delete = d
+ case "i", "q", "p", "I", "f", "s", "v", "S", "O", "m", "x", "y", "z", "w", "h", "X", "Y", "c", "r", "U", "P", "Q":
+ v, err := strconv.Atoi(ps[1])
+ if err != nil {
+ continue
+ }
+
+ switch ps[0] {
+ case "i":
+ o.ID = v
+ case "q":
+ o.Quite = byte(v)
+ case "p":
+ o.PlacementID = v
+ case "I":
+ o.Number = v
+ case "f":
+ o.Format = v
+ case "s":
+ o.ImageWidth = v
+ case "v":
+ o.ImageHeight = v
+ case "S":
+ o.Size = v
+ case "O":
+ o.Offset = v
+ case "m":
+ o.Chunk = v == 0 || v == 1
+ case "x":
+ o.X = v
+ case "y":
+ o.Y = v
+ case "z":
+ o.Z = v
+ case "w":
+ o.Width = v
+ case "h":
+ o.Height = v
+ case "X":
+ o.OffsetX = v
+ case "Y":
+ o.OffsetY = v
+ case "c":
+ o.Columns = v
+ case "r":
+ o.Rows = v
+ case "U":
+ o.VirtualPlacement = v == 1
+ case "P":
+ o.ParentID = v
+ case "Q":
+ o.ParentPlacementID = v
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/method.go b/vendor/github.com/charmbracelet/x/ansi/method.go
new file mode 100644
index 0000000..0218809
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/method.go
@@ -0,0 +1,172 @@
+package ansi
+
+// Method is a type that represents the how the renderer should calculate the
+// display width of cells.
+type Method uint8
+
+// Display width modes.
+const (
+ WcWidth Method = iota
+ GraphemeWidth
+)
+
+// StringWidth returns the width of a string in cells. This is the number of
+// cells that the string will occupy when printed in a terminal. ANSI escape
+// codes are ignored and wide characters (such as East Asians and emojis) are
+// accounted for.
+func (m Method) StringWidth(s string) int {
+ return stringWidth(m, s)
+}
+
+// Truncate truncates a string to a given length, adding a tail to the end if
+// the string is longer than the given length. This function is aware of ANSI
+// escape codes and will not break them, and accounts for wide-characters (such
+// as East-Asian characters and emojis).
+func (m Method) Truncate(s string, length int, tail string) string {
+ return truncate(m, s, length, tail)
+}
+
+// TruncateLeft truncates a string to a given length, adding a prefix to the
+// beginning if the string is longer than the given length. This function is
+// aware of ANSI escape codes and will not break them, and accounts for
+// wide-characters (such as East-Asian characters and emojis).
+func (m Method) TruncateLeft(s string, length int, prefix string) string {
+ return truncateLeft(m, s, length, prefix)
+}
+
+// Cut the string, without adding any prefix or tail strings. This function is
+// aware of ANSI escape codes and will not break them, and accounts for
+// wide-characters (such as East-Asian characters and emojis). Note that the
+// [left] parameter is inclusive, while [right] isn't.
+func (m Method) Cut(s string, left, right int) string {
+ return cut(m, s, left, right)
+}
+
+// Hardwrap wraps a string or a block of text to a given line length, breaking
+// word boundaries. This will preserve ANSI escape codes and will account for
+// wide-characters in the string.
+// When preserveSpace is true, spaces at the beginning of a line will be
+// preserved.
+// This treats the text as a sequence of graphemes.
+func (m Method) Hardwrap(s string, length int, preserveSpace bool) string {
+ return hardwrap(m, s, length, preserveSpace)
+}
+
+// Wordwrap wraps a string or a block of text to a given line length, not
+// breaking word boundaries. This will preserve ANSI escape codes and will
+// account for wide-characters in the string.
+// The breakpoints string is a list of characters that are considered
+// breakpoints for word wrapping. A hyphen (-) is always considered a
+// breakpoint.
+//
+// Note: breakpoints must be a string of 1-cell wide rune characters.
+func (m Method) Wordwrap(s string, length int, breakpoints string) string {
+ return wordwrap(m, s, length, breakpoints)
+}
+
+// Wrap wraps a string or a block of text to a given line length, breaking word
+// boundaries if necessary. This will preserve ANSI escape codes and will
+// account for wide-characters in the string. The breakpoints string is a list
+// of characters that are considered breakpoints for word wrapping. A hyphen
+// (-) is always considered a breakpoint.
+//
+// Note: breakpoints must be a string of 1-cell wide rune characters.
+func (m Method) Wrap(s string, length int, breakpoints string) string {
+ return wrap(m, s, length, breakpoints)
+}
+
+// DecodeSequence decodes the first ANSI escape sequence or a printable
+// grapheme from the given data. It returns the sequence slice, the number of
+// bytes read, the cell width for each sequence, and the new state.
+//
+// The cell width will always be 0 for control and escape sequences, 1 for
+// ASCII printable characters, and the number of cells other Unicode characters
+// occupy. It uses the uniseg package to calculate the width of Unicode
+// graphemes and characters. This means it will always do grapheme clustering
+// (mode 2027).
+//
+// Passing a non-nil [*Parser] as the last argument will allow the decoder to
+// collect sequence parameters, data, and commands. The parser cmd will have
+// the packed command value that contains intermediate and prefix characters.
+// In the case of a OSC sequence, the cmd will be the OSC command number. Use
+// [Cmd] and [Param] types to unpack command intermediates and prefixes as well
+// as parameters.
+//
+// Zero [Cmd] means the CSI, DCS, or ESC sequence is invalid. Moreover, checking the
+// validity of other data sequences, OSC, DCS, etc, will require checking for
+// the returned sequence terminator bytes such as ST (ESC \\) and BEL).
+//
+// We store the command byte in [Cmd] in the most significant byte, the
+// prefix byte in the next byte, and the intermediate byte in the least
+// significant byte. This is done to avoid using a struct to store the command
+// and its intermediates and prefixes. The command byte is always the least
+// significant byte i.e. [Cmd & 0xff]. Use the [Cmd] type to unpack the
+// command, intermediate, and prefix bytes. Note that we only collect the last
+// prefix character and intermediate byte.
+//
+// The [p.Params] slice will contain the parameters of the sequence. Any
+// sub-parameter will have the [parser.HasMoreFlag] set. Use the [Param] type
+// to unpack the parameters.
+//
+// Example:
+//
+// var state byte // the initial state is always zero [NormalState]
+// p := NewParser(32, 1024) // create a new parser with a 32 params buffer and 1024 data buffer (optional)
+// input := []byte("\x1b[31mHello, World!\x1b[0m")
+// for len(input) > 0 {
+// seq, width, n, newState := DecodeSequence(input, state, p)
+// log.Printf("seq: %q, width: %d", seq, width)
+// state = newState
+// input = input[n:]
+// }
+func (m Method) DecodeSequence(data []byte, state byte, p *Parser) (seq []byte, width, n int, newState byte) {
+ return decodeSequence(m, data, state, p)
+}
+
+// DecodeSequenceInString decodes the first ANSI escape sequence or a printable
+// grapheme from the given data. It returns the sequence slice, the number of
+// bytes read, the cell width for each sequence, and the new state.
+//
+// The cell width will always be 0 for control and escape sequences, 1 for
+// ASCII printable characters, and the number of cells other Unicode characters
+// occupy. It uses the uniseg package to calculate the width of Unicode
+// graphemes and characters. This means it will always do grapheme clustering
+// (mode 2027).
+//
+// Passing a non-nil [*Parser] as the last argument will allow the decoder to
+// collect sequence parameters, data, and commands. The parser cmd will have
+// the packed command value that contains intermediate and prefix characters.
+// In the case of a OSC sequence, the cmd will be the OSC command number. Use
+// [Cmd] and [Param] types to unpack command intermediates and prefixes as well
+// as parameters.
+//
+// Zero [Cmd] means the CSI, DCS, or ESC sequence is invalid. Moreover, checking the
+// validity of other data sequences, OSC, DCS, etc, will require checking for
+// the returned sequence terminator bytes such as ST (ESC \\) and BEL).
+//
+// We store the command byte in [Cmd] in the most significant byte, the
+// prefix byte in the next byte, and the intermediate byte in the least
+// significant byte. This is done to avoid using a struct to store the command
+// and its intermediates and prefixes. The command byte is always the least
+// significant byte i.e. [Cmd & 0xff]. Use the [Cmd] type to unpack the
+// command, intermediate, and prefix bytes. Note that we only collect the last
+// prefix character and intermediate byte.
+//
+// The [p.Params] slice will contain the parameters of the sequence. Any
+// sub-parameter will have the [parser.HasMoreFlag] set. Use the [Param] type
+// to unpack the parameters.
+//
+// Example:
+//
+// var state byte // the initial state is always zero [NormalState]
+// p := NewParser(32, 1024) // create a new parser with a 32 params buffer and 1024 data buffer (optional)
+// input := []byte("\x1b[31mHello, World!\x1b[0m")
+// for len(input) > 0 {
+// seq, width, n, newState := DecodeSequenceInString(input, state, p)
+// log.Printf("seq: %q, width: %d", seq, width)
+// state = newState
+// input = input[n:]
+// }
+func (m Method) DecodeSequenceInString(data string, state byte, p *Parser) (seq string, width, n int, newState byte) {
+ return decodeSequence(m, data, state, p)
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/mode.go b/vendor/github.com/charmbracelet/x/ansi/mode.go
new file mode 100644
index 0000000..57f3f0a
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/mode.go
@@ -0,0 +1,763 @@
+package ansi
+
+import (
+ "strconv"
+ "strings"
+)
+
+// ModeSetting represents a mode setting.
+type ModeSetting byte
+
+// ModeSetting constants.
+const (
+ ModeNotRecognized ModeSetting = iota
+ ModeSet
+ ModeReset
+ ModePermanentlySet
+ ModePermanentlyReset
+)
+
+// IsNotRecognized returns true if the mode is not recognized.
+func (m ModeSetting) IsNotRecognized() bool {
+ return m == ModeNotRecognized
+}
+
+// IsSet returns true if the mode is set or permanently set.
+func (m ModeSetting) IsSet() bool {
+ return m == ModeSet || m == ModePermanentlySet
+}
+
+// IsReset returns true if the mode is reset or permanently reset.
+func (m ModeSetting) IsReset() bool {
+ return m == ModeReset || m == ModePermanentlyReset
+}
+
+// IsPermanentlySet returns true if the mode is permanently set.
+func (m ModeSetting) IsPermanentlySet() bool {
+ return m == ModePermanentlySet
+}
+
+// IsPermanentlyReset returns true if the mode is permanently reset.
+func (m ModeSetting) IsPermanentlyReset() bool {
+ return m == ModePermanentlyReset
+}
+
+// Mode represents an interface for terminal modes.
+// Modes can be set, reset, and requested.
+type Mode interface {
+ Mode() int
+}
+
+// SetMode (SM) returns a sequence to set a mode.
+// The mode arguments are a list of modes to set.
+//
+// If one of the modes is a [DECMode], the function will returns two escape
+// sequences.
+//
+// ANSI format:
+//
+// CSI Pd ; ... ; Pd h
+//
+// DEC format:
+//
+// CSI ? Pd ; ... ; Pd h
+//
+// See: https://vt100.net/docs/vt510-rm/SM.html
+func SetMode(modes ...Mode) string {
+ return setMode(false, modes...)
+}
+
+// SM is an alias for [SetMode].
+func SM(modes ...Mode) string {
+ return SetMode(modes...)
+}
+
+// ResetMode (RM) returns a sequence to reset a mode.
+// The mode arguments are a list of modes to reset.
+//
+// If one of the modes is a [DECMode], the function will returns two escape
+// sequences.
+//
+// ANSI format:
+//
+// CSI Pd ; ... ; Pd l
+//
+// DEC format:
+//
+// CSI ? Pd ; ... ; Pd l
+//
+// See: https://vt100.net/docs/vt510-rm/RM.html
+func ResetMode(modes ...Mode) string {
+ return setMode(true, modes...)
+}
+
+// RM is an alias for [ResetMode].
+func RM(modes ...Mode) string {
+ return ResetMode(modes...)
+}
+
+func setMode(reset bool, modes ...Mode) (s string) {
+ if len(modes) == 0 {
+ return
+ }
+
+ cmd := "h"
+ if reset {
+ cmd = "l"
+ }
+
+ seq := "\x1b["
+ if len(modes) == 1 {
+ switch modes[0].(type) {
+ case DECMode:
+ seq += "?"
+ }
+ return seq + strconv.Itoa(modes[0].Mode()) + cmd
+ }
+
+ dec := make([]string, 0, len(modes)/2)
+ ansi := make([]string, 0, len(modes)/2)
+ for _, m := range modes {
+ switch m.(type) {
+ case DECMode:
+ dec = append(dec, strconv.Itoa(m.Mode()))
+ case ANSIMode:
+ ansi = append(ansi, strconv.Itoa(m.Mode()))
+ }
+ }
+
+ if len(ansi) > 0 {
+ s += seq + strings.Join(ansi, ";") + cmd
+ }
+ if len(dec) > 0 {
+ s += seq + "?" + strings.Join(dec, ";") + cmd
+ }
+ return
+}
+
+// RequestMode (DECRQM) returns a sequence to request a mode from the terminal.
+// The terminal responds with a report mode function [DECRPM].
+//
+// ANSI format:
+//
+// CSI Pa $ p
+//
+// DEC format:
+//
+// CSI ? Pa $ p
+//
+// See: https://vt100.net/docs/vt510-rm/DECRQM.html
+func RequestMode(m Mode) string {
+ seq := "\x1b["
+ switch m.(type) {
+ case DECMode:
+ seq += "?"
+ }
+ return seq + strconv.Itoa(m.Mode()) + "$p"
+}
+
+// DECRQM is an alias for [RequestMode].
+func DECRQM(m Mode) string {
+ return RequestMode(m)
+}
+
+// ReportMode (DECRPM) returns a sequence that the terminal sends to the host
+// in response to a mode request [DECRQM].
+//
+// ANSI format:
+//
+// CSI Pa ; Ps ; $ y
+//
+// DEC format:
+//
+// CSI ? Pa ; Ps $ y
+//
+// Where Pa is the mode number, and Ps is the mode value.
+//
+// 0: Not recognized
+// 1: Set
+// 2: Reset
+// 3: Permanent set
+// 4: Permanent reset
+//
+// See: https://vt100.net/docs/vt510-rm/DECRPM.html
+func ReportMode(mode Mode, value ModeSetting) string {
+ if value > 4 {
+ value = 0
+ }
+ switch mode.(type) {
+ case DECMode:
+ return "\x1b[?" + strconv.Itoa(mode.Mode()) + ";" + strconv.Itoa(int(value)) + "$y"
+ }
+ return "\x1b[" + strconv.Itoa(mode.Mode()) + ";" + strconv.Itoa(int(value)) + "$y"
+}
+
+// DECRPM is an alias for [ReportMode].
+func DECRPM(mode Mode, value ModeSetting) string {
+ return ReportMode(mode, value)
+}
+
+// ANSIMode represents an ANSI terminal mode.
+type ANSIMode int //nolint:revive
+
+// Mode returns the ANSI mode as an integer.
+func (m ANSIMode) Mode() int {
+ return int(m)
+}
+
+// DECMode represents a private DEC terminal mode.
+type DECMode int
+
+// Mode returns the DEC mode as an integer.
+func (m DECMode) Mode() int {
+ return int(m)
+}
+
+// Keyboard Action Mode (KAM) is a mode that controls locking of the keyboard.
+// When the keyboard is locked, it cannot send data to the terminal.
+//
+// See: https://vt100.net/docs/vt510-rm/KAM.html
+const (
+ KeyboardActionMode = ANSIMode(2)
+ KAM = KeyboardActionMode
+
+ SetKeyboardActionMode = "\x1b[2h"
+ ResetKeyboardActionMode = "\x1b[2l"
+ RequestKeyboardActionMode = "\x1b[2$p"
+)
+
+// Insert/Replace Mode (IRM) is a mode that determines whether characters are
+// inserted or replaced when typed.
+//
+// When enabled, characters are inserted at the cursor position pushing the
+// characters to the right. When disabled, characters replace the character at
+// the cursor position.
+//
+// See: https://vt100.net/docs/vt510-rm/IRM.html
+const (
+ InsertReplaceMode = ANSIMode(4)
+ IRM = InsertReplaceMode
+
+ SetInsertReplaceMode = "\x1b[4h"
+ ResetInsertReplaceMode = "\x1b[4l"
+ RequestInsertReplaceMode = "\x1b[4$p"
+)
+
+// Send Receive Mode (SRM) or Local Echo Mode is a mode that determines whether
+// the terminal echoes characters back to the host. When enabled, the terminal
+// sends characters to the host as they are typed.
+//
+// See: https://vt100.net/docs/vt510-rm/SRM.html
+const (
+ SendReceiveMode = ANSIMode(12)
+ LocalEchoMode = SendReceiveMode
+ SRM = SendReceiveMode
+
+ SetSendReceiveMode = "\x1b[12h"
+ ResetSendReceiveMode = "\x1b[12l"
+ RequestSendReceiveMode = "\x1b[12$p"
+
+ SetLocalEchoMode = "\x1b[12h"
+ ResetLocalEchoMode = "\x1b[12l"
+ RequestLocalEchoMode = "\x1b[12$p"
+)
+
+// Line Feed/New Line Mode (LNM) is a mode that determines whether the terminal
+// interprets the line feed character as a new line.
+//
+// When enabled, the terminal interprets the line feed character as a new line.
+// When disabled, the terminal interprets the line feed character as a line feed.
+//
+// A new line moves the cursor to the first position of the next line.
+// A line feed moves the cursor down one line without changing the column
+// scrolling the screen if necessary.
+//
+// See: https://vt100.net/docs/vt510-rm/LNM.html
+const (
+ LineFeedNewLineMode = ANSIMode(20)
+ LNM = LineFeedNewLineMode
+
+ SetLineFeedNewLineMode = "\x1b[20h"
+ ResetLineFeedNewLineMode = "\x1b[20l"
+ RequestLineFeedNewLineMode = "\x1b[20$p"
+)
+
+// Cursor Keys Mode (DECCKM) is a mode that determines whether the cursor keys
+// send ANSI cursor sequences or application sequences.
+//
+// See: https://vt100.net/docs/vt510-rm/DECCKM.html
+const (
+ CursorKeysMode = DECMode(1)
+ DECCKM = CursorKeysMode
+
+ SetCursorKeysMode = "\x1b[?1h"
+ ResetCursorKeysMode = "\x1b[?1l"
+ RequestCursorKeysMode = "\x1b[?1$p"
+)
+
+// Deprecated: use [SetCursorKeysMode] and [ResetCursorKeysMode] instead.
+const (
+ EnableCursorKeys = "\x1b[?1h"
+ DisableCursorKeys = "\x1b[?1l"
+)
+
+// Origin Mode (DECOM) is a mode that determines whether the cursor moves to the
+// home position or the margin position.
+//
+// See: https://vt100.net/docs/vt510-rm/DECOM.html
+const (
+ OriginMode = DECMode(6)
+ DECOM = OriginMode
+
+ SetOriginMode = "\x1b[?6h"
+ ResetOriginMode = "\x1b[?6l"
+ RequestOriginMode = "\x1b[?6$p"
+)
+
+// Auto Wrap Mode (DECAWM) is a mode that determines whether the cursor wraps
+// to the next line when it reaches the right margin.
+//
+// See: https://vt100.net/docs/vt510-rm/DECAWM.html
+const (
+ AutoWrapMode = DECMode(7)
+ DECAWM = AutoWrapMode
+
+ SetAutoWrapMode = "\x1b[?7h"
+ ResetAutoWrapMode = "\x1b[?7l"
+ RequestAutoWrapMode = "\x1b[?7$p"
+)
+
+// X10 Mouse Mode is a mode that determines whether the mouse reports on button
+// presses.
+//
+// The terminal responds with the following encoding:
+//
+// CSI M CbCxCy
+//
+// Where Cb is the button-1, where it can be 1, 2, or 3.
+// Cx and Cy are the x and y coordinates of the mouse event.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+const (
+ X10MouseMode = DECMode(9)
+
+ SetX10MouseMode = "\x1b[?9h"
+ ResetX10MouseMode = "\x1b[?9l"
+ RequestX10MouseMode = "\x1b[?9$p"
+)
+
+// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
+//
+// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
+const (
+ TextCursorEnableMode = DECMode(25)
+ DECTCEM = TextCursorEnableMode
+
+ SetTextCursorEnableMode = "\x1b[?25h"
+ ResetTextCursorEnableMode = "\x1b[?25l"
+ RequestTextCursorEnableMode = "\x1b[?25$p"
+)
+
+// These are aliases for [SetTextCursorEnableMode] and [ResetTextCursorEnableMode].
+const (
+ ShowCursor = SetTextCursorEnableMode
+ HideCursor = ResetTextCursorEnableMode
+)
+
+// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
+//
+// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
+//
+// Deprecated: use [SetTextCursorEnableMode] and [ResetTextCursorEnableMode] instead.
+const (
+ CursorEnableMode = DECMode(25)
+ RequestCursorVisibility = "\x1b[?25$p"
+)
+
+// Numeric Keypad Mode (DECNKM) is a mode that determines whether the keypad
+// sends application sequences or numeric sequences.
+//
+// This works like [DECKPAM] and [DECKPNM], but uses different sequences.
+//
+// See: https://vt100.net/docs/vt510-rm/DECNKM.html
+const (
+ NumericKeypadMode = DECMode(66)
+ DECNKM = NumericKeypadMode
+
+ SetNumericKeypadMode = "\x1b[?66h"
+ ResetNumericKeypadMode = "\x1b[?66l"
+ RequestNumericKeypadMode = "\x1b[?66$p"
+)
+
+// Backarrow Key Mode (DECBKM) is a mode that determines whether the backspace
+// key sends a backspace or delete character. Disabled by default.
+//
+// See: https://vt100.net/docs/vt510-rm/DECBKM.html
+const (
+ BackarrowKeyMode = DECMode(67)
+ DECBKM = BackarrowKeyMode
+
+ SetBackarrowKeyMode = "\x1b[?67h"
+ ResetBackarrowKeyMode = "\x1b[?67l"
+ RequestBackarrowKeyMode = "\x1b[?67$p"
+)
+
+// Left Right Margin Mode (DECLRMM) is a mode that determines whether the left
+// and right margins can be set with [DECSLRM].
+//
+// See: https://vt100.net/docs/vt510-rm/DECLRMM.html
+const (
+ LeftRightMarginMode = DECMode(69)
+ DECLRMM = LeftRightMarginMode
+
+ SetLeftRightMarginMode = "\x1b[?69h"
+ ResetLeftRightMarginMode = "\x1b[?69l"
+ RequestLeftRightMarginMode = "\x1b[?69$p"
+)
+
+// Normal Mouse Mode is a mode that determines whether the mouse reports on
+// button presses and releases. It will also report modifier keys, wheel
+// events, and extra buttons.
+//
+// It uses the same encoding as [X10MouseMode] with a few differences:
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+const (
+ NormalMouseMode = DECMode(1000)
+
+ SetNormalMouseMode = "\x1b[?1000h"
+ ResetNormalMouseMode = "\x1b[?1000l"
+ RequestNormalMouseMode = "\x1b[?1000$p"
+)
+
+// VT Mouse Tracking is a mode that determines whether the mouse reports on
+// button press and release.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+//
+// Deprecated: use [NormalMouseMode] instead.
+const (
+ MouseMode = DECMode(1000)
+
+ EnableMouse = "\x1b[?1000h"
+ DisableMouse = "\x1b[?1000l"
+ RequestMouse = "\x1b[?1000$p"
+)
+
+// Highlight Mouse Tracking is a mode that determines whether the mouse reports
+// on button presses, releases, and highlighted cells.
+//
+// It uses the same encoding as [NormalMouseMode] with a few differences:
+//
+// On highlight events, the terminal responds with the following encoding:
+//
+// CSI t CxCy
+// CSI T CxCyCxCyCxCy
+//
+// Where the parameters are startx, starty, endx, endy, mousex, and mousey.
+const (
+ HighlightMouseMode = DECMode(1001)
+
+ SetHighlightMouseMode = "\x1b[?1001h"
+ ResetHighlightMouseMode = "\x1b[?1001l"
+ RequestHighlightMouseMode = "\x1b[?1001$p"
+)
+
+// VT Hilite Mouse Tracking is a mode that determines whether the mouse reports on
+// button presses, releases, and highlighted cells.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+//
+// Deprecated: use [HighlightMouseMode] instead.
+const (
+ MouseHiliteMode = DECMode(1001)
+
+ EnableMouseHilite = "\x1b[?1001h"
+ DisableMouseHilite = "\x1b[?1001l"
+ RequestMouseHilite = "\x1b[?1001$p"
+)
+
+// Button Event Mouse Tracking is essentially the same as [NormalMouseMode],
+// but it also reports button-motion events when a button is pressed.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+const (
+ ButtonEventMouseMode = DECMode(1002)
+
+ SetButtonEventMouseMode = "\x1b[?1002h"
+ ResetButtonEventMouseMode = "\x1b[?1002l"
+ RequestButtonEventMouseMode = "\x1b[?1002$p"
+)
+
+// Cell Motion Mouse Tracking is a mode that determines whether the mouse
+// reports on button press, release, and motion events.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+//
+// Deprecated: use [ButtonEventMouseMode] instead.
+const (
+ MouseCellMotionMode = DECMode(1002)
+
+ EnableMouseCellMotion = "\x1b[?1002h"
+ DisableMouseCellMotion = "\x1b[?1002l"
+ RequestMouseCellMotion = "\x1b[?1002$p"
+)
+
+// Any Event Mouse Tracking is the same as [ButtonEventMouseMode], except that
+// all motion events are reported even if no mouse buttons are pressed.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+const (
+ AnyEventMouseMode = DECMode(1003)
+
+ SetAnyEventMouseMode = "\x1b[?1003h"
+ ResetAnyEventMouseMode = "\x1b[?1003l"
+ RequestAnyEventMouseMode = "\x1b[?1003$p"
+)
+
+// All Mouse Tracking is a mode that determines whether the mouse reports on
+// button press, release, motion, and highlight events.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+//
+// Deprecated: use [AnyEventMouseMode] instead.
+const (
+ MouseAllMotionMode = DECMode(1003)
+
+ EnableMouseAllMotion = "\x1b[?1003h"
+ DisableMouseAllMotion = "\x1b[?1003l"
+ RequestMouseAllMotion = "\x1b[?1003$p"
+)
+
+// Focus Event Mode is a mode that determines whether the terminal reports focus
+// and blur events.
+//
+// The terminal sends the following encoding:
+//
+// CSI I // Focus In
+// CSI O // Focus Out
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Focus-Tracking
+const (
+ FocusEventMode = DECMode(1004)
+
+ SetFocusEventMode = "\x1b[?1004h"
+ ResetFocusEventMode = "\x1b[?1004l"
+ RequestFocusEventMode = "\x1b[?1004$p"
+)
+
+// Deprecated: use [SetFocusEventMode], [ResetFocusEventMode], and
+// [RequestFocusEventMode] instead.
+const (
+ ReportFocusMode = DECMode(1004)
+
+ EnableReportFocus = "\x1b[?1004h"
+ DisableReportFocus = "\x1b[?1004l"
+ RequestReportFocus = "\x1b[?1004$p"
+)
+
+// SGR Extended Mouse Mode is a mode that changes the mouse tracking encoding
+// to use SGR parameters.
+//
+// The terminal responds with the following encoding:
+//
+// CSI < Cb ; Cx ; Cy M
+//
+// Where Cb is the same as [NormalMouseMode], and Cx and Cy are the x and y.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+const (
+ SgrExtMouseMode = DECMode(1006)
+
+ SetSgrExtMouseMode = "\x1b[?1006h"
+ ResetSgrExtMouseMode = "\x1b[?1006l"
+ RequestSgrExtMouseMode = "\x1b[?1006$p"
+)
+
+// Deprecated: use [SgrExtMouseMode] [SetSgrExtMouseMode],
+// [ResetSgrExtMouseMode], and [RequestSgrExtMouseMode] instead.
+const (
+ MouseSgrExtMode = DECMode(1006)
+ EnableMouseSgrExt = "\x1b[?1006h"
+ DisableMouseSgrExt = "\x1b[?1006l"
+ RequestMouseSgrExt = "\x1b[?1006$p"
+)
+
+// UTF-8 Extended Mouse Mode is a mode that changes the mouse tracking encoding
+// to use UTF-8 parameters.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+const (
+ Utf8ExtMouseMode = DECMode(1005)
+
+ SetUtf8ExtMouseMode = "\x1b[?1005h"
+ ResetUtf8ExtMouseMode = "\x1b[?1005l"
+ RequestUtf8ExtMouseMode = "\x1b[?1005$p"
+)
+
+// URXVT Extended Mouse Mode is a mode that changes the mouse tracking encoding
+// to use an alternate encoding.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+const (
+ UrxvtExtMouseMode = DECMode(1015)
+
+ SetUrxvtExtMouseMode = "\x1b[?1015h"
+ ResetUrxvtExtMouseMode = "\x1b[?1015l"
+ RequestUrxvtExtMouseMode = "\x1b[?1015$p"
+)
+
+// SGR Pixel Extended Mouse Mode is a mode that changes the mouse tracking
+// encoding to use SGR parameters with pixel coordinates.
+//
+// This is similar to [SgrExtMouseMode], but also reports pixel coordinates.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
+const (
+ SgrPixelExtMouseMode = DECMode(1016)
+
+ SetSgrPixelExtMouseMode = "\x1b[?1016h"
+ ResetSgrPixelExtMouseMode = "\x1b[?1016l"
+ RequestSgrPixelExtMouseMode = "\x1b[?1016$p"
+)
+
+// Alternate Screen Mode is a mode that determines whether the alternate screen
+// buffer is active. When this mode is enabled, the alternate screen buffer is
+// cleared.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
+const (
+ AltScreenMode = DECMode(1047)
+
+ SetAltScreenMode = "\x1b[?1047h"
+ ResetAltScreenMode = "\x1b[?1047l"
+ RequestAltScreenMode = "\x1b[?1047$p"
+)
+
+// Save Cursor Mode is a mode that saves the cursor position.
+// This is equivalent to [SaveCursor] and [RestoreCursor].
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
+const (
+ SaveCursorMode = DECMode(1048)
+
+ SetSaveCursorMode = "\x1b[?1048h"
+ ResetSaveCursorMode = "\x1b[?1048l"
+ RequestSaveCursorMode = "\x1b[?1048$p"
+)
+
+// Alternate Screen Save Cursor Mode is a mode that saves the cursor position as in
+// [SaveCursorMode], switches to the alternate screen buffer as in [AltScreenMode],
+// and clears the screen on switch.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
+const (
+ AltScreenSaveCursorMode = DECMode(1049)
+
+ SetAltScreenSaveCursorMode = "\x1b[?1049h"
+ ResetAltScreenSaveCursorMode = "\x1b[?1049l"
+ RequestAltScreenSaveCursorMode = "\x1b[?1049$p"
+)
+
+// Alternate Screen Buffer is a mode that determines whether the alternate screen
+// buffer is active.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
+//
+// Deprecated: use [AltScreenSaveCursorMode] instead.
+const (
+ AltScreenBufferMode = DECMode(1049)
+
+ SetAltScreenBufferMode = "\x1b[?1049h"
+ ResetAltScreenBufferMode = "\x1b[?1049l"
+ RequestAltScreenBufferMode = "\x1b[?1049$p"
+
+ EnableAltScreenBuffer = "\x1b[?1049h"
+ DisableAltScreenBuffer = "\x1b[?1049l"
+ RequestAltScreenBuffer = "\x1b[?1049$p"
+)
+
+// Bracketed Paste Mode is a mode that determines whether pasted text is
+// bracketed with escape sequences.
+//
+// See: https://cirw.in/blog/bracketed-paste
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
+const (
+ BracketedPasteMode = DECMode(2004)
+
+ SetBracketedPasteMode = "\x1b[?2004h"
+ ResetBracketedPasteMode = "\x1b[?2004l"
+ RequestBracketedPasteMode = "\x1b[?2004$p"
+)
+
+// Deprecated: use [SetBracketedPasteMode], [ResetBracketedPasteMode], and
+// [RequestBracketedPasteMode] instead.
+const (
+ EnableBracketedPaste = "\x1b[?2004h"
+ DisableBracketedPaste = "\x1b[?2004l"
+ RequestBracketedPaste = "\x1b[?2004$p"
+)
+
+// Synchronized Output Mode is a mode that determines whether output is
+// synchronized with the terminal.
+//
+// See: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
+const (
+ SynchronizedOutputMode = DECMode(2026)
+
+ SetSynchronizedOutputMode = "\x1b[?2026h"
+ ResetSynchronizedOutputMode = "\x1b[?2026l"
+ RequestSynchronizedOutputMode = "\x1b[?2026$p"
+)
+
+// Deprecated: use [SynchronizedOutputMode], [SetSynchronizedOutputMode], and
+// [ResetSynchronizedOutputMode], and [RequestSynchronizedOutputMode] instead.
+const (
+ SyncdOutputMode = DECMode(2026)
+
+ EnableSyncdOutput = "\x1b[?2026h"
+ DisableSyncdOutput = "\x1b[?2026l"
+ RequestSyncdOutput = "\x1b[?2026$p"
+)
+
+// Grapheme Clustering Mode is a mode that determines whether the terminal
+// should look for grapheme clusters instead of single runes in the rendered
+// text. This makes the terminal properly render combining characters such as
+// emojis.
+//
+// See: https://github.com/contour-terminal/terminal-unicode-core
+const (
+ GraphemeClusteringMode = DECMode(2027)
+
+ SetGraphemeClusteringMode = "\x1b[?2027h"
+ ResetGraphemeClusteringMode = "\x1b[?2027l"
+ RequestGraphemeClusteringMode = "\x1b[?2027$p"
+)
+
+// Deprecated: use [SetGraphemeClusteringMode], [ResetGraphemeClusteringMode], and
+// [RequestGraphemeClusteringMode] instead.
+const (
+ EnableGraphemeClustering = "\x1b[?2027h"
+ DisableGraphemeClustering = "\x1b[?2027l"
+ RequestGraphemeClustering = "\x1b[?2027$p"
+)
+
+// Win32Input is a mode that determines whether input is processed by the
+// Win32 console and Conpty.
+//
+// See: https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md
+const (
+ Win32InputMode = DECMode(9001)
+
+ SetWin32InputMode = "\x1b[?9001h"
+ ResetWin32InputMode = "\x1b[?9001l"
+ RequestWin32InputMode = "\x1b[?9001$p"
+)
+
+// Deprecated: use [SetWin32InputMode], [ResetWin32InputMode], and
+// [RequestWin32InputMode] instead.
+const (
+ EnableWin32Input = "\x1b[?9001h"
+ DisableWin32Input = "\x1b[?9001l"
+ RequestWin32Input = "\x1b[?9001$p"
+)
diff --git a/vendor/github.com/charmbracelet/x/ansi/modes.go b/vendor/github.com/charmbracelet/x/ansi/modes.go
new file mode 100644
index 0000000..1bec5bc
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/modes.go
@@ -0,0 +1,71 @@
+package ansi
+
+// Modes represents the terminal modes that can be set or reset. By default,
+// all modes are [ModeNotRecognized].
+type Modes map[Mode]ModeSetting
+
+// NewModes creates a new Modes map. By default, all modes are
+// [ModeNotRecognized].
+func NewModes() Modes {
+ return make(Modes)
+}
+
+// Get returns the setting of a terminal mode. If the mode is not set, it
+// returns [ModeNotRecognized].
+func (m Modes) Get(mode Mode) ModeSetting {
+ return m[mode]
+}
+
+// Delete deletes a terminal mode. This has the same effect as setting the mode
+// to [ModeNotRecognized].
+func (m Modes) Delete(mode Mode) {
+ delete(m, mode)
+}
+
+// Set sets a terminal mode to [ModeSet].
+func (m Modes) Set(modes ...Mode) {
+ for _, mode := range modes {
+ m[mode] = ModeSet
+ }
+}
+
+// PermanentlySet sets a terminal mode to [ModePermanentlySet].
+func (m Modes) PermanentlySet(modes ...Mode) {
+ for _, mode := range modes {
+ m[mode] = ModePermanentlySet
+ }
+}
+
+// Reset sets a terminal mode to [ModeReset].
+func (m Modes) Reset(modes ...Mode) {
+ for _, mode := range modes {
+ m[mode] = ModeReset
+ }
+}
+
+// PermanentlyReset sets a terminal mode to [ModePermanentlyReset].
+func (m Modes) PermanentlyReset(modes ...Mode) {
+ for _, mode := range modes {
+ m[mode] = ModePermanentlyReset
+ }
+}
+
+// IsSet returns true if the mode is set to [ModeSet] or [ModePermanentlySet].
+func (m Modes) IsSet(mode Mode) bool {
+ return m[mode].IsSet()
+}
+
+// IsPermanentlySet returns true if the mode is set to [ModePermanentlySet].
+func (m Modes) IsPermanentlySet(mode Mode) bool {
+ return m[mode].IsPermanentlySet()
+}
+
+// IsReset returns true if the mode is set to [ModeReset] or [ModePermanentlyReset].
+func (m Modes) IsReset(mode Mode) bool {
+ return m[mode].IsReset()
+}
+
+// IsPermanentlyReset returns true if the mode is set to [ModePermanentlyReset].
+func (m Modes) IsPermanentlyReset(mode Mode) bool {
+ return m[mode].IsPermanentlyReset()
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/mouse.go b/vendor/github.com/charmbracelet/x/ansi/mouse.go
new file mode 100644
index 0000000..95b0127
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/mouse.go
@@ -0,0 +1,172 @@
+package ansi
+
+import (
+ "fmt"
+)
+
+// MouseButton represents the button that was pressed during a mouse message.
+type MouseButton byte
+
+// Mouse event buttons
+//
+// This is based on X11 mouse button codes.
+//
+// 1 = left button
+// 2 = middle button (pressing the scroll wheel)
+// 3 = right button
+// 4 = turn scroll wheel up
+// 5 = turn scroll wheel down
+// 6 = push scroll wheel left
+// 7 = push scroll wheel right
+// 8 = 4th button (aka browser backward button)
+// 9 = 5th button (aka browser forward button)
+// 10
+// 11
+//
+// Other buttons are not supported.
+const (
+ MouseNone MouseButton = iota
+ MouseButton1
+ MouseButton2
+ MouseButton3
+ MouseButton4
+ MouseButton5
+ MouseButton6
+ MouseButton7
+ MouseButton8
+ MouseButton9
+ MouseButton10
+ MouseButton11
+
+ MouseLeft = MouseButton1
+ MouseMiddle = MouseButton2
+ MouseRight = MouseButton3
+ MouseWheelUp = MouseButton4
+ MouseWheelDown = MouseButton5
+ MouseWheelLeft = MouseButton6
+ MouseWheelRight = MouseButton7
+ MouseBackward = MouseButton8
+ MouseForward = MouseButton9
+ MouseRelease = MouseNone
+)
+
+var mouseButtons = map[MouseButton]string{
+ MouseNone: "none",
+ MouseLeft: "left",
+ MouseMiddle: "middle",
+ MouseRight: "right",
+ MouseWheelUp: "wheelup",
+ MouseWheelDown: "wheeldown",
+ MouseWheelLeft: "wheelleft",
+ MouseWheelRight: "wheelright",
+ MouseBackward: "backward",
+ MouseForward: "forward",
+ MouseButton10: "button10",
+ MouseButton11: "button11",
+}
+
+// String returns a string representation of the mouse button.
+func (b MouseButton) String() string {
+ return mouseButtons[b]
+}
+
+// EncodeMouseButton returns a byte representing a mouse button.
+// The button is a bitmask of the following leftmost values:
+//
+// - The first two bits are the button number:
+// 0 = left button, wheel up, or button no. 8 aka (backwards)
+// 1 = middle button, wheel down, or button no. 9 aka (forwards)
+// 2 = right button, wheel left, or button no. 10
+// 3 = release event, wheel right, or button no. 11
+//
+// - The third bit indicates whether the shift key was pressed.
+//
+// - The fourth bit indicates the alt key was pressed.
+//
+// - The fifth bit indicates the control key was pressed.
+//
+// - The sixth bit indicates motion events. Combined with button number 3, i.e.
+// release event, it represents a drag event.
+//
+// - The seventh bit indicates a wheel event.
+//
+// - The eighth bit indicates additional buttons.
+//
+// If button is [MouseNone], and motion is false, this returns a release event.
+// If button is undefined, this function returns 0xff.
+func EncodeMouseButton(b MouseButton, motion, shift, alt, ctrl bool) (m byte) {
+ // mouse bit shifts
+ const (
+ bitShift = 0b0000_0100
+ bitAlt = 0b0000_1000
+ bitCtrl = 0b0001_0000
+ bitMotion = 0b0010_0000
+ bitWheel = 0b0100_0000
+ bitAdd = 0b1000_0000 // additional buttons 8-11
+
+ bitsMask = 0b0000_0011
+ )
+
+ if b == MouseNone {
+ m = bitsMask
+ } else if b >= MouseLeft && b <= MouseRight {
+ m = byte(b - MouseLeft)
+ } else if b >= MouseWheelUp && b <= MouseWheelRight {
+ m = byte(b - MouseWheelUp)
+ m |= bitWheel
+ } else if b >= MouseBackward && b <= MouseButton11 {
+ m = byte(b - MouseBackward)
+ m |= bitAdd
+ } else {
+ m = 0xff // invalid button
+ }
+
+ if shift {
+ m |= bitShift
+ }
+ if alt {
+ m |= bitAlt
+ }
+ if ctrl {
+ m |= bitCtrl
+ }
+ if motion {
+ m |= bitMotion
+ }
+
+ return
+}
+
+// x10Offset is the offset for X10 mouse events.
+// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
+const x10Offset = 32
+
+// MouseX10 returns an escape sequence representing a mouse event in X10 mode.
+// Note that this requires the terminal support X10 mouse modes.
+//
+// CSI M Cb Cx Cy
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
+func MouseX10(b byte, x, y int) string {
+ return "\x1b[M" + string(b+x10Offset) + string(byte(x)+x10Offset+1) + string(byte(y)+x10Offset+1)
+}
+
+// MouseSgr returns an escape sequence representing a mouse event in SGR mode.
+//
+// CSI < Cb ; Cx ; Cy M
+// CSI < Cb ; Cx ; Cy m (release)
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
+func MouseSgr(b byte, x, y int, release bool) string {
+ s := 'M'
+ if release {
+ s = 'm'
+ }
+ if x < 0 {
+ x = -x
+ }
+ if y < 0 {
+ y = -y
+ }
+ return fmt.Sprintf("\x1b[<%d;%d;%d%c", b, x+1, y+1, s)
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/notification.go b/vendor/github.com/charmbracelet/x/ansi/notification.go
new file mode 100644
index 0000000..c712f34
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/notification.go
@@ -0,0 +1,13 @@
+package ansi
+
+// Notify sends a desktop notification using iTerm's OSC 9.
+//
+// OSC 9 ; Mc ST
+// OSC 9 ; Mc BEL
+//
+// Where Mc is the notification body.
+//
+// See: https://iterm2.com/documentation-escape-codes.html
+func Notify(s string) string {
+ return "\x1b]9;" + s + "\x07"
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/parser.go b/vendor/github.com/charmbracelet/x/ansi/parser.go
new file mode 100644
index 0000000..882e1ed
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/parser.go
@@ -0,0 +1,417 @@
+package ansi
+
+import (
+ "unicode/utf8"
+ "unsafe"
+
+ "github.com/charmbracelet/x/ansi/parser"
+)
+
+// Parser represents a DEC ANSI compatible sequence parser.
+//
+// It uses a state machine to parse ANSI escape sequences and control
+// characters. The parser is designed to be used with a terminal emulator or
+// similar application that needs to parse ANSI escape sequences and control
+// characters.
+// See package [parser] for more information.
+//
+//go:generate go run ./gen.go
+type Parser struct {
+ handler Handler
+
+ // params contains the raw parameters of the sequence.
+ // These parameters used when constructing CSI and DCS sequences.
+ params []int
+
+ // data contains the raw data of the sequence.
+ // These data used when constructing OSC, DCS, SOS, PM, and APC sequences.
+ data []byte
+
+ // dataLen keeps track of the length of the data buffer.
+ // If dataLen is -1, the data buffer is unlimited and will grow as needed.
+ // Otherwise, dataLen is limited by the size of the data buffer.
+ dataLen int
+
+ // paramsLen keeps track of the number of parameters.
+ // This is limited by the size of the params buffer.
+ //
+ // This is also used when collecting UTF-8 runes to keep track of the
+ // number of rune bytes collected.
+ paramsLen int
+
+ // cmd contains the raw command along with the private prefix and
+ // intermediate bytes of the sequence.
+ // The first lower byte contains the command byte, the next byte contains
+ // the private prefix, and the next byte contains the intermediate byte.
+ //
+ // This is also used when collecting UTF-8 runes treating it as a slice of
+ // 4 bytes.
+ cmd int
+
+ // state is the current state of the parser.
+ state byte
+}
+
+// NewParser returns a new parser with the default settings.
+// The [Parser] uses a default size of 32 for the parameters and 64KB for the
+// data buffer. Use [Parser.SetParamsSize] and [Parser.SetDataSize] to set the
+// size of the parameters and data buffer respectively.
+func NewParser() *Parser {
+ p := new(Parser)
+ p.SetParamsSize(parser.MaxParamsSize)
+ p.SetDataSize(1024 * 64) // 64KB data buffer
+ return p
+}
+
+// SetParamsSize sets the size of the parameters buffer.
+// This is used when constructing CSI and DCS sequences.
+func (p *Parser) SetParamsSize(size int) {
+ p.params = make([]int, size)
+}
+
+// SetDataSize sets the size of the data buffer.
+// This is used when constructing OSC, DCS, SOS, PM, and APC sequences.
+// If size is less than or equal to 0, the data buffer is unlimited and will
+// grow as needed.
+func (p *Parser) SetDataSize(size int) {
+ if size <= 0 {
+ size = 0
+ p.dataLen = -1
+ }
+ p.data = make([]byte, size)
+}
+
+// Params returns the list of parsed packed parameters.
+func (p *Parser) Params() Params {
+ return unsafe.Slice((*Param)(unsafe.Pointer(&p.params[0])), p.paramsLen)
+}
+
+// Param returns the parameter at the given index and falls back to the default
+// value if the parameter is missing. If the index is out of bounds, it returns
+// the default value and false.
+func (p *Parser) Param(i, def int) (int, bool) {
+ if i < 0 || i >= p.paramsLen {
+ return def, false
+ }
+ return Param(p.params[i]).Param(def), true
+}
+
+// Command returns the packed command of the last dispatched sequence. Use
+// [Cmd] to unpack the command.
+func (p *Parser) Command() int {
+ return p.cmd
+}
+
+// Rune returns the last dispatched sequence as a rune.
+func (p *Parser) Rune() rune {
+ rw := utf8ByteLen(byte(p.cmd & 0xff))
+ if rw == -1 {
+ return utf8.RuneError
+ }
+ r, _ := utf8.DecodeRune((*[utf8.UTFMax]byte)(unsafe.Pointer(&p.cmd))[:rw])
+ return r
+}
+
+// Control returns the last dispatched sequence as a control code.
+func (p *Parser) Control() byte {
+ return byte(p.cmd & 0xff)
+}
+
+// Data returns the raw data of the last dispatched sequence.
+func (p *Parser) Data() []byte {
+ return p.data[:p.dataLen]
+}
+
+// Reset resets the parser to its initial state.
+func (p *Parser) Reset() {
+ p.clear()
+ p.state = parser.GroundState
+}
+
+// clear clears the parser parameters and command.
+func (p *Parser) clear() {
+ if len(p.params) > 0 {
+ p.params[0] = parser.MissingParam
+ }
+ p.paramsLen = 0
+ p.cmd = 0
+}
+
+// State returns the current state of the parser.
+func (p *Parser) State() parser.State {
+ return p.state
+}
+
+// StateName returns the name of the current state.
+func (p *Parser) StateName() string {
+ return parser.StateNames[p.state]
+}
+
+// Parse parses the given dispatcher and byte buffer.
+// Deprecated: Loop over the buffer and call [Parser.Advance] instead.
+func (p *Parser) Parse(b []byte) {
+ for i := 0; i < len(b); i++ {
+ p.Advance(b[i])
+ }
+}
+
+// Advance advances the parser using the given byte. It returns the action
+// performed by the parser.
+func (p *Parser) Advance(b byte) parser.Action {
+ switch p.state {
+ case parser.Utf8State:
+ // We handle UTF-8 here.
+ return p.advanceUtf8(b)
+ default:
+ return p.advance(b)
+ }
+}
+
+func (p *Parser) collectRune(b byte) {
+ if p.paramsLen >= utf8.UTFMax {
+ return
+ }
+
+ shift := p.paramsLen * 8
+ p.cmd &^= 0xff << shift
+ p.cmd |= int(b) << shift
+ p.paramsLen++
+}
+
+func (p *Parser) advanceUtf8(b byte) parser.Action {
+ // Collect UTF-8 rune bytes.
+ p.collectRune(b)
+ rw := utf8ByteLen(byte(p.cmd & 0xff))
+ if rw == -1 {
+ // We panic here because the first byte comes from the state machine,
+ // if this panics, it means there is a bug in the state machine!
+ panic("invalid rune") // unreachable
+ }
+
+ if p.paramsLen < rw {
+ return parser.CollectAction
+ }
+
+ // We have enough bytes to decode the rune using unsafe
+ if p.handler.Print != nil {
+ p.handler.Print(p.Rune())
+ }
+
+ p.state = parser.GroundState
+ p.paramsLen = 0
+
+ return parser.PrintAction
+}
+
+func (p *Parser) advance(b byte) parser.Action {
+ state, action := parser.Table.Transition(p.state, b)
+
+ // We need to clear the parser state if the state changes from EscapeState.
+ // This is because when we enter the EscapeState, we don't get a chance to
+ // clear the parser state. For example, when a sequence terminates with a
+ // ST (\x1b\\ or \x9c), we dispatch the current sequence and transition to
+ // EscapeState. However, the parser state is not cleared in this case and
+ // we need to clear it here before dispatching the esc sequence.
+ if p.state != state {
+ if p.state == parser.EscapeState {
+ p.performAction(parser.ClearAction, state, b)
+ }
+ if action == parser.PutAction &&
+ p.state == parser.DcsEntryState && state == parser.DcsStringState {
+ // XXX: This is a special case where we need to start collecting
+ // non-string parameterized data i.e. doesn't follow the ECMA-48 §
+ // 5.4.1 string parameters format.
+ p.performAction(parser.StartAction, state, 0)
+ }
+ }
+
+ // Handle special cases
+ switch {
+ case b == ESC && p.state == parser.EscapeState:
+ // Two ESCs in a row
+ p.performAction(parser.ExecuteAction, state, b)
+ default:
+ p.performAction(action, state, b)
+ }
+
+ p.state = state
+
+ return action
+}
+
+func (p *Parser) parseStringCmd() {
+ // Try to parse the command
+ datalen := len(p.data)
+ if p.dataLen >= 0 {
+ datalen = p.dataLen
+ }
+ for i := 0; i < datalen; i++ {
+ d := p.data[i]
+ if d < '0' || d > '9' {
+ break
+ }
+ if p.cmd == parser.MissingCommand {
+ p.cmd = 0
+ }
+ p.cmd *= 10
+ p.cmd += int(d - '0')
+ }
+}
+
+func (p *Parser) performAction(action parser.Action, state parser.State, b byte) {
+ switch action {
+ case parser.IgnoreAction:
+ break
+
+ case parser.ClearAction:
+ p.clear()
+
+ case parser.PrintAction:
+ p.cmd = int(b)
+ if p.handler.Print != nil {
+ p.handler.Print(rune(b))
+ }
+
+ case parser.ExecuteAction:
+ p.cmd = int(b)
+ if p.handler.Execute != nil {
+ p.handler.Execute(b)
+ }
+
+ case parser.PrefixAction:
+ // Collect private prefix
+ // we only store the last prefix
+ p.cmd &^= 0xff << parser.PrefixShift
+ p.cmd |= int(b) << parser.PrefixShift
+
+ case parser.CollectAction:
+ if state == parser.Utf8State {
+ // Reset the UTF-8 counter
+ p.paramsLen = 0
+ p.collectRune(b)
+ } else {
+ // Collect intermediate bytes
+ // we only store the last intermediate byte
+ p.cmd &^= 0xff << parser.IntermedShift
+ p.cmd |= int(b) << parser.IntermedShift
+ }
+
+ case parser.ParamAction:
+ // Collect parameters
+ if p.paramsLen >= len(p.params) {
+ break
+ }
+
+ if b >= '0' && b <= '9' {
+ if p.params[p.paramsLen] == parser.MissingParam {
+ p.params[p.paramsLen] = 0
+ }
+
+ p.params[p.paramsLen] *= 10
+ p.params[p.paramsLen] += int(b - '0')
+ }
+
+ if b == ':' {
+ p.params[p.paramsLen] |= parser.HasMoreFlag
+ }
+
+ if b == ';' || b == ':' {
+ p.paramsLen++
+ if p.paramsLen < len(p.params) {
+ p.params[p.paramsLen] = parser.MissingParam
+ }
+ }
+
+ case parser.StartAction:
+ if p.dataLen < 0 && p.data != nil {
+ p.data = p.data[:0]
+ } else {
+ p.dataLen = 0
+ }
+ if p.state >= parser.DcsEntryState && p.state <= parser.DcsStringState {
+ // Collect the command byte for DCS
+ p.cmd |= int(b)
+ } else {
+ p.cmd = parser.MissingCommand
+ }
+
+ case parser.PutAction:
+ switch p.state {
+ case parser.OscStringState:
+ if b == ';' && p.cmd == parser.MissingCommand {
+ p.parseStringCmd()
+ }
+ }
+
+ if p.dataLen < 0 {
+ p.data = append(p.data, b)
+ } else {
+ if p.dataLen < len(p.data) {
+ p.data[p.dataLen] = b
+ p.dataLen++
+ }
+ }
+
+ case parser.DispatchAction:
+ // Increment the last parameter
+ if p.paramsLen > 0 && p.paramsLen < len(p.params)-1 ||
+ p.paramsLen == 0 && len(p.params) > 0 && p.params[0] != parser.MissingParam {
+ p.paramsLen++
+ }
+
+ if p.state == parser.OscStringState && p.cmd == parser.MissingCommand {
+ // Ensure we have a command for OSC
+ p.parseStringCmd()
+ }
+
+ data := p.data
+ if p.dataLen >= 0 {
+ data = data[:p.dataLen]
+ }
+ switch p.state {
+ case parser.CsiEntryState, parser.CsiParamState, parser.CsiIntermediateState:
+ p.cmd |= int(b)
+ if p.handler.HandleCsi != nil {
+ p.handler.HandleCsi(Cmd(p.cmd), p.Params())
+ }
+ case parser.EscapeState, parser.EscapeIntermediateState:
+ p.cmd |= int(b)
+ if p.handler.HandleEsc != nil {
+ p.handler.HandleEsc(Cmd(p.cmd))
+ }
+ case parser.DcsEntryState, parser.DcsParamState, parser.DcsIntermediateState, parser.DcsStringState:
+ if p.handler.HandleDcs != nil {
+ p.handler.HandleDcs(Cmd(p.cmd), p.Params(), data)
+ }
+ case parser.OscStringState:
+ if p.handler.HandleOsc != nil {
+ p.handler.HandleOsc(p.cmd, data)
+ }
+ case parser.SosStringState:
+ if p.handler.HandleSos != nil {
+ p.handler.HandleSos(data)
+ }
+ case parser.PmStringState:
+ if p.handler.HandlePm != nil {
+ p.handler.HandlePm(data)
+ }
+ case parser.ApcStringState:
+ if p.handler.HandleApc != nil {
+ p.handler.HandleApc(data)
+ }
+ }
+ }
+}
+
+func utf8ByteLen(b byte) int {
+ if b <= 0b0111_1111 { // 0x00-0x7F
+ return 1
+ } else if b >= 0b1100_0000 && b <= 0b1101_1111 { // 0xC0-0xDF
+ return 2
+ } else if b >= 0b1110_0000 && b <= 0b1110_1111 { // 0xE0-0xEF
+ return 3
+ } else if b >= 0b1111_0000 && b <= 0b1111_0111 { // 0xF0-0xF7
+ return 4
+ }
+ return -1
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/parser/const.go b/vendor/github.com/charmbracelet/x/ansi/parser/const.go
new file mode 100644
index 0000000..d62dbf3
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/parser/const.go
@@ -0,0 +1,78 @@
+package parser
+
+// Action is a DEC ANSI parser action.
+type Action = byte
+
+// These are the actions that the parser can take.
+const (
+ NoneAction Action = iota
+ ClearAction
+ CollectAction
+ PrefixAction
+ DispatchAction
+ ExecuteAction
+ StartAction // Start of a data string
+ PutAction // Put into the data string
+ ParamAction
+ PrintAction
+
+ IgnoreAction = NoneAction
+)
+
+// nolint: unused
+var ActionNames = []string{
+ "NoneAction",
+ "ClearAction",
+ "CollectAction",
+ "PrefixAction",
+ "DispatchAction",
+ "ExecuteAction",
+ "StartAction",
+ "PutAction",
+ "ParamAction",
+ "PrintAction",
+}
+
+// State is a DEC ANSI parser state.
+type State = byte
+
+// These are the states that the parser can be in.
+const (
+ GroundState State = iota
+ CsiEntryState
+ CsiIntermediateState
+ CsiParamState
+ DcsEntryState
+ DcsIntermediateState
+ DcsParamState
+ DcsStringState
+ EscapeState
+ EscapeIntermediateState
+ OscStringState
+ SosStringState
+ PmStringState
+ ApcStringState
+
+ // Utf8State is not part of the DEC ANSI standard. It is used to handle
+ // UTF-8 sequences.
+ Utf8State
+)
+
+// nolint: unused
+var StateNames = []string{
+ "GroundState",
+ "CsiEntryState",
+ "CsiIntermediateState",
+ "CsiParamState",
+ "DcsEntryState",
+ "DcsIntermediateState",
+ "DcsParamState",
+ "DcsStringState",
+ "EscapeState",
+ "EscapeIntermediateState",
+ "OscStringState",
+ "SosStringState",
+ "PmStringState",
+ "ApcStringState",
+ "Utf8State",
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/parser/seq.go b/vendor/github.com/charmbracelet/x/ansi/parser/seq.go
new file mode 100644
index 0000000..29f491d
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/parser/seq.go
@@ -0,0 +1,136 @@
+package parser
+
+import "math"
+
+// Shift and masks for sequence parameters and intermediates.
+const (
+ PrefixShift = 8
+ IntermedShift = 16
+ FinalMask = 0xff
+ HasMoreFlag = math.MinInt32
+ ParamMask = ^HasMoreFlag
+ MissingParam = ParamMask
+ MissingCommand = MissingParam
+ MaxParam = math.MaxUint16 // the maximum value a parameter can have
+)
+
+const (
+ // MaxParamsSize is the maximum number of parameters a sequence can have.
+ MaxParamsSize = 32
+
+ // DefaultParamValue is the default value used for missing parameters.
+ DefaultParamValue = 0
+)
+
+// Prefix returns the prefix byte of the sequence.
+// This is always gonna be one of the following '<' '=' '>' '?' and in the
+// range of 0x3C-0x3F.
+// Zero is returned if the sequence does not have a prefix.
+func Prefix(cmd int) int {
+ return (cmd >> PrefixShift) & FinalMask
+}
+
+// Intermediate returns the intermediate byte of the sequence.
+// An intermediate byte is in the range of 0x20-0x2F. This includes these
+// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+',
+// ',', '-', '.', '/'.
+// Zero is returned if the sequence does not have an intermediate byte.
+func Intermediate(cmd int) int {
+ return (cmd >> IntermedShift) & FinalMask
+}
+
+// Command returns the command byte of the CSI sequence.
+func Command(cmd int) int {
+ return cmd & FinalMask
+}
+
+// Param returns the parameter at the given index.
+// It returns -1 if the parameter does not exist.
+func Param(params []int, i int) int {
+ if len(params) == 0 || i < 0 || i >= len(params) {
+ return -1
+ }
+
+ p := params[i] & ParamMask
+ if p == MissingParam {
+ return -1
+ }
+
+ return p
+}
+
+// HasMore returns true if the parameter has more sub-parameters.
+func HasMore(params []int, i int) bool {
+ if len(params) == 0 || i >= len(params) {
+ return false
+ }
+
+ return params[i]&HasMoreFlag != 0
+}
+
+// Subparams returns the sub-parameters of the given parameter.
+// It returns nil if the parameter does not exist.
+func Subparams(params []int, i int) []int {
+ if len(params) == 0 || i < 0 || i >= len(params) {
+ return nil
+ }
+
+ // Count the number of parameters before the given parameter index.
+ var count int
+ var j int
+ for j = 0; j < len(params); j++ {
+ if count == i {
+ break
+ }
+ if !HasMore(params, j) {
+ count++
+ }
+ }
+
+ if count > i || j >= len(params) {
+ return nil
+ }
+
+ var subs []int
+ for ; j < len(params); j++ {
+ if !HasMore(params, j) {
+ break
+ }
+ p := Param(params, j)
+ if p == -1 {
+ p = DefaultParamValue
+ }
+ subs = append(subs, p)
+ }
+
+ p := Param(params, j)
+ if p == -1 {
+ p = DefaultParamValue
+ }
+
+ return append(subs, p)
+}
+
+// Len returns the number of parameters in the sequence.
+// This will return the number of parameters in the sequence, excluding any
+// sub-parameters.
+func Len(params []int) int {
+ var n int
+ for i := 0; i < len(params); i++ {
+ if !HasMore(params, i) {
+ n++
+ }
+ }
+ return n
+}
+
+// Range iterates over the parameters of the sequence and calls the given
+// function for each parameter.
+// The function should return false to stop the iteration.
+func Range(params []int, fn func(i int, param int, hasMore bool) bool) {
+ for i := 0; i < len(params); i++ {
+ if !fn(i, Param(params, i), HasMore(params, i)) {
+ break
+ }
+ }
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go b/vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go
new file mode 100644
index 0000000..558a5ea
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/parser/transition_table.go
@@ -0,0 +1,273 @@
+package parser
+
+// Table values are generated like this:
+//
+// index: currentState << IndexStateShift | charCode
+// value: action << TransitionActionShift | nextState
+const (
+ TransitionActionShift = 4
+ TransitionStateMask = 15
+ IndexStateShift = 8
+
+ // DefaultTableSize is the default size of the transition table.
+ DefaultTableSize = 4096
+)
+
+// Table is a DEC ANSI transition table.
+var Table = GenerateTransitionTable()
+
+// TransitionTable is a DEC ANSI transition table.
+// https://vt100.net/emu/dec_ansi_parser
+type TransitionTable []byte
+
+// NewTransitionTable returns a new DEC ANSI transition table.
+func NewTransitionTable(size int) TransitionTable {
+ if size <= 0 {
+ size = DefaultTableSize
+ }
+ return TransitionTable(make([]byte, size))
+}
+
+// SetDefault sets default transition.
+func (t TransitionTable) SetDefault(action Action, state State) {
+ for i := 0; i < len(t); i++ {
+ t[i] = action<<TransitionActionShift | state
+ }
+}
+
+// AddOne adds a transition.
+func (t TransitionTable) AddOne(code byte, state State, action Action, next State) {
+ idx := int(state)<<IndexStateShift | int(code)
+ value := action<<TransitionActionShift | next
+ t[idx] = value
+}
+
+// AddMany adds many transitions.
+func (t TransitionTable) AddMany(codes []byte, state State, action Action, next State) {
+ for _, code := range codes {
+ t.AddOne(code, state, action, next)
+ }
+}
+
+// AddRange adds a range of transitions.
+func (t TransitionTable) AddRange(start, end byte, state State, action Action, next State) {
+ for i := int(start); i <= int(end); i++ {
+ t.AddOne(byte(i), state, action, next)
+ }
+}
+
+// Transition returns the next state and action for the given state and byte.
+func (t TransitionTable) Transition(state State, code byte) (State, Action) {
+ index := int(state)<<IndexStateShift | int(code)
+ value := t[index]
+ return value & TransitionStateMask, value >> TransitionActionShift
+}
+
+// byte range macro
+func r(start, end byte) []byte {
+ var a []byte
+ for i := int(start); i <= int(end); i++ {
+ a = append(a, byte(i))
+ }
+ return a
+}
+
+// GenerateTransitionTable generates a DEC ANSI transition table compatible
+// with the VT500-series of terminals. This implementation includes a few
+// modifications that include:
+// - A new Utf8State is introduced to handle UTF8 sequences.
+// - Osc and Dcs data accept UTF8 sequences by extending the printable range
+// to 0xFF and 0xFE respectively.
+// - We don't ignore 0x3A (':') when building Csi and Dcs parameters and
+// instead use it to denote sub-parameters.
+// - Support dispatching SosPmApc sequences.
+// - The DEL (0x7F) character is executed in the Ground state.
+// - The DEL (0x7F) character is collected in the DcsPassthrough string state.
+// - The ST C1 control character (0x9C) is executed and not ignored.
+func GenerateTransitionTable() TransitionTable {
+ table := NewTransitionTable(DefaultTableSize)
+ table.SetDefault(NoneAction, GroundState)
+
+ // Anywhere
+ for _, state := range r(GroundState, Utf8State) {
+ // Anywhere -> Ground
+ table.AddMany([]byte{0x18, 0x1a, 0x99, 0x9a}, state, ExecuteAction, GroundState)
+ table.AddRange(0x80, 0x8F, state, ExecuteAction, GroundState)
+ table.AddRange(0x90, 0x97, state, ExecuteAction, GroundState)
+ table.AddOne(0x9C, state, ExecuteAction, GroundState)
+ // Anywhere -> Escape
+ table.AddOne(0x1B, state, ClearAction, EscapeState)
+ // Anywhere -> SosStringState
+ table.AddOne(0x98, state, StartAction, SosStringState)
+ // Anywhere -> PmStringState
+ table.AddOne(0x9E, state, StartAction, PmStringState)
+ // Anywhere -> ApcStringState
+ table.AddOne(0x9F, state, StartAction, ApcStringState)
+ // Anywhere -> CsiEntry
+ table.AddOne(0x9B, state, ClearAction, CsiEntryState)
+ // Anywhere -> DcsEntry
+ table.AddOne(0x90, state, ClearAction, DcsEntryState)
+ // Anywhere -> OscString
+ table.AddOne(0x9D, state, StartAction, OscStringState)
+ // Anywhere -> Utf8
+ table.AddRange(0xC2, 0xDF, state, CollectAction, Utf8State) // UTF8 2 byte sequence
+ table.AddRange(0xE0, 0xEF, state, CollectAction, Utf8State) // UTF8 3 byte sequence
+ table.AddRange(0xF0, 0xF4, state, CollectAction, Utf8State) // UTF8 4 byte sequence
+ }
+
+ // Ground
+ table.AddRange(0x00, 0x17, GroundState, ExecuteAction, GroundState)
+ table.AddOne(0x19, GroundState, ExecuteAction, GroundState)
+ table.AddRange(0x1C, 0x1F, GroundState, ExecuteAction, GroundState)
+ table.AddRange(0x20, 0x7E, GroundState, PrintAction, GroundState)
+ table.AddOne(0x7F, GroundState, ExecuteAction, GroundState)
+
+ // EscapeIntermediate
+ table.AddRange(0x00, 0x17, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
+ table.AddOne(0x19, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
+ table.AddRange(0x1C, 0x1F, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
+ table.AddRange(0x20, 0x2F, EscapeIntermediateState, CollectAction, EscapeIntermediateState)
+ table.AddOne(0x7F, EscapeIntermediateState, IgnoreAction, EscapeIntermediateState)
+ // EscapeIntermediate -> Ground
+ table.AddRange(0x30, 0x7E, EscapeIntermediateState, DispatchAction, GroundState)
+
+ // Escape
+ table.AddRange(0x00, 0x17, EscapeState, ExecuteAction, EscapeState)
+ table.AddOne(0x19, EscapeState, ExecuteAction, EscapeState)
+ table.AddRange(0x1C, 0x1F, EscapeState, ExecuteAction, EscapeState)
+ table.AddOne(0x7F, EscapeState, IgnoreAction, EscapeState)
+ // Escape -> Ground
+ table.AddRange(0x30, 0x4F, EscapeState, DispatchAction, GroundState)
+ table.AddRange(0x51, 0x57, EscapeState, DispatchAction, GroundState)
+ table.AddOne(0x59, EscapeState, DispatchAction, GroundState)
+ table.AddOne(0x5A, EscapeState, DispatchAction, GroundState)
+ table.AddOne(0x5C, EscapeState, DispatchAction, GroundState)
+ table.AddRange(0x60, 0x7E, EscapeState, DispatchAction, GroundState)
+ // Escape -> Escape_intermediate
+ table.AddRange(0x20, 0x2F, EscapeState, CollectAction, EscapeIntermediateState)
+ // Escape -> Sos_pm_apc_string
+ table.AddOne('X', EscapeState, StartAction, SosStringState) // SOS
+ table.AddOne('^', EscapeState, StartAction, PmStringState) // PM
+ table.AddOne('_', EscapeState, StartAction, ApcStringState) // APC
+ // Escape -> Dcs_entry
+ table.AddOne('P', EscapeState, ClearAction, DcsEntryState)
+ // Escape -> Csi_entry
+ table.AddOne('[', EscapeState, ClearAction, CsiEntryState)
+ // Escape -> Osc_string
+ table.AddOne(']', EscapeState, StartAction, OscStringState)
+
+ // Sos_pm_apc_string
+ for _, state := range r(SosStringState, ApcStringState) {
+ table.AddRange(0x00, 0x17, state, PutAction, state)
+ table.AddOne(0x19, state, PutAction, state)
+ table.AddRange(0x1C, 0x1F, state, PutAction, state)
+ table.AddRange(0x20, 0x7F, state, PutAction, state)
+ // ESC, ST, CAN, and SUB terminate the sequence
+ table.AddOne(0x1B, state, DispatchAction, EscapeState)
+ table.AddOne(0x9C, state, DispatchAction, GroundState)
+ table.AddMany([]byte{0x18, 0x1A}, state, IgnoreAction, GroundState)
+ }
+
+ // Dcs_entry
+ table.AddRange(0x00, 0x07, DcsEntryState, IgnoreAction, DcsEntryState)
+ table.AddRange(0x0E, 0x17, DcsEntryState, IgnoreAction, DcsEntryState)
+ table.AddOne(0x19, DcsEntryState, IgnoreAction, DcsEntryState)
+ table.AddRange(0x1C, 0x1F, DcsEntryState, IgnoreAction, DcsEntryState)
+ table.AddOne(0x7F, DcsEntryState, IgnoreAction, DcsEntryState)
+ // Dcs_entry -> Dcs_intermediate
+ table.AddRange(0x20, 0x2F, DcsEntryState, CollectAction, DcsIntermediateState)
+ // Dcs_entry -> Dcs_param
+ table.AddRange(0x30, 0x3B, DcsEntryState, ParamAction, DcsParamState)
+ table.AddRange(0x3C, 0x3F, DcsEntryState, PrefixAction, DcsParamState)
+ // Dcs_entry -> Dcs_passthrough
+ table.AddRange(0x08, 0x0D, DcsEntryState, PutAction, DcsStringState) // Follows ECMA-48 § 8.3.27
+ // XXX: allows passing ESC (not a ECMA-48 standard) this to allow for
+ // passthrough of ANSI sequences like in Screen or Tmux passthrough mode.
+ table.AddOne(0x1B, DcsEntryState, PutAction, DcsStringState)
+ table.AddRange(0x40, 0x7E, DcsEntryState, StartAction, DcsStringState)
+
+ // Dcs_intermediate
+ table.AddRange(0x00, 0x17, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
+ table.AddOne(0x19, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
+ table.AddRange(0x1C, 0x1F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
+ table.AddRange(0x20, 0x2F, DcsIntermediateState, CollectAction, DcsIntermediateState)
+ table.AddOne(0x7F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
+ // Dcs_intermediate -> Dcs_passthrough
+ table.AddRange(0x30, 0x3F, DcsIntermediateState, StartAction, DcsStringState)
+ table.AddRange(0x40, 0x7E, DcsIntermediateState, StartAction, DcsStringState)
+
+ // Dcs_param
+ table.AddRange(0x00, 0x17, DcsParamState, IgnoreAction, DcsParamState)
+ table.AddOne(0x19, DcsParamState, IgnoreAction, DcsParamState)
+ table.AddRange(0x1C, 0x1F, DcsParamState, IgnoreAction, DcsParamState)
+ table.AddRange(0x30, 0x3B, DcsParamState, ParamAction, DcsParamState)
+ table.AddOne(0x7F, DcsParamState, IgnoreAction, DcsParamState)
+ table.AddRange(0x3C, 0x3F, DcsParamState, IgnoreAction, DcsParamState)
+ // Dcs_param -> Dcs_intermediate
+ table.AddRange(0x20, 0x2F, DcsParamState, CollectAction, DcsIntermediateState)
+ // Dcs_param -> Dcs_passthrough
+ table.AddRange(0x40, 0x7E, DcsParamState, StartAction, DcsStringState)
+
+ // Dcs_passthrough
+ table.AddRange(0x00, 0x17, DcsStringState, PutAction, DcsStringState)
+ table.AddOne(0x19, DcsStringState, PutAction, DcsStringState)
+ table.AddRange(0x1C, 0x1F, DcsStringState, PutAction, DcsStringState)
+ table.AddRange(0x20, 0x7E, DcsStringState, PutAction, DcsStringState)
+ table.AddOne(0x7F, DcsStringState, PutAction, DcsStringState)
+ table.AddRange(0x80, 0xFF, DcsStringState, PutAction, DcsStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF
+ // ST, CAN, SUB, and ESC terminate the sequence
+ table.AddOne(0x1B, DcsStringState, DispatchAction, EscapeState)
+ table.AddOne(0x9C, DcsStringState, DispatchAction, GroundState)
+ table.AddMany([]byte{0x18, 0x1A}, DcsStringState, IgnoreAction, GroundState)
+
+ // Csi_param
+ table.AddRange(0x00, 0x17, CsiParamState, ExecuteAction, CsiParamState)
+ table.AddOne(0x19, CsiParamState, ExecuteAction, CsiParamState)
+ table.AddRange(0x1C, 0x1F, CsiParamState, ExecuteAction, CsiParamState)
+ table.AddRange(0x30, 0x3B, CsiParamState, ParamAction, CsiParamState)
+ table.AddOne(0x7F, CsiParamState, IgnoreAction, CsiParamState)
+ table.AddRange(0x3C, 0x3F, CsiParamState, IgnoreAction, CsiParamState)
+ // Csi_param -> Ground
+ table.AddRange(0x40, 0x7E, CsiParamState, DispatchAction, GroundState)
+ // Csi_param -> Csi_intermediate
+ table.AddRange(0x20, 0x2F, CsiParamState, CollectAction, CsiIntermediateState)
+
+ // Csi_intermediate
+ table.AddRange(0x00, 0x17, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
+ table.AddOne(0x19, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
+ table.AddRange(0x1C, 0x1F, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
+ table.AddRange(0x20, 0x2F, CsiIntermediateState, CollectAction, CsiIntermediateState)
+ table.AddOne(0x7F, CsiIntermediateState, IgnoreAction, CsiIntermediateState)
+ // Csi_intermediate -> Ground
+ table.AddRange(0x40, 0x7E, CsiIntermediateState, DispatchAction, GroundState)
+ // Csi_intermediate -> Csi_ignore
+ table.AddRange(0x30, 0x3F, CsiIntermediateState, IgnoreAction, GroundState)
+
+ // Csi_entry
+ table.AddRange(0x00, 0x17, CsiEntryState, ExecuteAction, CsiEntryState)
+ table.AddOne(0x19, CsiEntryState, ExecuteAction, CsiEntryState)
+ table.AddRange(0x1C, 0x1F, CsiEntryState, ExecuteAction, CsiEntryState)
+ table.AddOne(0x7F, CsiEntryState, IgnoreAction, CsiEntryState)
+ // Csi_entry -> Ground
+ table.AddRange(0x40, 0x7E, CsiEntryState, DispatchAction, GroundState)
+ // Csi_entry -> Csi_intermediate
+ table.AddRange(0x20, 0x2F, CsiEntryState, CollectAction, CsiIntermediateState)
+ // Csi_entry -> Csi_param
+ table.AddRange(0x30, 0x3B, CsiEntryState, ParamAction, CsiParamState)
+ table.AddRange(0x3C, 0x3F, CsiEntryState, PrefixAction, CsiParamState)
+
+ // Osc_string
+ table.AddRange(0x00, 0x06, OscStringState, IgnoreAction, OscStringState)
+ table.AddRange(0x08, 0x17, OscStringState, IgnoreAction, OscStringState)
+ table.AddOne(0x19, OscStringState, IgnoreAction, OscStringState)
+ table.AddRange(0x1C, 0x1F, OscStringState, IgnoreAction, OscStringState)
+ table.AddRange(0x20, 0xFF, OscStringState, PutAction, OscStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF
+
+ // ST, CAN, SUB, ESC, and BEL terminate the sequence
+ table.AddOne(0x1B, OscStringState, DispatchAction, EscapeState)
+ table.AddOne(0x07, OscStringState, DispatchAction, GroundState)
+ table.AddOne(0x9C, OscStringState, DispatchAction, GroundState)
+ table.AddMany([]byte{0x18, 0x1A}, OscStringState, IgnoreAction, GroundState)
+
+ return table
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/parser_decode.go b/vendor/github.com/charmbracelet/x/ansi/parser_decode.go
new file mode 100644
index 0000000..3e50473
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/parser_decode.go
@@ -0,0 +1,524 @@
+package ansi
+
+import (
+ "unicode/utf8"
+
+ "github.com/charmbracelet/x/ansi/parser"
+ "github.com/mattn/go-runewidth"
+ "github.com/rivo/uniseg"
+)
+
+// State represents the state of the ANSI escape sequence parser used by
+// [DecodeSequence].
+type State = byte
+
+// ANSI escape sequence states used by [DecodeSequence].
+const (
+ NormalState State = iota
+ PrefixState
+ ParamsState
+ IntermedState
+ EscapeState
+ StringState
+)
+
+// DecodeSequence decodes the first ANSI escape sequence or a printable
+// grapheme from the given data. It returns the sequence slice, the number of
+// bytes read, the cell width for each sequence, and the new state.
+//
+// The cell width will always be 0 for control and escape sequences, 1 for
+// ASCII printable characters, and the number of cells other Unicode characters
+// occupy. It uses the uniseg package to calculate the width of Unicode
+// graphemes and characters. This means it will always do grapheme clustering
+// (mode 2027).
+//
+// Passing a non-nil [*Parser] as the last argument will allow the decoder to
+// collect sequence parameters, data, and commands. The parser cmd will have
+// the packed command value that contains intermediate and prefix characters.
+// In the case of a OSC sequence, the cmd will be the OSC command number. Use
+// [Cmd] and [Param] types to unpack command intermediates and prefixes as well
+// as parameters.
+//
+// Zero [Cmd] means the CSI, DCS, or ESC sequence is invalid. Moreover, checking the
+// validity of other data sequences, OSC, DCS, etc, will require checking for
+// the returned sequence terminator bytes such as ST (ESC \\) and BEL).
+//
+// We store the command byte in [Cmd] in the most significant byte, the
+// prefix byte in the next byte, and the intermediate byte in the least
+// significant byte. This is done to avoid using a struct to store the command
+// and its intermediates and prefixes. The command byte is always the least
+// significant byte i.e. [Cmd & 0xff]. Use the [Cmd] type to unpack the
+// command, intermediate, and prefix bytes. Note that we only collect the last
+// prefix character and intermediate byte.
+//
+// The [p.Params] slice will contain the parameters of the sequence. Any
+// sub-parameter will have the [parser.HasMoreFlag] set. Use the [Param] type
+// to unpack the parameters.
+//
+// Example:
+//
+// var state byte // the initial state is always zero [NormalState]
+// p := NewParser(32, 1024) // create a new parser with a 32 params buffer and 1024 data buffer (optional)
+// input := []byte("\x1b[31mHello, World!\x1b[0m")
+// for len(input) > 0 {
+// seq, width, n, newState := DecodeSequence(input, state, p)
+// log.Printf("seq: %q, width: %d", seq, width)
+// state = newState
+// input = input[n:]
+// }
+//
+// This function treats the text as a sequence of grapheme clusters.
+func DecodeSequence[T string | []byte](b T, state byte, p *Parser) (seq T, width int, n int, newState byte) {
+ return decodeSequence(GraphemeWidth, b, state, p)
+}
+
+// DecodeSequenceWc decodes the first ANSI escape sequence or a printable
+// grapheme from the given data. It returns the sequence slice, the number of
+// bytes read, the cell width for each sequence, and the new state.
+//
+// The cell width will always be 0 for control and escape sequences, 1 for
+// ASCII printable characters, and the number of cells other Unicode characters
+// occupy. It uses the uniseg package to calculate the width of Unicode
+// graphemes and characters. This means it will always do grapheme clustering
+// (mode 2027).
+//
+// Passing a non-nil [*Parser] as the last argument will allow the decoder to
+// collect sequence parameters, data, and commands. The parser cmd will have
+// the packed command value that contains intermediate and prefix characters.
+// In the case of a OSC sequence, the cmd will be the OSC command number. Use
+// [Cmd] and [Param] types to unpack command intermediates and prefixes as well
+// as parameters.
+//
+// Zero [Cmd] means the CSI, DCS, or ESC sequence is invalid. Moreover, checking the
+// validity of other data sequences, OSC, DCS, etc, will require checking for
+// the returned sequence terminator bytes such as ST (ESC \\) and BEL).
+//
+// We store the command byte in [Cmd] in the most significant byte, the
+// prefix byte in the next byte, and the intermediate byte in the least
+// significant byte. This is done to avoid using a struct to store the command
+// and its intermediates and prefixes. The command byte is always the least
+// significant byte i.e. [Cmd & 0xff]. Use the [Cmd] type to unpack the
+// command, intermediate, and prefix bytes. Note that we only collect the last
+// prefix character and intermediate byte.
+//
+// The [p.Params] slice will contain the parameters of the sequence. Any
+// sub-parameter will have the [parser.HasMoreFlag] set. Use the [Param] type
+// to unpack the parameters.
+//
+// Example:
+//
+// var state byte // the initial state is always zero [NormalState]
+// p := NewParser(32, 1024) // create a new parser with a 32 params buffer and 1024 data buffer (optional)
+// input := []byte("\x1b[31mHello, World!\x1b[0m")
+// for len(input) > 0 {
+// seq, width, n, newState := DecodeSequenceWc(input, state, p)
+// log.Printf("seq: %q, width: %d", seq, width)
+// state = newState
+// input = input[n:]
+// }
+//
+// This function treats the text as a sequence of wide characters and runes.
+func DecodeSequenceWc[T string | []byte](b T, state byte, p *Parser) (seq T, width int, n int, newState byte) {
+ return decodeSequence(WcWidth, b, state, p)
+}
+
+func decodeSequence[T string | []byte](m Method, b T, state State, p *Parser) (seq T, width int, n int, newState byte) {
+ for i := 0; i < len(b); i++ {
+ c := b[i]
+
+ switch state {
+ case NormalState:
+ switch c {
+ case ESC:
+ if p != nil {
+ if len(p.params) > 0 {
+ p.params[0] = parser.MissingParam
+ }
+ p.cmd = 0
+ p.paramsLen = 0
+ p.dataLen = 0
+ }
+ state = EscapeState
+ continue
+ case CSI, DCS:
+ if p != nil {
+ if len(p.params) > 0 {
+ p.params[0] = parser.MissingParam
+ }
+ p.cmd = 0
+ p.paramsLen = 0
+ p.dataLen = 0
+ }
+ state = PrefixState
+ continue
+ case OSC, APC, SOS, PM:
+ if p != nil {
+ p.cmd = parser.MissingCommand
+ p.dataLen = 0
+ }
+ state = StringState
+ continue
+ }
+
+ if p != nil {
+ p.dataLen = 0
+ p.paramsLen = 0
+ p.cmd = 0
+ }
+ if c > US && c < DEL {
+ // ASCII printable characters
+ return b[i : i+1], 1, 1, NormalState
+ }
+
+ if c <= US || c == DEL || c < 0xC0 {
+ // C0 & C1 control characters & DEL
+ return b[i : i+1], 0, 1, NormalState
+ }
+
+ if utf8.RuneStart(c) {
+ seq, _, width, _ = FirstGraphemeCluster(b, -1)
+ if m == WcWidth {
+ width = runewidth.StringWidth(string(seq))
+ }
+ i += len(seq)
+ return b[:i], width, i, NormalState
+ }
+
+ // Invalid UTF-8 sequence
+ return b[:i], 0, i, NormalState
+ case PrefixState:
+ if c >= '<' && c <= '?' {
+ if p != nil {
+ // We only collect the last prefix character.
+ p.cmd &^= 0xff << parser.PrefixShift
+ p.cmd |= int(c) << parser.PrefixShift
+ }
+ break
+ }
+
+ state = ParamsState
+ fallthrough
+ case ParamsState:
+ if c >= '0' && c <= '9' {
+ if p != nil {
+ if p.params[p.paramsLen] == parser.MissingParam {
+ p.params[p.paramsLen] = 0
+ }
+
+ p.params[p.paramsLen] *= 10
+ p.params[p.paramsLen] += int(c - '0')
+ }
+ break
+ }
+
+ if c == ':' {
+ if p != nil {
+ p.params[p.paramsLen] |= parser.HasMoreFlag
+ }
+ }
+
+ if c == ';' || c == ':' {
+ if p != nil {
+ p.paramsLen++
+ if p.paramsLen < len(p.params) {
+ p.params[p.paramsLen] = parser.MissingParam
+ }
+ }
+ break
+ }
+
+ state = IntermedState
+ fallthrough
+ case IntermedState:
+ if c >= ' ' && c <= '/' {
+ if p != nil {
+ p.cmd &^= 0xff << parser.IntermedShift
+ p.cmd |= int(c) << parser.IntermedShift
+ }
+ break
+ }
+
+ if p != nil {
+ // Increment the last parameter
+ if p.paramsLen > 0 && p.paramsLen < len(p.params)-1 ||
+ p.paramsLen == 0 && len(p.params) > 0 && p.params[0] != parser.MissingParam {
+ p.paramsLen++
+ }
+ }
+
+ if c >= '@' && c <= '~' {
+ if p != nil {
+ p.cmd &^= 0xff
+ p.cmd |= int(c)
+ }
+
+ if HasDcsPrefix(b) {
+ // Continue to collect DCS data
+ if p != nil {
+ p.dataLen = 0
+ }
+ state = StringState
+ continue
+ }
+
+ return b[:i+1], 0, i + 1, NormalState
+ }
+
+ // Invalid CSI/DCS sequence
+ return b[:i], 0, i, NormalState
+ case EscapeState:
+ switch c {
+ case '[', 'P':
+ if p != nil {
+ if len(p.params) > 0 {
+ p.params[0] = parser.MissingParam
+ }
+ p.paramsLen = 0
+ p.cmd = 0
+ }
+ state = PrefixState
+ continue
+ case ']', 'X', '^', '_':
+ if p != nil {
+ p.cmd = parser.MissingCommand
+ p.dataLen = 0
+ }
+ state = StringState
+ continue
+ }
+
+ if c >= ' ' && c <= '/' {
+ if p != nil {
+ p.cmd &^= 0xff << parser.IntermedShift
+ p.cmd |= int(c) << parser.IntermedShift
+ }
+ continue
+ } else if c >= '0' && c <= '~' {
+ if p != nil {
+ p.cmd &^= 0xff
+ p.cmd |= int(c)
+ }
+ return b[:i+1], 0, i + 1, NormalState
+ }
+
+ // Invalid escape sequence
+ return b[:i], 0, i, NormalState
+ case StringState:
+ switch c {
+ case BEL:
+ if HasOscPrefix(b) {
+ parseOscCmd(p)
+ return b[:i+1], 0, i + 1, NormalState
+ }
+ case CAN, SUB:
+ if HasOscPrefix(b) {
+ // Ensure we parse the OSC command number
+ parseOscCmd(p)
+ }
+
+ // Cancel the sequence
+ return b[:i], 0, i, NormalState
+ case ST:
+ if HasOscPrefix(b) {
+ // Ensure we parse the OSC command number
+ parseOscCmd(p)
+ }
+
+ return b[:i+1], 0, i + 1, NormalState
+ case ESC:
+ if HasStPrefix(b[i:]) {
+ if HasOscPrefix(b) {
+ // Ensure we parse the OSC command number
+ parseOscCmd(p)
+ }
+
+ // End of string 7-bit (ST)
+ return b[:i+2], 0, i + 2, NormalState
+ }
+
+ // Otherwise, cancel the sequence
+ return b[:i], 0, i, NormalState
+ }
+
+ if p != nil && p.dataLen < len(p.data) {
+ p.data[p.dataLen] = c
+ p.dataLen++
+
+ // Parse the OSC command number
+ if c == ';' && HasOscPrefix(b) {
+ parseOscCmd(p)
+ }
+ }
+ }
+ }
+
+ return b, 0, len(b), state
+}
+
+func parseOscCmd(p *Parser) {
+ if p == nil || p.cmd != parser.MissingCommand {
+ return
+ }
+ for j := 0; j < p.dataLen; j++ {
+ d := p.data[j]
+ if d < '0' || d > '9' {
+ break
+ }
+ if p.cmd == parser.MissingCommand {
+ p.cmd = 0
+ }
+ p.cmd *= 10
+ p.cmd += int(d - '0')
+ }
+}
+
+// Equal returns true if the given byte slices are equal.
+func Equal[T string | []byte](a, b T) bool {
+ return string(a) == string(b)
+}
+
+// HasPrefix returns true if the given byte slice has prefix.
+func HasPrefix[T string | []byte](b, prefix T) bool {
+ return len(b) >= len(prefix) && Equal(b[0:len(prefix)], prefix)
+}
+
+// HasSuffix returns true if the given byte slice has suffix.
+func HasSuffix[T string | []byte](b, suffix T) bool {
+ return len(b) >= len(suffix) && Equal(b[len(b)-len(suffix):], suffix)
+}
+
+// HasCsiPrefix returns true if the given byte slice has a CSI prefix.
+func HasCsiPrefix[T string | []byte](b T) bool {
+ return (len(b) > 0 && b[0] == CSI) ||
+ (len(b) > 1 && b[0] == ESC && b[1] == '[')
+}
+
+// HasOscPrefix returns true if the given byte slice has an OSC prefix.
+func HasOscPrefix[T string | []byte](b T) bool {
+ return (len(b) > 0 && b[0] == OSC) ||
+ (len(b) > 1 && b[0] == ESC && b[1] == ']')
+}
+
+// HasApcPrefix returns true if the given byte slice has an APC prefix.
+func HasApcPrefix[T string | []byte](b T) bool {
+ return (len(b) > 0 && b[0] == APC) ||
+ (len(b) > 1 && b[0] == ESC && b[1] == '_')
+}
+
+// HasDcsPrefix returns true if the given byte slice has a DCS prefix.
+func HasDcsPrefix[T string | []byte](b T) bool {
+ return (len(b) > 0 && b[0] == DCS) ||
+ (len(b) > 1 && b[0] == ESC && b[1] == 'P')
+}
+
+// HasSosPrefix returns true if the given byte slice has a SOS prefix.
+func HasSosPrefix[T string | []byte](b T) bool {
+ return (len(b) > 0 && b[0] == SOS) ||
+ (len(b) > 1 && b[0] == ESC && b[1] == 'X')
+}
+
+// HasPmPrefix returns true if the given byte slice has a PM prefix.
+func HasPmPrefix[T string | []byte](b T) bool {
+ return (len(b) > 0 && b[0] == PM) ||
+ (len(b) > 1 && b[0] == ESC && b[1] == '^')
+}
+
+// HasStPrefix returns true if the given byte slice has a ST prefix.
+func HasStPrefix[T string | []byte](b T) bool {
+ return (len(b) > 0 && b[0] == ST) ||
+ (len(b) > 1 && b[0] == ESC && b[1] == '\\')
+}
+
+// HasEscPrefix returns true if the given byte slice has an ESC prefix.
+func HasEscPrefix[T string | []byte](b T) bool {
+ return len(b) > 0 && b[0] == ESC
+}
+
+// FirstGraphemeCluster returns the first grapheme cluster in the given string or byte slice.
+// This is a syntactic sugar function that wraps
+// uniseg.FirstGraphemeClusterInString and uniseg.FirstGraphemeCluster.
+func FirstGraphemeCluster[T string | []byte](b T, state int) (T, T, int, int) {
+ switch b := any(b).(type) {
+ case string:
+ cluster, rest, width, newState := uniseg.FirstGraphemeClusterInString(b, state)
+ return T(cluster), T(rest), width, newState
+ case []byte:
+ cluster, rest, width, newState := uniseg.FirstGraphemeCluster(b, state)
+ return T(cluster), T(rest), width, newState
+ }
+ panic("unreachable")
+}
+
+// Cmd represents a sequence command. This is used to pack/unpack a sequence
+// command with its intermediate and prefix characters. Those are commonly
+// found in CSI and DCS sequences.
+type Cmd int
+
+// Prefix returns the unpacked prefix byte of the CSI sequence.
+// This is always gonna be one of the following '<' '=' '>' '?' and in the
+// range of 0x3C-0x3F.
+// Zero is returned if the sequence does not have a prefix.
+func (c Cmd) Prefix() byte {
+ return byte(parser.Prefix(int(c)))
+}
+
+// Intermediate returns the unpacked intermediate byte of the CSI sequence.
+// An intermediate byte is in the range of 0x20-0x2F. This includes these
+// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+',
+// ',', '-', '.', '/'.
+// Zero is returned if the sequence does not have an intermediate byte.
+func (c Cmd) Intermediate() byte {
+ return byte(parser.Intermediate(int(c)))
+}
+
+// Final returns the unpacked command byte of the CSI sequence.
+func (c Cmd) Final() byte {
+ return byte(parser.Command(int(c)))
+}
+
+// Command packs a command with the given prefix, intermediate, and final. A
+// zero byte means the sequence does not have a prefix or intermediate.
+//
+// Prefixes are in the range of 0x3C-0x3F that is one of `<=>?`.
+//
+// Intermediates are in the range of 0x20-0x2F that is anything in
+// `!"#$%&'()*+,-./`.
+//
+// Final bytes are in the range of 0x40-0x7E that is anything in the range
+// `@A–Z[\]^_`a–z{|}~`.
+func Command(prefix, inter, final byte) (c int) {
+ c = int(final)
+ c |= int(prefix) << parser.PrefixShift
+ c |= int(inter) << parser.IntermedShift
+ return
+}
+
+// Param represents a sequence parameter. Sequence parameters with
+// sub-parameters are packed with the HasMoreFlag set. This is used to unpack
+// the parameters from a CSI and DCS sequences.
+type Param int
+
+// Param returns the unpacked parameter at the given index.
+// It returns the default value if the parameter is missing.
+func (s Param) Param(def int) int {
+ p := int(s) & parser.ParamMask
+ if p == parser.MissingParam {
+ return def
+ }
+ return p
+}
+
+// HasMore unpacks the HasMoreFlag from the parameter.
+func (s Param) HasMore() bool {
+ return s&parser.HasMoreFlag != 0
+}
+
+// Parameter packs an escape code parameter with the given parameter and
+// whether this parameter has following sub-parameters.
+func Parameter(p int, hasMore bool) (s int) {
+ s = p & parser.ParamMask
+ if hasMore {
+ s |= parser.HasMoreFlag
+ }
+ return
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/parser_handler.go b/vendor/github.com/charmbracelet/x/ansi/parser_handler.go
new file mode 100644
index 0000000..03f9ed4
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/parser_handler.go
@@ -0,0 +1,60 @@
+package ansi
+
+import "unsafe"
+
+// Params represents a list of packed parameters.
+type Params []Param
+
+// Param returns the parameter at the given index and if it is part of a
+// sub-parameters. It falls back to the default value if the parameter is
+// missing. If the index is out of bounds, it returns the default value and
+// false.
+func (p Params) Param(i, def int) (int, bool, bool) {
+ if i < 0 || i >= len(p) {
+ return def, false, false
+ }
+ return p[i].Param(def), p[i].HasMore(), true
+}
+
+// ForEach iterates over the parameters and calls the given function for each
+// parameter. If a parameter is part of a sub-parameter, it will be called with
+// hasMore set to true.
+// Use def to set a default value for missing parameters.
+func (p Params) ForEach(def int, f func(i, param int, hasMore bool)) {
+ for i := range p {
+ f(i, p[i].Param(def), p[i].HasMore())
+ }
+}
+
+// ToParams converts a list of integers to a list of parameters.
+func ToParams(params []int) Params {
+ return unsafe.Slice((*Param)(unsafe.Pointer(&params[0])), len(params))
+}
+
+// Handler handles actions performed by the parser.
+// It is used to handle ANSI escape sequences, control characters, and runes.
+type Handler struct {
+ // Print is called when a printable rune is encountered.
+ Print func(r rune)
+ // Execute is called when a control character is encountered.
+ Execute func(b byte)
+ // HandleCsi is called when a CSI sequence is encountered.
+ HandleCsi func(cmd Cmd, params Params)
+ // HandleEsc is called when an ESC sequence is encountered.
+ HandleEsc func(cmd Cmd)
+ // HandleDcs is called when a DCS sequence is encountered.
+ HandleDcs func(cmd Cmd, params Params, data []byte)
+ // HandleOsc is called when an OSC sequence is encountered.
+ HandleOsc func(cmd int, data []byte)
+ // HandlePm is called when a PM sequence is encountered.
+ HandlePm func(data []byte)
+ // HandleApc is called when an APC sequence is encountered.
+ HandleApc func(data []byte)
+ // HandleSos is called when a SOS sequence is encountered.
+ HandleSos func(data []byte)
+}
+
+// SetHandler sets the handler for the parser.
+func (p *Parser) SetHandler(h Handler) {
+ p.handler = h
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/parser_sync.go b/vendor/github.com/charmbracelet/x/ansi/parser_sync.go
new file mode 100644
index 0000000..65d25a9
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/parser_sync.go
@@ -0,0 +1,29 @@
+package ansi
+
+import (
+ "sync"
+
+ "github.com/charmbracelet/x/ansi/parser"
+)
+
+var parserPool = sync.Pool{
+ New: func() any {
+ p := NewParser()
+ p.SetParamsSize(parser.MaxParamsSize)
+ p.SetDataSize(1024 * 1024 * 4) // 4MB of data buffer
+ return p
+ },
+}
+
+// GetParser returns a parser from a sync pool.
+func GetParser() *Parser {
+ return parserPool.Get().(*Parser)
+}
+
+// PutParser returns a parser to a sync pool. The parser is reset
+// automatically.
+func PutParser(p *Parser) {
+ p.Reset()
+ p.dataLen = 0
+ parserPool.Put(p)
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/passthrough.go b/vendor/github.com/charmbracelet/x/ansi/passthrough.go
new file mode 100644
index 0000000..14a7452
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/passthrough.go
@@ -0,0 +1,63 @@
+package ansi
+
+import (
+ "bytes"
+)
+
+// ScreenPassthrough wraps the given ANSI sequence in a DCS passthrough
+// sequence to be sent to the outer terminal. This is used to send raw escape
+// sequences to the outer terminal when running inside GNU Screen.
+//
+// DCS <data> ST
+//
+// Note: Screen limits the length of string sequences to 768 bytes (since 2014).
+// Use zero to indicate no limit, otherwise, this will chunk the returned
+// string into limit sized chunks.
+//
+// See: https://www.gnu.org/software/screen/manual/screen.html#String-Escapes
+// See: https://git.savannah.gnu.org/cgit/screen.git/tree/src/screen.h?id=c184c6ec27683ff1a860c45be5cf520d896fd2ef#n44
+func ScreenPassthrough(seq string, limit int) string {
+ var b bytes.Buffer
+ b.WriteString("\x1bP")
+ if limit > 0 {
+ for i := 0; i < len(seq); i += limit {
+ end := i + limit
+ if end > len(seq) {
+ end = len(seq)
+ }
+ b.WriteString(seq[i:end])
+ if end < len(seq) {
+ b.WriteString("\x1b\\\x1bP")
+ }
+ }
+ } else {
+ b.WriteString(seq)
+ }
+ b.WriteString("\x1b\\")
+ return b.String()
+}
+
+// TmuxPassthrough wraps the given ANSI sequence in a special DCS passthrough
+// sequence to be sent to the outer terminal. This is used to send raw escape
+// sequences to the outer terminal when running inside Tmux.
+//
+// DCS tmux ; <escaped-data> ST
+//
+// Where <escaped-data> is the given sequence in which all occurrences of ESC
+// (0x1b) are doubled i.e. replaced with ESC ESC (0x1b 0x1b).
+//
+// Note: this needs the `allow-passthrough` option to be set to `on`.
+//
+// See: https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it
+func TmuxPassthrough(seq string) string {
+ var b bytes.Buffer
+ b.WriteString("\x1bPtmux;")
+ for i := 0; i < len(seq); i++ {
+ if seq[i] == ESC {
+ b.WriteByte(ESC)
+ }
+ b.WriteByte(seq[i])
+ }
+ b.WriteString("\x1b\\")
+ return b.String()
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/paste.go b/vendor/github.com/charmbracelet/x/ansi/paste.go
new file mode 100644
index 0000000..2f9ea6f
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/paste.go
@@ -0,0 +1,7 @@
+package ansi
+
+// BracketedPasteStart is the control sequence to enable bracketed paste mode.
+const BracketedPasteStart = "\x1b[200~"
+
+// BracketedPasteEnd is the control sequence to disable bracketed paste mode.
+const BracketedPasteEnd = "\x1b[201~"
diff --git a/vendor/github.com/charmbracelet/x/ansi/reset.go b/vendor/github.com/charmbracelet/x/ansi/reset.go
new file mode 100644
index 0000000..c1b89ea
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/reset.go
@@ -0,0 +1,11 @@
+package ansi
+
+// ResetInitialState (RIS) resets the terminal to its initial state.
+//
+// ESC c
+//
+// See: https://vt100.net/docs/vt510-rm/RIS.html
+const (
+ ResetInitialState = "\x1bc"
+ RIS = ResetInitialState
+)
diff --git a/vendor/github.com/charmbracelet/x/ansi/screen.go b/vendor/github.com/charmbracelet/x/ansi/screen.go
new file mode 100644
index 0000000..c76e4f0
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/screen.go
@@ -0,0 +1,410 @@
+package ansi
+
+import (
+ "strconv"
+ "strings"
+)
+
+// EraseDisplay (ED) clears the display or parts of the display. A screen is
+// the shown part of the terminal display excluding the scrollback buffer.
+// Possible values:
+//
+// Default is 0.
+//
+// 0: Clear from cursor to end of screen.
+// 1: Clear from cursor to beginning of the screen.
+// 2: Clear entire screen (and moves cursor to upper left on DOS).
+// 3: Clear entire display which delete all lines saved in the scrollback buffer (xterm).
+//
+// CSI <n> J
+//
+// See: https://vt100.net/docs/vt510-rm/ED.html
+func EraseDisplay(n int) string {
+ var s string
+ if n > 0 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "J"
+}
+
+// ED is an alias for [EraseDisplay].
+func ED(n int) string {
+ return EraseDisplay(n)
+}
+
+// EraseDisplay constants.
+// These are the possible values for the EraseDisplay function.
+const (
+ EraseScreenBelow = "\x1b[J"
+ EraseScreenAbove = "\x1b[1J"
+ EraseEntireScreen = "\x1b[2J"
+ EraseEntireDisplay = "\x1b[3J"
+)
+
+// EraseLine (EL) clears the current line or parts of the line. Possible values:
+//
+// 0: Clear from cursor to end of line.
+// 1: Clear from cursor to beginning of the line.
+// 2: Clear entire line.
+//
+// The cursor position is not affected.
+//
+// CSI <n> K
+//
+// See: https://vt100.net/docs/vt510-rm/EL.html
+func EraseLine(n int) string {
+ var s string
+ if n > 0 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "K"
+}
+
+// EL is an alias for [EraseLine].
+func EL(n int) string {
+ return EraseLine(n)
+}
+
+// EraseLine constants.
+// These are the possible values for the EraseLine function.
+const (
+ EraseLineRight = "\x1b[K"
+ EraseLineLeft = "\x1b[1K"
+ EraseEntireLine = "\x1b[2K"
+)
+
+// ScrollUp (SU) scrolls the screen up n lines. New lines are added at the
+// bottom of the screen.
+//
+// CSI Pn S
+//
+// See: https://vt100.net/docs/vt510-rm/SU.html
+func ScrollUp(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "S"
+}
+
+// PanDown is an alias for [ScrollUp].
+func PanDown(n int) string {
+ return ScrollUp(n)
+}
+
+// SU is an alias for [ScrollUp].
+func SU(n int) string {
+ return ScrollUp(n)
+}
+
+// ScrollDown (SD) scrolls the screen down n lines. New lines are added at the
+// top of the screen.
+//
+// CSI Pn T
+//
+// See: https://vt100.net/docs/vt510-rm/SD.html
+func ScrollDown(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "T"
+}
+
+// PanUp is an alias for [ScrollDown].
+func PanUp(n int) string {
+ return ScrollDown(n)
+}
+
+// SD is an alias for [ScrollDown].
+func SD(n int) string {
+ return ScrollDown(n)
+}
+
+// InsertLine (IL) inserts n blank lines at the current cursor position.
+// Existing lines are moved down.
+//
+// CSI Pn L
+//
+// See: https://vt100.net/docs/vt510-rm/IL.html
+func InsertLine(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "L"
+}
+
+// IL is an alias for [InsertLine].
+func IL(n int) string {
+ return InsertLine(n)
+}
+
+// DeleteLine (DL) deletes n lines at the current cursor position. Existing
+// lines are moved up.
+//
+// CSI Pn M
+//
+// See: https://vt100.net/docs/vt510-rm/DL.html
+func DeleteLine(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "M"
+}
+
+// DL is an alias for [DeleteLine].
+func DL(n int) string {
+ return DeleteLine(n)
+}
+
+// SetTopBottomMargins (DECSTBM) sets the top and bottom margins for the scrolling
+// region. The default is the entire screen.
+//
+// Default is 1 and the bottom of the screen.
+//
+// CSI Pt ; Pb r
+//
+// See: https://vt100.net/docs/vt510-rm/DECSTBM.html
+func SetTopBottomMargins(top, bot int) string {
+ var t, b string
+ if top > 0 {
+ t = strconv.Itoa(top)
+ }
+ if bot > 0 {
+ b = strconv.Itoa(bot)
+ }
+ return "\x1b[" + t + ";" + b + "r"
+}
+
+// DECSTBM is an alias for [SetTopBottomMargins].
+func DECSTBM(top, bot int) string {
+ return SetTopBottomMargins(top, bot)
+}
+
+// SetLeftRightMargins (DECSLRM) sets the left and right margins for the scrolling
+// region.
+//
+// Default is 1 and the right of the screen.
+//
+// CSI Pl ; Pr s
+//
+// See: https://vt100.net/docs/vt510-rm/DECSLRM.html
+func SetLeftRightMargins(left, right int) string {
+ var l, r string
+ if left > 0 {
+ l = strconv.Itoa(left)
+ }
+ if right > 0 {
+ r = strconv.Itoa(right)
+ }
+ return "\x1b[" + l + ";" + r + "s"
+}
+
+// DECSLRM is an alias for [SetLeftRightMargins].
+func DECSLRM(left, right int) string {
+ return SetLeftRightMargins(left, right)
+}
+
+// SetScrollingRegion (DECSTBM) sets the top and bottom margins for the scrolling
+// region. The default is the entire screen.
+//
+// CSI <top> ; <bottom> r
+//
+// See: https://vt100.net/docs/vt510-rm/DECSTBM.html
+//
+// Deprecated: use [SetTopBottomMargins] instead.
+func SetScrollingRegion(t, b int) string {
+ if t < 0 {
+ t = 0
+ }
+ if b < 0 {
+ b = 0
+ }
+ return "\x1b[" + strconv.Itoa(t) + ";" + strconv.Itoa(b) + "r"
+}
+
+// InsertCharacter (ICH) inserts n blank characters at the current cursor
+// position. Existing characters move to the right. Characters moved past the
+// right margin are lost. ICH has no effect outside the scrolling margins.
+//
+// Default is 1.
+//
+// CSI Pn @
+//
+// See: https://vt100.net/docs/vt510-rm/ICH.html
+func InsertCharacter(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "@"
+}
+
+// ICH is an alias for [InsertCharacter].
+func ICH(n int) string {
+ return InsertCharacter(n)
+}
+
+// DeleteCharacter (DCH) deletes n characters at the current cursor position.
+// As the characters are deleted, the remaining characters move to the left and
+// the cursor remains at the same position.
+//
+// Default is 1.
+//
+// CSI Pn P
+//
+// See: https://vt100.net/docs/vt510-rm/DCH.html
+func DeleteCharacter(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "P"
+}
+
+// DCH is an alias for [DeleteCharacter].
+func DCH(n int) string {
+ return DeleteCharacter(n)
+}
+
+// SetTabEvery8Columns (DECST8C) sets the tab stops at every 8 columns.
+//
+// CSI ? 5 W
+//
+// See: https://vt100.net/docs/vt510-rm/DECST8C.html
+const (
+ SetTabEvery8Columns = "\x1b[?5W"
+ DECST8C = SetTabEvery8Columns
+)
+
+// HorizontalTabSet (HTS) sets a horizontal tab stop at the current cursor
+// column.
+//
+// This is equivalent to [HTS].
+//
+// ESC H
+//
+// See: https://vt100.net/docs/vt510-rm/HTS.html
+const HorizontalTabSet = "\x1bH"
+
+// TabClear (TBC) clears tab stops.
+//
+// Default is 0.
+//
+// Possible values:
+// 0: Clear tab stop at the current column. (default)
+// 3: Clear all tab stops.
+//
+// CSI Pn g
+//
+// See: https://vt100.net/docs/vt510-rm/TBC.html
+func TabClear(n int) string {
+ var s string
+ if n > 0 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "g"
+}
+
+// TBC is an alias for [TabClear].
+func TBC(n int) string {
+ return TabClear(n)
+}
+
+// RequestPresentationStateReport (DECRQPSR) requests the terminal to send a
+// report of the presentation state. This includes the cursor information [DECCIR],
+// and tab stop [DECTABSR] reports.
+//
+// Default is 0.
+//
+// Possible values:
+// 0: Error, request ignored.
+// 1: Cursor information report [DECCIR].
+// 2: Tab stop report [DECTABSR].
+//
+// CSI Ps $ w
+//
+// See: https://vt100.net/docs/vt510-rm/DECRQPSR.html
+func RequestPresentationStateReport(n int) string {
+ var s string
+ if n > 0 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "$w"
+}
+
+// DECRQPSR is an alias for [RequestPresentationStateReport].
+func DECRQPSR(n int) string {
+ return RequestPresentationStateReport(n)
+}
+
+// TabStopReport (DECTABSR) is the response to a tab stop report request.
+// It reports the tab stops set in the terminal.
+//
+// The response is a list of tab stops separated by a slash (/) character.
+//
+// DCS 2 $ u D ... D ST
+//
+// Where D is a decimal number representing a tab stop.
+//
+// See: https://vt100.net/docs/vt510-rm/DECTABSR.html
+func TabStopReport(stops ...int) string {
+ var s []string
+ for _, v := range stops {
+ s = append(s, strconv.Itoa(v))
+ }
+ return "\x1bP2$u" + strings.Join(s, "/") + "\x1b\\"
+}
+
+// DECTABSR is an alias for [TabStopReport].
+func DECTABSR(stops ...int) string {
+ return TabStopReport(stops...)
+}
+
+// CursorInformationReport (DECCIR) is the response to a cursor information
+// report request. It reports the cursor position, visual attributes, and
+// character protection attributes. It also reports the status of origin mode
+// [DECOM] and the current active character set.
+//
+// The response is a list of values separated by a semicolon (;) character.
+//
+// DCS 1 $ u D ... D ST
+//
+// Where D is a decimal number representing a value.
+//
+// See: https://vt100.net/docs/vt510-rm/DECCIR.html
+func CursorInformationReport(values ...int) string {
+ var s []string
+ for _, v := range values {
+ s = append(s, strconv.Itoa(v))
+ }
+ return "\x1bP1$u" + strings.Join(s, ";") + "\x1b\\"
+}
+
+// DECCIR is an alias for [CursorInformationReport].
+func DECCIR(values ...int) string {
+ return CursorInformationReport(values...)
+}
+
+// RepeatPreviousCharacter (REP) repeats the previous character n times.
+// This is identical to typing the same character n times.
+//
+// Default is 1.
+//
+// CSI Pn b
+//
+// See: ECMA-48 § 8.3.103
+func RepeatPreviousCharacter(n int) string {
+ var s string
+ if n > 1 {
+ s = strconv.Itoa(n)
+ }
+ return "\x1b[" + s + "b"
+}
+
+// REP is an alias for [RepeatPreviousCharacter].
+func REP(n int) string {
+ return RepeatPreviousCharacter(n)
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/sgr.go b/vendor/github.com/charmbracelet/x/ansi/sgr.go
new file mode 100644
index 0000000..1a18c98
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/sgr.go
@@ -0,0 +1,95 @@
+package ansi
+
+import "strconv"
+
+// Select Graphic Rendition (SGR) is a command that sets display attributes.
+//
+// Default is 0.
+//
+// CSI Ps ; Ps ... m
+//
+// See: https://vt100.net/docs/vt510-rm/SGR.html
+func SelectGraphicRendition(ps ...Attr) string {
+ if len(ps) == 0 {
+ return ResetStyle
+ }
+
+ var s Style
+ for _, p := range ps {
+ attr, ok := attrStrings[p]
+ if ok {
+ s = append(s, attr)
+ } else {
+ if p < 0 {
+ p = 0
+ }
+ s = append(s, strconv.Itoa(p))
+ }
+ }
+
+ return s.String()
+}
+
+// SGR is an alias for [SelectGraphicRendition].
+func SGR(ps ...Attr) string {
+ return SelectGraphicRendition(ps...)
+}
+
+var attrStrings = map[int]string{
+ ResetAttr: "0",
+ BoldAttr: "1",
+ FaintAttr: "2",
+ ItalicAttr: "3",
+ UnderlineAttr: "4",
+ SlowBlinkAttr: "5",
+ RapidBlinkAttr: "6",
+ ReverseAttr: "7",
+ ConcealAttr: "8",
+ StrikethroughAttr: "9",
+ NoBoldAttr: "21",
+ NormalIntensityAttr: "22",
+ NoItalicAttr: "23",
+ NoUnderlineAttr: "24",
+ NoBlinkAttr: "25",
+ NoReverseAttr: "27",
+ NoConcealAttr: "28",
+ NoStrikethroughAttr: "29",
+ BlackForegroundColorAttr: "30",
+ RedForegroundColorAttr: "31",
+ GreenForegroundColorAttr: "32",
+ YellowForegroundColorAttr: "33",
+ BlueForegroundColorAttr: "34",
+ MagentaForegroundColorAttr: "35",
+ CyanForegroundColorAttr: "36",
+ WhiteForegroundColorAttr: "37",
+ ExtendedForegroundColorAttr: "38",
+ DefaultForegroundColorAttr: "39",
+ BlackBackgroundColorAttr: "40",
+ RedBackgroundColorAttr: "41",
+ GreenBackgroundColorAttr: "42",
+ YellowBackgroundColorAttr: "43",
+ BlueBackgroundColorAttr: "44",
+ MagentaBackgroundColorAttr: "45",
+ CyanBackgroundColorAttr: "46",
+ WhiteBackgroundColorAttr: "47",
+ ExtendedBackgroundColorAttr: "48",
+ DefaultBackgroundColorAttr: "49",
+ ExtendedUnderlineColorAttr: "58",
+ DefaultUnderlineColorAttr: "59",
+ BrightBlackForegroundColorAttr: "90",
+ BrightRedForegroundColorAttr: "91",
+ BrightGreenForegroundColorAttr: "92",
+ BrightYellowForegroundColorAttr: "93",
+ BrightBlueForegroundColorAttr: "94",
+ BrightMagentaForegroundColorAttr: "95",
+ BrightCyanForegroundColorAttr: "96",
+ BrightWhiteForegroundColorAttr: "97",
+ BrightBlackBackgroundColorAttr: "100",
+ BrightRedBackgroundColorAttr: "101",
+ BrightGreenBackgroundColorAttr: "102",
+ BrightYellowBackgroundColorAttr: "103",
+ BrightBlueBackgroundColorAttr: "104",
+ BrightMagentaBackgroundColorAttr: "105",
+ BrightCyanBackgroundColorAttr: "106",
+ BrightWhiteBackgroundColorAttr: "107",
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/status.go b/vendor/github.com/charmbracelet/x/ansi/status.go
new file mode 100644
index 0000000..4337e18
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/status.go
@@ -0,0 +1,144 @@
+package ansi
+
+import (
+ "strconv"
+ "strings"
+)
+
+// StatusReport represents a terminal status report.
+type StatusReport interface {
+ // StatusReport returns the status report identifier.
+ StatusReport() int
+}
+
+// ANSIReport represents an ANSI terminal status report.
+type ANSIStatusReport int //nolint:revive
+
+// Report returns the status report identifier.
+func (s ANSIStatusReport) StatusReport() int {
+ return int(s)
+}
+
+// DECStatusReport represents a DEC terminal status report.
+type DECStatusReport int
+
+// Status returns the status report identifier.
+func (s DECStatusReport) StatusReport() int {
+ return int(s)
+}
+
+// DeviceStatusReport (DSR) is a control sequence that reports the terminal's
+// status.
+// The terminal responds with a DSR sequence.
+//
+// CSI Ps n
+// CSI ? Ps n
+//
+// If one of the statuses is a [DECStatus], the sequence will use the DEC
+// format.
+//
+// See also https://vt100.net/docs/vt510-rm/DSR.html
+func DeviceStatusReport(statues ...StatusReport) string {
+ var dec bool
+ list := make([]string, len(statues))
+ seq := "\x1b["
+ for i, status := range statues {
+ list[i] = strconv.Itoa(status.StatusReport())
+ switch status.(type) {
+ case DECStatusReport:
+ dec = true
+ }
+ }
+ if dec {
+ seq += "?"
+ }
+ return seq + strings.Join(list, ";") + "n"
+}
+
+// DSR is an alias for [DeviceStatusReport].
+func DSR(status StatusReport) string {
+ return DeviceStatusReport(status)
+}
+
+// RequestCursorPositionReport is an escape sequence that requests the current
+// cursor position.
+//
+// CSI 6 n
+//
+// The terminal will report the cursor position as a CSI sequence in the
+// following format:
+//
+// CSI Pl ; Pc R
+//
+// Where Pl is the line number and Pc is the column number.
+// See: https://vt100.net/docs/vt510-rm/CPR.html
+const RequestCursorPositionReport = "\x1b[6n"
+
+// RequestExtendedCursorPositionReport (DECXCPR) is a sequence for requesting
+// the cursor position report including the current page number.
+//
+// CSI ? 6 n
+//
+// The terminal will report the cursor position as a CSI sequence in the
+// following format:
+//
+// CSI ? Pl ; Pc ; Pp R
+//
+// Where Pl is the line number, Pc is the column number, and Pp is the page
+// number.
+// See: https://vt100.net/docs/vt510-rm/DECXCPR.html
+const RequestExtendedCursorPositionReport = "\x1b[?6n"
+
+// CursorPositionReport (CPR) is a control sequence that reports the cursor's
+// position.
+//
+// CSI Pl ; Pc R
+//
+// Where Pl is the line number and Pc is the column number.
+//
+// See also https://vt100.net/docs/vt510-rm/CPR.html
+func CursorPositionReport(line, column int) string {
+ if line < 1 {
+ line = 1
+ }
+ if column < 1 {
+ column = 1
+ }
+ return "\x1b[" + strconv.Itoa(line) + ";" + strconv.Itoa(column) + "R"
+}
+
+// CPR is an alias for [CursorPositionReport].
+func CPR(line, column int) string {
+ return CursorPositionReport(line, column)
+}
+
+// ExtendedCursorPositionReport (DECXCPR) is a control sequence that reports the
+// cursor's position along with the page number (optional).
+//
+// CSI ? Pl ; Pc R
+// CSI ? Pl ; Pc ; Pv R
+//
+// Where Pl is the line number, Pc is the column number, and Pv is the page
+// number.
+//
+// If the page number is zero or negative, the returned sequence won't include
+// the page number.
+//
+// See also https://vt100.net/docs/vt510-rm/DECXCPR.html
+func ExtendedCursorPositionReport(line, column, page int) string {
+ if line < 1 {
+ line = 1
+ }
+ if column < 1 {
+ column = 1
+ }
+ if page < 1 {
+ return "\x1b[?" + strconv.Itoa(line) + ";" + strconv.Itoa(column) + "R"
+ }
+ return "\x1b[?" + strconv.Itoa(line) + ";" + strconv.Itoa(column) + ";" + strconv.Itoa(page) + "R"
+}
+
+// DECXCPR is an alias for [ExtendedCursorPositionReport].
+func DECXCPR(line, column, page int) string {
+ return ExtendedCursorPositionReport(line, column, page)
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/style.go b/vendor/github.com/charmbracelet/x/ansi/style.go
new file mode 100644
index 0000000..46ddcaa
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/style.go
@@ -0,0 +1,660 @@
+package ansi
+
+import (
+ "image/color"
+ "strconv"
+ "strings"
+)
+
+// ResetStyle is a SGR (Select Graphic Rendition) style sequence that resets
+// all attributes.
+// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
+const ResetStyle = "\x1b[m"
+
+// Attr is a SGR (Select Graphic Rendition) style attribute.
+type Attr = int
+
+// Style represents an ANSI SGR (Select Graphic Rendition) style.
+type Style []string
+
+// String returns the ANSI SGR (Select Graphic Rendition) style sequence for
+// the given style.
+func (s Style) String() string {
+ if len(s) == 0 {
+ return ResetStyle
+ }
+ return "\x1b[" + strings.Join(s, ";") + "m"
+}
+
+// Styled returns a styled string with the given style applied.
+func (s Style) Styled(str string) string {
+ if len(s) == 0 {
+ return str
+ }
+ return s.String() + str + ResetStyle
+}
+
+// Reset appends the reset style attribute to the style.
+func (s Style) Reset() Style {
+ return append(s, resetAttr)
+}
+
+// Bold appends the bold style attribute to the style.
+func (s Style) Bold() Style {
+ return append(s, boldAttr)
+}
+
+// Faint appends the faint style attribute to the style.
+func (s Style) Faint() Style {
+ return append(s, faintAttr)
+}
+
+// Italic appends the italic style attribute to the style.
+func (s Style) Italic() Style {
+ return append(s, italicAttr)
+}
+
+// Underline appends the underline style attribute to the style.
+func (s Style) Underline() Style {
+ return append(s, underlineAttr)
+}
+
+// UnderlineStyle appends the underline style attribute to the style.
+func (s Style) UnderlineStyle(u UnderlineStyle) Style {
+ switch u {
+ case NoUnderlineStyle:
+ return s.NoUnderline()
+ case SingleUnderlineStyle:
+ return s.Underline()
+ case DoubleUnderlineStyle:
+ return append(s, doubleUnderlineStyle)
+ case CurlyUnderlineStyle:
+ return append(s, curlyUnderlineStyle)
+ case DottedUnderlineStyle:
+ return append(s, dottedUnderlineStyle)
+ case DashedUnderlineStyle:
+ return append(s, dashedUnderlineStyle)
+ }
+ return s
+}
+
+// DoubleUnderline appends the double underline style attribute to the style.
+// This is a convenience method for UnderlineStyle(DoubleUnderlineStyle).
+func (s Style) DoubleUnderline() Style {
+ return s.UnderlineStyle(DoubleUnderlineStyle)
+}
+
+// CurlyUnderline appends the curly underline style attribute to the style.
+// This is a convenience method for UnderlineStyle(CurlyUnderlineStyle).
+func (s Style) CurlyUnderline() Style {
+ return s.UnderlineStyle(CurlyUnderlineStyle)
+}
+
+// DottedUnderline appends the dotted underline style attribute to the style.
+// This is a convenience method for UnderlineStyle(DottedUnderlineStyle).
+func (s Style) DottedUnderline() Style {
+ return s.UnderlineStyle(DottedUnderlineStyle)
+}
+
+// DashedUnderline appends the dashed underline style attribute to the style.
+// This is a convenience method for UnderlineStyle(DashedUnderlineStyle).
+func (s Style) DashedUnderline() Style {
+ return s.UnderlineStyle(DashedUnderlineStyle)
+}
+
+// SlowBlink appends the slow blink style attribute to the style.
+func (s Style) SlowBlink() Style {
+ return append(s, slowBlinkAttr)
+}
+
+// RapidBlink appends the rapid blink style attribute to the style.
+func (s Style) RapidBlink() Style {
+ return append(s, rapidBlinkAttr)
+}
+
+// Reverse appends the reverse style attribute to the style.
+func (s Style) Reverse() Style {
+ return append(s, reverseAttr)
+}
+
+// Conceal appends the conceal style attribute to the style.
+func (s Style) Conceal() Style {
+ return append(s, concealAttr)
+}
+
+// Strikethrough appends the strikethrough style attribute to the style.
+func (s Style) Strikethrough() Style {
+ return append(s, strikethroughAttr)
+}
+
+// NoBold appends the no bold style attribute to the style.
+func (s Style) NoBold() Style {
+ return append(s, noBoldAttr)
+}
+
+// NormalIntensity appends the normal intensity style attribute to the style.
+func (s Style) NormalIntensity() Style {
+ return append(s, normalIntensityAttr)
+}
+
+// NoItalic appends the no italic style attribute to the style.
+func (s Style) NoItalic() Style {
+ return append(s, noItalicAttr)
+}
+
+// NoUnderline appends the no underline style attribute to the style.
+func (s Style) NoUnderline() Style {
+ return append(s, noUnderlineAttr)
+}
+
+// NoBlink appends the no blink style attribute to the style.
+func (s Style) NoBlink() Style {
+ return append(s, noBlinkAttr)
+}
+
+// NoReverse appends the no reverse style attribute to the style.
+func (s Style) NoReverse() Style {
+ return append(s, noReverseAttr)
+}
+
+// NoConceal appends the no conceal style attribute to the style.
+func (s Style) NoConceal() Style {
+ return append(s, noConcealAttr)
+}
+
+// NoStrikethrough appends the no strikethrough style attribute to the style.
+func (s Style) NoStrikethrough() Style {
+ return append(s, noStrikethroughAttr)
+}
+
+// DefaultForegroundColor appends the default foreground color style attribute to the style.
+func (s Style) DefaultForegroundColor() Style {
+ return append(s, defaultForegroundColorAttr)
+}
+
+// DefaultBackgroundColor appends the default background color style attribute to the style.
+func (s Style) DefaultBackgroundColor() Style {
+ return append(s, defaultBackgroundColorAttr)
+}
+
+// DefaultUnderlineColor appends the default underline color style attribute to the style.
+func (s Style) DefaultUnderlineColor() Style {
+ return append(s, defaultUnderlineColorAttr)
+}
+
+// ForegroundColor appends the foreground color style attribute to the style.
+func (s Style) ForegroundColor(c Color) Style {
+ return append(s, foregroundColorString(c))
+}
+
+// BackgroundColor appends the background color style attribute to the style.
+func (s Style) BackgroundColor(c Color) Style {
+ return append(s, backgroundColorString(c))
+}
+
+// UnderlineColor appends the underline color style attribute to the style.
+func (s Style) UnderlineColor(c Color) Style {
+ return append(s, underlineColorString(c))
+}
+
+// UnderlineStyle represents an ANSI SGR (Select Graphic Rendition) underline
+// style.
+type UnderlineStyle = byte
+
+const (
+ doubleUnderlineStyle = "4:2"
+ curlyUnderlineStyle = "4:3"
+ dottedUnderlineStyle = "4:4"
+ dashedUnderlineStyle = "4:5"
+)
+
+const (
+ // NoUnderlineStyle is the default underline style.
+ NoUnderlineStyle UnderlineStyle = iota
+ // SingleUnderlineStyle is a single underline style.
+ SingleUnderlineStyle
+ // DoubleUnderlineStyle is a double underline style.
+ DoubleUnderlineStyle
+ // CurlyUnderlineStyle is a curly underline style.
+ CurlyUnderlineStyle
+ // DottedUnderlineStyle is a dotted underline style.
+ DottedUnderlineStyle
+ // DashedUnderlineStyle is a dashed underline style.
+ DashedUnderlineStyle
+)
+
+// SGR (Select Graphic Rendition) style attributes.
+// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
+const (
+ ResetAttr Attr = 0
+ BoldAttr Attr = 1
+ FaintAttr Attr = 2
+ ItalicAttr Attr = 3
+ UnderlineAttr Attr = 4
+ SlowBlinkAttr Attr = 5
+ RapidBlinkAttr Attr = 6
+ ReverseAttr Attr = 7
+ ConcealAttr Attr = 8
+ StrikethroughAttr Attr = 9
+ NoBoldAttr Attr = 21 // Some terminals treat this as double underline.
+ NormalIntensityAttr Attr = 22
+ NoItalicAttr Attr = 23
+ NoUnderlineAttr Attr = 24
+ NoBlinkAttr Attr = 25
+ NoReverseAttr Attr = 27
+ NoConcealAttr Attr = 28
+ NoStrikethroughAttr Attr = 29
+ BlackForegroundColorAttr Attr = 30
+ RedForegroundColorAttr Attr = 31
+ GreenForegroundColorAttr Attr = 32
+ YellowForegroundColorAttr Attr = 33
+ BlueForegroundColorAttr Attr = 34
+ MagentaForegroundColorAttr Attr = 35
+ CyanForegroundColorAttr Attr = 36
+ WhiteForegroundColorAttr Attr = 37
+ ExtendedForegroundColorAttr Attr = 38
+ DefaultForegroundColorAttr Attr = 39
+ BlackBackgroundColorAttr Attr = 40
+ RedBackgroundColorAttr Attr = 41
+ GreenBackgroundColorAttr Attr = 42
+ YellowBackgroundColorAttr Attr = 43
+ BlueBackgroundColorAttr Attr = 44
+ MagentaBackgroundColorAttr Attr = 45
+ CyanBackgroundColorAttr Attr = 46
+ WhiteBackgroundColorAttr Attr = 47
+ ExtendedBackgroundColorAttr Attr = 48
+ DefaultBackgroundColorAttr Attr = 49
+ ExtendedUnderlineColorAttr Attr = 58
+ DefaultUnderlineColorAttr Attr = 59
+ BrightBlackForegroundColorAttr Attr = 90
+ BrightRedForegroundColorAttr Attr = 91
+ BrightGreenForegroundColorAttr Attr = 92
+ BrightYellowForegroundColorAttr Attr = 93
+ BrightBlueForegroundColorAttr Attr = 94
+ BrightMagentaForegroundColorAttr Attr = 95
+ BrightCyanForegroundColorAttr Attr = 96
+ BrightWhiteForegroundColorAttr Attr = 97
+ BrightBlackBackgroundColorAttr Attr = 100
+ BrightRedBackgroundColorAttr Attr = 101
+ BrightGreenBackgroundColorAttr Attr = 102
+ BrightYellowBackgroundColorAttr Attr = 103
+ BrightBlueBackgroundColorAttr Attr = 104
+ BrightMagentaBackgroundColorAttr Attr = 105
+ BrightCyanBackgroundColorAttr Attr = 106
+ BrightWhiteBackgroundColorAttr Attr = 107
+
+ RGBColorIntroducerAttr Attr = 2
+ ExtendedColorIntroducerAttr Attr = 5
+)
+
+const (
+ resetAttr = "0"
+ boldAttr = "1"
+ faintAttr = "2"
+ italicAttr = "3"
+ underlineAttr = "4"
+ slowBlinkAttr = "5"
+ rapidBlinkAttr = "6"
+ reverseAttr = "7"
+ concealAttr = "8"
+ strikethroughAttr = "9"
+ noBoldAttr = "21"
+ normalIntensityAttr = "22"
+ noItalicAttr = "23"
+ noUnderlineAttr = "24"
+ noBlinkAttr = "25"
+ noReverseAttr = "27"
+ noConcealAttr = "28"
+ noStrikethroughAttr = "29"
+ blackForegroundColorAttr = "30"
+ redForegroundColorAttr = "31"
+ greenForegroundColorAttr = "32"
+ yellowForegroundColorAttr = "33"
+ blueForegroundColorAttr = "34"
+ magentaForegroundColorAttr = "35"
+ cyanForegroundColorAttr = "36"
+ whiteForegroundColorAttr = "37"
+ extendedForegroundColorAttr = "38"
+ defaultForegroundColorAttr = "39"
+ blackBackgroundColorAttr = "40"
+ redBackgroundColorAttr = "41"
+ greenBackgroundColorAttr = "42"
+ yellowBackgroundColorAttr = "43"
+ blueBackgroundColorAttr = "44"
+ magentaBackgroundColorAttr = "45"
+ cyanBackgroundColorAttr = "46"
+ whiteBackgroundColorAttr = "47"
+ extendedBackgroundColorAttr = "48"
+ defaultBackgroundColorAttr = "49"
+ extendedUnderlineColorAttr = "58"
+ defaultUnderlineColorAttr = "59"
+ brightBlackForegroundColorAttr = "90"
+ brightRedForegroundColorAttr = "91"
+ brightGreenForegroundColorAttr = "92"
+ brightYellowForegroundColorAttr = "93"
+ brightBlueForegroundColorAttr = "94"
+ brightMagentaForegroundColorAttr = "95"
+ brightCyanForegroundColorAttr = "96"
+ brightWhiteForegroundColorAttr = "97"
+ brightBlackBackgroundColorAttr = "100"
+ brightRedBackgroundColorAttr = "101"
+ brightGreenBackgroundColorAttr = "102"
+ brightYellowBackgroundColorAttr = "103"
+ brightBlueBackgroundColorAttr = "104"
+ brightMagentaBackgroundColorAttr = "105"
+ brightCyanBackgroundColorAttr = "106"
+ brightWhiteBackgroundColorAttr = "107"
+)
+
+// foregroundColorString returns the style SGR attribute for the given
+// foreground color.
+// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
+func foregroundColorString(c Color) string {
+ switch c := c.(type) {
+ case BasicColor:
+ // 3-bit or 4-bit ANSI foreground
+ // "3<n>" or "9<n>" where n is the color number from 0 to 7
+ switch c {
+ case Black:
+ return blackForegroundColorAttr
+ case Red:
+ return redForegroundColorAttr
+ case Green:
+ return greenForegroundColorAttr
+ case Yellow:
+ return yellowForegroundColorAttr
+ case Blue:
+ return blueForegroundColorAttr
+ case Magenta:
+ return magentaForegroundColorAttr
+ case Cyan:
+ return cyanForegroundColorAttr
+ case White:
+ return whiteForegroundColorAttr
+ case BrightBlack:
+ return brightBlackForegroundColorAttr
+ case BrightRed:
+ return brightRedForegroundColorAttr
+ case BrightGreen:
+ return brightGreenForegroundColorAttr
+ case BrightYellow:
+ return brightYellowForegroundColorAttr
+ case BrightBlue:
+ return brightBlueForegroundColorAttr
+ case BrightMagenta:
+ return brightMagentaForegroundColorAttr
+ case BrightCyan:
+ return brightCyanForegroundColorAttr
+ case BrightWhite:
+ return brightWhiteForegroundColorAttr
+ }
+ case ExtendedColor:
+ // 256-color ANSI foreground
+ // "38;5;<n>"
+ return "38;5;" + strconv.FormatUint(uint64(c), 10)
+ case TrueColor, color.Color:
+ // 24-bit "true color" foreground
+ // "38;2;<r>;<g>;<b>"
+ r, g, b, _ := c.RGBA()
+ return "38;2;" +
+ strconv.FormatUint(uint64(shift(r)), 10) + ";" +
+ strconv.FormatUint(uint64(shift(g)), 10) + ";" +
+ strconv.FormatUint(uint64(shift(b)), 10)
+ }
+ return defaultForegroundColorAttr
+}
+
+// backgroundColorString returns the style SGR attribute for the given
+// background color.
+// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
+func backgroundColorString(c Color) string {
+ switch c := c.(type) {
+ case BasicColor:
+ // 3-bit or 4-bit ANSI foreground
+ // "4<n>" or "10<n>" where n is the color number from 0 to 7
+ switch c {
+ case Black:
+ return blackBackgroundColorAttr
+ case Red:
+ return redBackgroundColorAttr
+ case Green:
+ return greenBackgroundColorAttr
+ case Yellow:
+ return yellowBackgroundColorAttr
+ case Blue:
+ return blueBackgroundColorAttr
+ case Magenta:
+ return magentaBackgroundColorAttr
+ case Cyan:
+ return cyanBackgroundColorAttr
+ case White:
+ return whiteBackgroundColorAttr
+ case BrightBlack:
+ return brightBlackBackgroundColorAttr
+ case BrightRed:
+ return brightRedBackgroundColorAttr
+ case BrightGreen:
+ return brightGreenBackgroundColorAttr
+ case BrightYellow:
+ return brightYellowBackgroundColorAttr
+ case BrightBlue:
+ return brightBlueBackgroundColorAttr
+ case BrightMagenta:
+ return brightMagentaBackgroundColorAttr
+ case BrightCyan:
+ return brightCyanBackgroundColorAttr
+ case BrightWhite:
+ return brightWhiteBackgroundColorAttr
+ }
+ case ExtendedColor:
+ // 256-color ANSI foreground
+ // "48;5;<n>"
+ return "48;5;" + strconv.FormatUint(uint64(c), 10)
+ case TrueColor, color.Color:
+ // 24-bit "true color" foreground
+ // "38;2;<r>;<g>;<b>"
+ r, g, b, _ := c.RGBA()
+ return "48;2;" +
+ strconv.FormatUint(uint64(shift(r)), 10) + ";" +
+ strconv.FormatUint(uint64(shift(g)), 10) + ";" +
+ strconv.FormatUint(uint64(shift(b)), 10)
+ }
+ return defaultBackgroundColorAttr
+}
+
+// underlineColorString returns the style SGR attribute for the given underline
+// color.
+// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
+func underlineColorString(c Color) string {
+ switch c := c.(type) {
+ // NOTE: we can't use 3-bit and 4-bit ANSI color codes with underline
+ // color, use 256-color instead.
+ //
+ // 256-color ANSI underline color
+ // "58;5;<n>"
+ case BasicColor:
+ return "58;5;" + strconv.FormatUint(uint64(c), 10)
+ case ExtendedColor:
+ return "58;5;" + strconv.FormatUint(uint64(c), 10)
+ case TrueColor, color.Color:
+ // 24-bit "true color" foreground
+ // "38;2;<r>;<g>;<b>"
+ r, g, b, _ := c.RGBA()
+ return "58;2;" +
+ strconv.FormatUint(uint64(shift(r)), 10) + ";" +
+ strconv.FormatUint(uint64(shift(g)), 10) + ";" +
+ strconv.FormatUint(uint64(shift(b)), 10)
+ }
+ return defaultUnderlineColorAttr
+}
+
+// ReadStyleColor decodes a color from a slice of parameters. It returns the
+// number of parameters read and the color. This function is used to read SGR
+// color parameters following the ITU T.416 standard.
+//
+// It supports reading the following color types:
+// - 0: implementation defined
+// - 1: transparent
+// - 2: RGB direct color
+// - 3: CMY direct color
+// - 4: CMYK direct color
+// - 5: indexed color
+// - 6: RGBA direct color (WezTerm extension)
+//
+// The parameters can be separated by semicolons (;) or colons (:). Mixing
+// separators is not allowed.
+//
+// The specs supports defining a color space id, a color tolerance value, and a
+// tolerance color space id. However, these values have no effect on the
+// returned color and will be ignored.
+//
+// This implementation includes a few modifications to the specs:
+// 1. Support for legacy color values separated by semicolons (;) with respect to RGB, and indexed colors
+// 2. Support ignoring and omitting the color space id (second parameter) with respect to RGB colors
+// 3. Support ignoring and omitting the 6th parameter with respect to RGB and CMY colors
+// 4. Support reading RGBA colors
+func ReadStyleColor(params Params, co *color.Color) (n int) {
+ if len(params) < 2 { // Need at least SGR type and color type
+ return 0
+ }
+
+ // First parameter indicates one of 38, 48, or 58 (foreground, background, or underline)
+ s := params[0]
+ p := params[1]
+ colorType := p.Param(0)
+ n = 2
+
+ paramsfn := func() (p1, p2, p3, p4 int) {
+ // Where should we start reading the color?
+ switch {
+ case s.HasMore() && p.HasMore() && len(params) > 8 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && params[5].HasMore() && params[6].HasMore() && params[7].HasMore():
+ // We have color space id, a 6th parameter, a tolerance value, and a tolerance color space
+ n += 7
+ return params[3].Param(0), params[4].Param(0), params[5].Param(0), params[6].Param(0)
+ case s.HasMore() && p.HasMore() && len(params) > 7 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && params[5].HasMore() && params[6].HasMore():
+ // We have color space id, a 6th parameter, and a tolerance value
+ n += 6
+ return params[3].Param(0), params[4].Param(0), params[5].Param(0), params[6].Param(0)
+ case s.HasMore() && p.HasMore() && len(params) > 6 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && params[5].HasMore():
+ // We have color space id and a 6th parameter
+ // 48 : 4 : : 1 : 2 : 3 :4
+ n += 5
+ return params[3].Param(0), params[4].Param(0), params[5].Param(0), params[6].Param(0)
+ case s.HasMore() && p.HasMore() && len(params) > 5 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && !params[5].HasMore():
+ // We have color space
+ // 48 : 3 : : 1 : 2 : 3
+ n += 4
+ return params[3].Param(0), params[4].Param(0), params[5].Param(0), -1
+ case s.HasMore() && p.HasMore() && p.Param(0) == 2 && params[2].HasMore() && params[3].HasMore() && !params[4].HasMore():
+ // We have color values separated by colons (:)
+ // 48 : 2 : 1 : 2 : 3
+ fallthrough
+ case !s.HasMore() && !p.HasMore() && p.Param(0) == 2 && !params[2].HasMore() && !params[3].HasMore() && !params[4].HasMore():
+ // Support legacy color values separated by semicolons (;)
+ // 48 ; 2 ; 1 ; 2 ; 3
+ n += 3
+ return params[2].Param(0), params[3].Param(0), params[4].Param(0), -1
+ }
+ // Ambiguous SGR color
+ return -1, -1, -1, -1
+ }
+
+ switch colorType {
+ case 0: // implementation defined
+ return 2
+ case 1: // transparent
+ *co = color.Transparent
+ return 2
+ case 2: // RGB direct color
+ if len(params) < 5 {
+ return 0
+ }
+
+ r, g, b, _ := paramsfn()
+ if r == -1 || g == -1 || b == -1 {
+ return 0
+ }
+
+ *co = color.RGBA{
+ R: uint8(r), //nolint:gosec
+ G: uint8(g), //nolint:gosec
+ B: uint8(b), //nolint:gosec
+ A: 0xff,
+ }
+ return
+
+ case 3: // CMY direct color
+ if len(params) < 5 {
+ return 0
+ }
+
+ c, m, y, _ := paramsfn()
+ if c == -1 || m == -1 || y == -1 {
+ return 0
+ }
+
+ *co = color.CMYK{
+ C: uint8(c), //nolint:gosec
+ M: uint8(m), //nolint:gosec
+ Y: uint8(y), //nolint:gosec
+ K: 0,
+ }
+ return
+
+ case 4: // CMYK direct color
+ if len(params) < 6 {
+ return 0
+ }
+
+ c, m, y, k := paramsfn()
+ if c == -1 || m == -1 || y == -1 || k == -1 {
+ return 0
+ }
+
+ *co = color.CMYK{
+ C: uint8(c), //nolint:gosec
+ M: uint8(m), //nolint:gosec
+ Y: uint8(y), //nolint:gosec
+ K: uint8(k), //nolint:gosec
+ }
+ return
+
+ case 5: // indexed color
+ if len(params) < 3 {
+ return 0
+ }
+ switch {
+ case s.HasMore() && p.HasMore() && !params[2].HasMore():
+ // Colon separated indexed color
+ // 38 : 5 : 234
+ case !s.HasMore() && !p.HasMore() && !params[2].HasMore():
+ // Legacy semicolon indexed color
+ // 38 ; 5 ; 234
+ default:
+ return 0
+ }
+ *co = ExtendedColor(params[2].Param(0)) //nolint:gosec
+ return 3
+
+ case 6: // RGBA direct color
+ if len(params) < 6 {
+ return 0
+ }
+
+ r, g, b, a := paramsfn()
+ if r == -1 || g == -1 || b == -1 || a == -1 {
+ return 0
+ }
+
+ *co = color.RGBA{
+ R: uint8(r), //nolint:gosec
+ G: uint8(g), //nolint:gosec
+ B: uint8(b), //nolint:gosec
+ A: uint8(a), //nolint:gosec
+ }
+ return
+
+ default:
+ return 0
+ }
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/termcap.go b/vendor/github.com/charmbracelet/x/ansi/termcap.go
new file mode 100644
index 0000000..3c5c7da
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/termcap.go
@@ -0,0 +1,41 @@
+package ansi
+
+import (
+ "encoding/hex"
+ "strings"
+)
+
+// RequestTermcap (XTGETTCAP) requests Termcap/Terminfo strings.
+//
+// DCS + q <Pt> ST
+//
+// Where <Pt> is a list of Termcap/Terminfo capabilities, encoded in 2-digit
+// hexadecimals, separated by semicolons.
+//
+// See: https://man7.org/linux/man-pages/man5/terminfo.5.html
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
+func XTGETTCAP(caps ...string) string {
+ if len(caps) == 0 {
+ return ""
+ }
+
+ s := "\x1bP+q"
+ for i, c := range caps {
+ if i > 0 {
+ s += ";"
+ }
+ s += strings.ToUpper(hex.EncodeToString([]byte(c)))
+ }
+
+ return s + "\x1b\\"
+}
+
+// RequestTermcap is an alias for [XTGETTCAP].
+func RequestTermcap(caps ...string) string {
+ return XTGETTCAP(caps...)
+}
+
+// RequestTerminfo is an alias for [XTGETTCAP].
+func RequestTerminfo(caps ...string) string {
+ return XTGETTCAP(caps...)
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/title.go b/vendor/github.com/charmbracelet/x/ansi/title.go
new file mode 100644
index 0000000..8fd8bf9
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/title.go
@@ -0,0 +1,32 @@
+package ansi
+
+// SetIconNameWindowTitle returns a sequence for setting the icon name and
+// window title.
+//
+// OSC 0 ; title ST
+// OSC 0 ; title BEL
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands
+func SetIconNameWindowTitle(s string) string {
+ return "\x1b]0;" + s + "\x07"
+}
+
+// SetIconName returns a sequence for setting the icon name.
+//
+// OSC 1 ; title ST
+// OSC 1 ; title BEL
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands
+func SetIconName(s string) string {
+ return "\x1b]1;" + s + "\x07"
+}
+
+// SetWindowTitle returns a sequence for setting the window title.
+//
+// OSC 2 ; title ST
+// OSC 2 ; title BEL
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands
+func SetWindowTitle(s string) string {
+ return "\x1b]2;" + s + "\x07"
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/truncate.go b/vendor/github.com/charmbracelet/x/ansi/truncate.go
new file mode 100644
index 0000000..1fa3efe
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/truncate.go
@@ -0,0 +1,282 @@
+package ansi
+
+import (
+ "bytes"
+
+ "github.com/charmbracelet/x/ansi/parser"
+ "github.com/mattn/go-runewidth"
+ "github.com/rivo/uniseg"
+)
+
+// Cut the string, without adding any prefix or tail strings. This function is
+// aware of ANSI escape codes and will not break them, and accounts for
+// wide-characters (such as East-Asian characters and emojis). Note that the
+// [left] parameter is inclusive, while [right] isn't.
+// This treats the text as a sequence of graphemes.
+func Cut(s string, left, right int) string {
+ return cut(GraphemeWidth, s, left, right)
+}
+
+// CutWc the string, without adding any prefix or tail strings. This function is
+// aware of ANSI escape codes and will not break them, and accounts for
+// wide-characters (such as East-Asian characters and emojis). Note that the
+// [left] parameter is inclusive, while [right] isn't.
+// This treats the text as a sequence of wide characters and runes.
+func CutWc(s string, left, right int) string {
+ return cut(WcWidth, s, left, right)
+}
+
+func cut(m Method, s string, left, right int) string {
+ if right <= left {
+ return ""
+ }
+
+ truncate := Truncate
+ truncateLeft := TruncateLeft
+ if m == WcWidth {
+ truncate = TruncateWc
+ truncateLeft = TruncateWc
+ }
+
+ if left == 0 {
+ return truncate(s, right, "")
+ }
+ return truncateLeft(Truncate(s, right, ""), left, "")
+}
+
+// Truncate truncates a string to a given length, adding a tail to the end if
+// the string is longer than the given length. This function is aware of ANSI
+// escape codes and will not break them, and accounts for wide-characters (such
+// as East-Asian characters and emojis).
+// This treats the text as a sequence of graphemes.
+func Truncate(s string, length int, tail string) string {
+ return truncate(GraphemeWidth, s, length, tail)
+}
+
+// TruncateWc truncates a string to a given length, adding a tail to the end if
+// the string is longer than the given length. This function is aware of ANSI
+// escape codes and will not break them, and accounts for wide-characters (such
+// as East-Asian characters and emojis).
+// This treats the text as a sequence of wide characters and runes.
+func TruncateWc(s string, length int, tail string) string {
+ return truncate(WcWidth, s, length, tail)
+}
+
+func truncate(m Method, s string, length int, tail string) string {
+ if sw := StringWidth(s); sw <= length {
+ return s
+ }
+
+ tw := StringWidth(tail)
+ length -= tw
+ if length < 0 {
+ return ""
+ }
+
+ var cluster []byte
+ var buf bytes.Buffer
+ curWidth := 0
+ ignoring := false
+ pstate := parser.GroundState // initial state
+ b := []byte(s)
+ i := 0
+
+ // Here we iterate over the bytes of the string and collect printable
+ // characters and runes. We also keep track of the width of the string
+ // in cells.
+ //
+ // Once we reach the given length, we start ignoring characters and only
+ // collect ANSI escape codes until we reach the end of string.
+ for i < len(b) {
+ state, action := parser.Table.Transition(pstate, b[i])
+ if state == parser.Utf8State {
+ // This action happens when we transition to the Utf8State.
+ var width int
+ cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
+ if m == WcWidth {
+ width = runewidth.StringWidth(string(cluster))
+ }
+
+ // increment the index by the length of the cluster
+ i += len(cluster)
+
+ // Are we ignoring? Skip to the next byte
+ if ignoring {
+ continue
+ }
+
+ // Is this gonna be too wide?
+ // If so write the tail and stop collecting.
+ if curWidth+width > length && !ignoring {
+ ignoring = true
+ buf.WriteString(tail)
+ }
+
+ if curWidth+width > length {
+ continue
+ }
+
+ curWidth += width
+ buf.Write(cluster)
+
+ // Done collecting, now we're back in the ground state.
+ pstate = parser.GroundState
+ continue
+ }
+
+ switch action {
+ case parser.PrintAction:
+ // Is this gonna be too wide?
+ // If so write the tail and stop collecting.
+ if curWidth >= length && !ignoring {
+ ignoring = true
+ buf.WriteString(tail)
+ }
+
+ // Skip to the next byte if we're ignoring
+ if ignoring {
+ i++
+ continue
+ }
+
+ // collects printable ASCII
+ curWidth++
+ fallthrough
+ default:
+ buf.WriteByte(b[i])
+ i++
+ }
+
+ // Transition to the next state.
+ pstate = state
+
+ // Once we reach the given length, we start ignoring runes and write
+ // the tail to the buffer.
+ if curWidth > length && !ignoring {
+ ignoring = true
+ buf.WriteString(tail)
+ }
+ }
+
+ return buf.String()
+}
+
+// TruncateLeft truncates a string from the left side by removing n characters,
+// adding a prefix to the beginning if the string is longer than n.
+// This function is aware of ANSI escape codes and will not break them, and
+// accounts for wide-characters (such as East-Asian characters and emojis).
+// This treats the text as a sequence of graphemes.
+func TruncateLeft(s string, n int, prefix string) string {
+ return truncateLeft(GraphemeWidth, s, n, prefix)
+}
+
+// TruncateLeftWc truncates a string from the left side by removing n characters,
+// adding a prefix to the beginning if the string is longer than n.
+// This function is aware of ANSI escape codes and will not break them, and
+// accounts for wide-characters (such as East-Asian characters and emojis).
+// This treats the text as a sequence of wide characters and runes.
+func TruncateLeftWc(s string, n int, prefix string) string {
+ return truncateLeft(WcWidth, s, n, prefix)
+}
+
+func truncateLeft(m Method, s string, n int, prefix string) string {
+ if n <= 0 {
+ return s
+ }
+
+ var cluster []byte
+ var buf bytes.Buffer
+ curWidth := 0
+ ignoring := true
+ pstate := parser.GroundState
+ b := []byte(s)
+ i := 0
+
+ for i < len(b) {
+ if !ignoring {
+ buf.Write(b[i:])
+ break
+ }
+
+ state, action := parser.Table.Transition(pstate, b[i])
+ if state == parser.Utf8State {
+ var width int
+ cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
+ if m == WcWidth {
+ width = runewidth.StringWidth(string(cluster))
+ }
+
+ i += len(cluster)
+ curWidth += width
+
+ if curWidth > n && ignoring {
+ ignoring = false
+ buf.WriteString(prefix)
+ }
+
+ if ignoring {
+ continue
+ }
+
+ if curWidth > n {
+ buf.Write(cluster)
+ }
+
+ pstate = parser.GroundState
+ continue
+ }
+
+ switch action {
+ case parser.PrintAction:
+ curWidth++
+
+ if curWidth > n && ignoring {
+ ignoring = false
+ buf.WriteString(prefix)
+ }
+
+ if ignoring {
+ i++
+ continue
+ }
+
+ fallthrough
+ default:
+ buf.WriteByte(b[i])
+ i++
+ }
+
+ pstate = state
+ if curWidth > n && ignoring {
+ ignoring = false
+ buf.WriteString(prefix)
+ }
+ }
+
+ return buf.String()
+}
+
+// ByteToGraphemeRange takes start and stop byte positions and converts them to
+// grapheme-aware char positions.
+// You can use this with [Truncate], [TruncateLeft], and [Cut].
+func ByteToGraphemeRange(str string, byteStart, byteStop int) (charStart, charStop int) {
+ bytePos, charPos := 0, 0
+ gr := uniseg.NewGraphemes(str)
+ for byteStart > bytePos {
+ if !gr.Next() {
+ break
+ }
+ bytePos += len(gr.Str())
+ charPos += max(1, gr.Width())
+ }
+ charStart = charPos
+ for byteStop > bytePos {
+ if !gr.Next() {
+ break
+ }
+ bytePos += len(gr.Str())
+ charPos += max(1, gr.Width())
+ }
+ charStop = charPos
+ return
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/util.go b/vendor/github.com/charmbracelet/x/ansi/util.go
new file mode 100644
index 0000000..301ef15
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/util.go
@@ -0,0 +1,106 @@
+package ansi
+
+import (
+ "fmt"
+ "image/color"
+ "strconv"
+ "strings"
+
+ "github.com/lucasb-eyer/go-colorful"
+)
+
+// colorToHexString returns a hex string representation of a color.
+func colorToHexString(c color.Color) string {
+ if c == nil {
+ return ""
+ }
+ shift := func(v uint32) uint32 {
+ if v > 0xff {
+ return v >> 8
+ }
+ return v
+ }
+ r, g, b, _ := c.RGBA()
+ r, g, b = shift(r), shift(g), shift(b)
+ return fmt.Sprintf("#%02x%02x%02x", r, g, b)
+}
+
+// rgbToHex converts red, green, and blue values to a hexadecimal value.
+//
+// hex := rgbToHex(0, 0, 255) // 0x0000FF
+func rgbToHex(r, g, b uint32) uint32 {
+ return r<<16 + g<<8 + b
+}
+
+type shiftable interface {
+ ~uint | ~uint16 | ~uint32 | ~uint64
+}
+
+func shift[T shiftable](x T) T {
+ if x > 0xff {
+ x >>= 8
+ }
+ return x
+}
+
+// XParseColor is a helper function that parses a string into a color.Color. It
+// provides a similar interface to the XParseColor function in Xlib. It
+// supports the following formats:
+//
+// - #RGB
+// - #RRGGBB
+// - rgb:RRRR/GGGG/BBBB
+// - rgba:RRRR/GGGG/BBBB/AAAA
+//
+// If the string is not a valid color, nil is returned.
+//
+// See: https://linux.die.net/man/3/xparsecolor
+func XParseColor(s string) color.Color {
+ switch {
+ case strings.HasPrefix(s, "#"):
+ c, err := colorful.Hex(s)
+ if err != nil {
+ return nil
+ }
+
+ return c
+ case strings.HasPrefix(s, "rgb:"):
+ parts := strings.Split(s[4:], "/")
+ if len(parts) != 3 {
+ return nil
+ }
+
+ r, _ := strconv.ParseUint(parts[0], 16, 32)
+ g, _ := strconv.ParseUint(parts[1], 16, 32)
+ b, _ := strconv.ParseUint(parts[2], 16, 32)
+
+ return color.RGBA{uint8(shift(r)), uint8(shift(g)), uint8(shift(b)), 255} //nolint:gosec
+ case strings.HasPrefix(s, "rgba:"):
+ parts := strings.Split(s[5:], "/")
+ if len(parts) != 4 {
+ return nil
+ }
+
+ r, _ := strconv.ParseUint(parts[0], 16, 32)
+ g, _ := strconv.ParseUint(parts[1], 16, 32)
+ b, _ := strconv.ParseUint(parts[2], 16, 32)
+ a, _ := strconv.ParseUint(parts[3], 16, 32)
+
+ return color.RGBA{uint8(shift(r)), uint8(shift(g)), uint8(shift(b)), uint8(shift(a))} //nolint:gosec
+ }
+ return nil
+}
+
+type ordered interface {
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
+ ~float32 | ~float64 |
+ ~string
+}
+
+func max[T ordered](a, b T) T { //nolint:predeclared
+ if a > b {
+ return a
+ }
+ return b
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/width.go b/vendor/github.com/charmbracelet/x/ansi/width.go
new file mode 100644
index 0000000..d0487d3
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/width.go
@@ -0,0 +1,113 @@
+package ansi
+
+import (
+ "bytes"
+
+ "github.com/charmbracelet/x/ansi/parser"
+ "github.com/mattn/go-runewidth"
+ "github.com/rivo/uniseg"
+)
+
+// Strip removes ANSI escape codes from a string.
+func Strip(s string) string {
+ var (
+ buf bytes.Buffer // buffer for collecting printable characters
+ ri int // rune index
+ rw int // rune width
+ pstate = parser.GroundState // initial state
+ )
+
+ // This implements a subset of the Parser to only collect runes and
+ // printable characters.
+ for i := 0; i < len(s); i++ {
+ if pstate == parser.Utf8State {
+ // During this state, collect rw bytes to form a valid rune in the
+ // buffer. After getting all the rune bytes into the buffer,
+ // transition to GroundState and reset the counters.
+ buf.WriteByte(s[i])
+ ri++
+ if ri < rw {
+ continue
+ }
+ pstate = parser.GroundState
+ ri = 0
+ rw = 0
+ continue
+ }
+
+ state, action := parser.Table.Transition(pstate, s[i])
+ switch action {
+ case parser.CollectAction:
+ if state == parser.Utf8State {
+ // This action happens when we transition to the Utf8State.
+ rw = utf8ByteLen(s[i])
+ buf.WriteByte(s[i])
+ ri++
+ }
+ case parser.PrintAction, parser.ExecuteAction:
+ // collects printable ASCII and non-printable characters
+ buf.WriteByte(s[i])
+ }
+
+ // Transition to the next state.
+ // The Utf8State is managed separately above.
+ if pstate != parser.Utf8State {
+ pstate = state
+ }
+ }
+
+ return buf.String()
+}
+
+// StringWidth returns the width of a string in cells. This is the number of
+// cells that the string will occupy when printed in a terminal. ANSI escape
+// codes are ignored and wide characters (such as East Asians and emojis) are
+// accounted for.
+// This treats the text as a sequence of grapheme clusters.
+func StringWidth(s string) int {
+ return stringWidth(GraphemeWidth, s)
+}
+
+// StringWidthWc returns the width of a string in cells. This is the number of
+// cells that the string will occupy when printed in a terminal. ANSI escape
+// codes are ignored and wide characters (such as East Asians and emojis) are
+// accounted for.
+// This treats the text as a sequence of wide characters and runes.
+func StringWidthWc(s string) int {
+ return stringWidth(WcWidth, s)
+}
+
+func stringWidth(m Method, s string) int {
+ if s == "" {
+ return 0
+ }
+
+ var (
+ pstate = parser.GroundState // initial state
+ cluster string
+ width int
+ )
+
+ for i := 0; i < len(s); i++ {
+ state, action := parser.Table.Transition(pstate, s[i])
+ if state == parser.Utf8State {
+ var w int
+ cluster, _, w, _ = uniseg.FirstGraphemeClusterInString(s[i:], -1)
+ if m == WcWidth {
+ w = runewidth.StringWidth(cluster)
+ }
+ width += w
+ i += len(cluster) - 1
+ pstate = parser.GroundState
+ continue
+ }
+
+ if action == parser.PrintAction {
+ width++
+ }
+
+ pstate = state
+ }
+
+ return width
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/winop.go b/vendor/github.com/charmbracelet/x/ansi/winop.go
new file mode 100644
index 0000000..0238780
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/winop.go
@@ -0,0 +1,53 @@
+package ansi
+
+import (
+ "strconv"
+ "strings"
+)
+
+const (
+ // ResizeWindowWinOp is a window operation that resizes the terminal
+ // window.
+ ResizeWindowWinOp = 4
+
+ // RequestWindowSizeWinOp is a window operation that requests a report of
+ // the size of the terminal window in pixels. The response is in the form:
+ // CSI 4 ; height ; width t
+ RequestWindowSizeWinOp = 14
+
+ // RequestCellSizeWinOp is a window operation that requests a report of
+ // the size of the terminal cell size in pixels. The response is in the form:
+ // CSI 6 ; height ; width t
+ RequestCellSizeWinOp = 16
+)
+
+// WindowOp (XTWINOPS) is a sequence that manipulates the terminal window.
+//
+// CSI Ps ; Ps ; Ps t
+//
+// Ps is a semicolon-separated list of parameters.
+// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps;Ps;Ps-t.1EB0
+func WindowOp(p int, ps ...int) string {
+ if p <= 0 {
+ return ""
+ }
+
+ if len(ps) == 0 {
+ return "\x1b[" + strconv.Itoa(p) + "t"
+ }
+
+ params := make([]string, 0, len(ps)+1)
+ params = append(params, strconv.Itoa(p))
+ for _, p := range ps {
+ if p >= 0 {
+ params = append(params, strconv.Itoa(p))
+ }
+ }
+
+ return "\x1b[" + strings.Join(params, ";") + "t"
+}
+
+// XTWINOPS is an alias for [WindowOp].
+func XTWINOPS(p int, ps ...int) string {
+ return WindowOp(p, ps...)
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/wrap.go b/vendor/github.com/charmbracelet/x/ansi/wrap.go
new file mode 100644
index 0000000..6b99580
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/wrap.go
@@ -0,0 +1,467 @@
+package ansi
+
+import (
+ "bytes"
+ "unicode"
+ "unicode/utf8"
+
+ "github.com/charmbracelet/x/ansi/parser"
+ "github.com/mattn/go-runewidth"
+ "github.com/rivo/uniseg"
+)
+
+// nbsp is a non-breaking space
+const nbsp = 0xA0
+
+// Hardwrap wraps a string or a block of text to a given line length, breaking
+// word boundaries. This will preserve ANSI escape codes and will account for
+// wide-characters in the string.
+// When preserveSpace is true, spaces at the beginning of a line will be
+// preserved.
+// This treats the text as a sequence of graphemes.
+func Hardwrap(s string, limit int, preserveSpace bool) string {
+ return hardwrap(GraphemeWidth, s, limit, preserveSpace)
+}
+
+// HardwrapWc wraps a string or a block of text to a given line length, breaking
+// word boundaries. This will preserve ANSI escape codes and will account for
+// wide-characters in the string.
+// When preserveSpace is true, spaces at the beginning of a line will be
+// preserved.
+// This treats the text as a sequence of wide characters and runes.
+func HardwrapWc(s string, limit int, preserveSpace bool) string {
+ return hardwrap(WcWidth, s, limit, preserveSpace)
+}
+
+func hardwrap(m Method, s string, limit int, preserveSpace bool) string {
+ if limit < 1 {
+ return s
+ }
+
+ var (
+ cluster []byte
+ buf bytes.Buffer
+ curWidth int
+ forceNewline bool
+ pstate = parser.GroundState // initial state
+ b = []byte(s)
+ )
+
+ addNewline := func() {
+ buf.WriteByte('\n')
+ curWidth = 0
+ }
+
+ i := 0
+ for i < len(b) {
+ state, action := parser.Table.Transition(pstate, b[i])
+ if state == parser.Utf8State {
+ var width int
+ cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
+ if m == WcWidth {
+ width = runewidth.StringWidth(string(cluster))
+ }
+ i += len(cluster)
+
+ if curWidth+width > limit {
+ addNewline()
+ }
+ if !preserveSpace && curWidth == 0 && len(cluster) <= 4 {
+ // Skip spaces at the beginning of a line
+ if r, _ := utf8.DecodeRune(cluster); r != utf8.RuneError && unicode.IsSpace(r) {
+ pstate = parser.GroundState
+ continue
+ }
+ }
+
+ buf.Write(cluster)
+ curWidth += width
+ pstate = parser.GroundState
+ continue
+ }
+
+ switch action {
+ case parser.PrintAction, parser.ExecuteAction:
+ if b[i] == '\n' {
+ addNewline()
+ forceNewline = false
+ break
+ }
+
+ if curWidth+1 > limit {
+ addNewline()
+ forceNewline = true
+ }
+
+ // Skip spaces at the beginning of a line
+ if curWidth == 0 {
+ if !preserveSpace && forceNewline && unicode.IsSpace(rune(b[i])) {
+ break
+ }
+ forceNewline = false
+ }
+
+ buf.WriteByte(b[i])
+ if action == parser.PrintAction {
+ curWidth++
+ }
+ default:
+ buf.WriteByte(b[i])
+ }
+
+ // We manage the UTF8 state separately manually above.
+ if pstate != parser.Utf8State {
+ pstate = state
+ }
+ i++
+ }
+
+ return buf.String()
+}
+
+// Wordwrap wraps a string or a block of text to a given line length, not
+// breaking word boundaries. This will preserve ANSI escape codes and will
+// account for wide-characters in the string.
+// The breakpoints string is a list of characters that are considered
+// breakpoints for word wrapping. A hyphen (-) is always considered a
+// breakpoint.
+//
+// Note: breakpoints must be a string of 1-cell wide rune characters.
+//
+// This treats the text as a sequence of graphemes.
+func Wordwrap(s string, limit int, breakpoints string) string {
+ return wordwrap(GraphemeWidth, s, limit, breakpoints)
+}
+
+// WordwrapWc wraps a string or a block of text to a given line length, not
+// breaking word boundaries. This will preserve ANSI escape codes and will
+// account for wide-characters in the string.
+// The breakpoints string is a list of characters that are considered
+// breakpoints for word wrapping. A hyphen (-) is always considered a
+// breakpoint.
+//
+// Note: breakpoints must be a string of 1-cell wide rune characters.
+//
+// This treats the text as a sequence of wide characters and runes.
+func WordwrapWc(s string, limit int, breakpoints string) string {
+ return wordwrap(WcWidth, s, limit, breakpoints)
+}
+
+func wordwrap(m Method, s string, limit int, breakpoints string) string {
+ if limit < 1 {
+ return s
+ }
+
+ var (
+ cluster []byte
+ buf bytes.Buffer
+ word bytes.Buffer
+ space bytes.Buffer
+ curWidth int
+ wordLen int
+ pstate = parser.GroundState // initial state
+ b = []byte(s)
+ )
+
+ addSpace := func() {
+ curWidth += space.Len()
+ buf.Write(space.Bytes())
+ space.Reset()
+ }
+
+ addWord := func() {
+ if word.Len() == 0 {
+ return
+ }
+
+ addSpace()
+ curWidth += wordLen
+ buf.Write(word.Bytes())
+ word.Reset()
+ wordLen = 0
+ }
+
+ addNewline := func() {
+ buf.WriteByte('\n')
+ curWidth = 0
+ space.Reset()
+ }
+
+ i := 0
+ for i < len(b) {
+ state, action := parser.Table.Transition(pstate, b[i])
+ if state == parser.Utf8State {
+ var width int
+ cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
+ if m == WcWidth {
+ width = runewidth.StringWidth(string(cluster))
+ }
+ i += len(cluster)
+
+ r, _ := utf8.DecodeRune(cluster)
+ if r != utf8.RuneError && unicode.IsSpace(r) && r != nbsp {
+ addWord()
+ space.WriteRune(r)
+ } else if bytes.ContainsAny(cluster, breakpoints) {
+ addSpace()
+ addWord()
+ buf.Write(cluster)
+ curWidth++
+ } else {
+ word.Write(cluster)
+ wordLen += width
+ if curWidth+space.Len()+wordLen > limit &&
+ wordLen < limit {
+ addNewline()
+ }
+ }
+
+ pstate = parser.GroundState
+ continue
+ }
+
+ switch action {
+ case parser.PrintAction, parser.ExecuteAction:
+ r := rune(b[i])
+ switch {
+ case r == '\n':
+ if wordLen == 0 {
+ if curWidth+space.Len() > limit {
+ curWidth = 0
+ } else {
+ buf.Write(space.Bytes())
+ }
+ space.Reset()
+ }
+
+ addWord()
+ addNewline()
+ case unicode.IsSpace(r):
+ addWord()
+ space.WriteByte(b[i])
+ case r == '-':
+ fallthrough
+ case runeContainsAny(r, breakpoints):
+ addSpace()
+ addWord()
+ buf.WriteByte(b[i])
+ curWidth++
+ default:
+ word.WriteByte(b[i])
+ wordLen++
+ if curWidth+space.Len()+wordLen > limit &&
+ wordLen < limit {
+ addNewline()
+ }
+ }
+
+ default:
+ word.WriteByte(b[i])
+ }
+
+ // We manage the UTF8 state separately manually above.
+ if pstate != parser.Utf8State {
+ pstate = state
+ }
+ i++
+ }
+
+ addWord()
+
+ return buf.String()
+}
+
+// Wrap wraps a string or a block of text to a given line length, breaking word
+// boundaries if necessary. This will preserve ANSI escape codes and will
+// account for wide-characters in the string. The breakpoints string is a list
+// of characters that are considered breakpoints for word wrapping. A hyphen
+// (-) is always considered a breakpoint.
+//
+// Note: breakpoints must be a string of 1-cell wide rune characters.
+//
+// This treats the text as a sequence of graphemes.
+func Wrap(s string, limit int, breakpoints string) string {
+ return wrap(GraphemeWidth, s, limit, breakpoints)
+}
+
+// WrapWc wraps a string or a block of text to a given line length, breaking word
+// boundaries if necessary. This will preserve ANSI escape codes and will
+// account for wide-characters in the string. The breakpoints string is a list
+// of characters that are considered breakpoints for word wrapping. A hyphen
+// (-) is always considered a breakpoint.
+//
+// Note: breakpoints must be a string of 1-cell wide rune characters.
+//
+// This treats the text as a sequence of wide characters and runes.
+func WrapWc(s string, limit int, breakpoints string) string {
+ return wrap(WcWidth, s, limit, breakpoints)
+}
+
+func wrap(m Method, s string, limit int, breakpoints string) string {
+ if limit < 1 {
+ return s
+ }
+
+ var (
+ cluster []byte
+ buf bytes.Buffer
+ word bytes.Buffer
+ space bytes.Buffer
+ curWidth int // written width of the line
+ wordLen int // word buffer len without ANSI escape codes
+ pstate = parser.GroundState // initial state
+ b = []byte(s)
+ )
+
+ addSpace := func() {
+ curWidth += space.Len()
+ buf.Write(space.Bytes())
+ space.Reset()
+ }
+
+ addWord := func() {
+ if word.Len() == 0 {
+ return
+ }
+
+ addSpace()
+ curWidth += wordLen
+ buf.Write(word.Bytes())
+ word.Reset()
+ wordLen = 0
+ }
+
+ addNewline := func() {
+ buf.WriteByte('\n')
+ curWidth = 0
+ space.Reset()
+ }
+
+ i := 0
+ for i < len(b) {
+ state, action := parser.Table.Transition(pstate, b[i])
+ if state == parser.Utf8State {
+ var width int
+ cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
+ if m == WcWidth {
+ width = runewidth.StringWidth(string(cluster))
+ }
+ i += len(cluster)
+
+ r, _ := utf8.DecodeRune(cluster)
+ switch {
+ case r != utf8.RuneError && unicode.IsSpace(r) && r != nbsp: // nbsp is a non-breaking space
+ addWord()
+ space.WriteRune(r)
+ case bytes.ContainsAny(cluster, breakpoints):
+ addSpace()
+ if curWidth+wordLen+width > limit {
+ word.Write(cluster)
+ wordLen += width
+ } else {
+ addWord()
+ buf.Write(cluster)
+ curWidth += width
+ }
+ default:
+ if wordLen+width > limit {
+ // Hardwrap the word if it's too long
+ addWord()
+ }
+
+ word.Write(cluster)
+ wordLen += width
+
+ if curWidth+wordLen+space.Len() > limit {
+ addNewline()
+ }
+ }
+
+ pstate = parser.GroundState
+ continue
+ }
+
+ switch action {
+ case parser.PrintAction, parser.ExecuteAction:
+ switch r := rune(b[i]); {
+ case r == '\n':
+ if wordLen == 0 {
+ if curWidth+space.Len() > limit {
+ curWidth = 0
+ } else {
+ // preserve whitespaces
+ buf.Write(space.Bytes())
+ }
+ space.Reset()
+ }
+
+ addWord()
+ addNewline()
+ case unicode.IsSpace(r):
+ addWord()
+ space.WriteRune(r)
+ case r == '-':
+ fallthrough
+ case runeContainsAny(r, breakpoints):
+ addSpace()
+ if curWidth+wordLen >= limit {
+ // We can't fit the breakpoint in the current line, treat
+ // it as part of the word.
+ word.WriteRune(r)
+ wordLen++
+ } else {
+ addWord()
+ buf.WriteRune(r)
+ curWidth++
+ }
+ default:
+ if curWidth == limit {
+ addNewline()
+ }
+ word.WriteRune(r)
+ wordLen++
+
+ if wordLen == limit {
+ // Hardwrap the word if it's too long
+ addWord()
+ }
+
+ if curWidth+wordLen+space.Len() > limit {
+ addNewline()
+ }
+ }
+
+ default:
+ word.WriteByte(b[i])
+ }
+
+ // We manage the UTF8 state separately manually above.
+ if pstate != parser.Utf8State {
+ pstate = state
+ }
+ i++
+ }
+
+ if wordLen == 0 {
+ if curWidth+space.Len() > limit {
+ curWidth = 0
+ } else {
+ // preserve whitespaces
+ buf.Write(space.Bytes())
+ }
+ space.Reset()
+ }
+
+ addWord()
+
+ return buf.String()
+}
+
+func runeContainsAny(r rune, s string) bool {
+ for _, c := range s {
+ if c == r {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/charmbracelet/x/ansi/xterm.go b/vendor/github.com/charmbracelet/x/ansi/xterm.go
new file mode 100644
index 0000000..83fd4bd
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/ansi/xterm.go
@@ -0,0 +1,138 @@
+package ansi
+
+import "strconv"
+
+// KeyModifierOptions (XTMODKEYS) sets/resets xterm key modifier options.
+//
+// Default is 0.
+//
+// CSI > Pp m
+// CSI > Pp ; Pv m
+//
+// If Pv is omitted, the resource is reset to its initial value.
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+func KeyModifierOptions(p int, vs ...int) string {
+ var pp, pv string
+ if p > 0 {
+ pp = strconv.Itoa(p)
+ }
+
+ if len(vs) == 0 {
+ return "\x1b[>" + strconv.Itoa(p) + "m"
+ }
+
+ v := vs[0]
+ if v > 0 {
+ pv = strconv.Itoa(v)
+ return "\x1b[>" + pp + ";" + pv + "m"
+ }
+
+ return "\x1b[>" + pp + "m"
+}
+
+// XTMODKEYS is an alias for [KeyModifierOptions].
+func XTMODKEYS(p int, vs ...int) string {
+ return KeyModifierOptions(p, vs...)
+}
+
+// SetKeyModifierOptions sets xterm key modifier options.
+// This is an alias for [KeyModifierOptions].
+func SetKeyModifierOptions(pp int, pv int) string {
+ return KeyModifierOptions(pp, pv)
+}
+
+// ResetKeyModifierOptions resets xterm key modifier options.
+// This is an alias for [KeyModifierOptions].
+func ResetKeyModifierOptions(pp int) string {
+ return KeyModifierOptions(pp)
+}
+
+// QueryKeyModifierOptions (XTQMODKEYS) requests xterm key modifier options.
+//
+// Default is 0.
+//
+// CSI ? Pp m
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+func QueryKeyModifierOptions(pp int) string {
+ var p string
+ if pp > 0 {
+ p = strconv.Itoa(pp)
+ }
+ return "\x1b[?" + p + "m"
+}
+
+// XTQMODKEYS is an alias for [QueryKeyModifierOptions].
+func XTQMODKEYS(pp int) string {
+ return QueryKeyModifierOptions(pp)
+}
+
+// Modify Other Keys (modifyOtherKeys) is an xterm feature that allows the
+// terminal to modify the behavior of certain keys to send different escape
+// sequences when pressed.
+//
+// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
+const (
+ SetModifyOtherKeys1 = "\x1b[>4;1m"
+ SetModifyOtherKeys2 = "\x1b[>4;2m"
+ ResetModifyOtherKeys = "\x1b[>4m"
+ QueryModifyOtherKeys = "\x1b[?4m"
+)
+
+// ModifyOtherKeys returns a sequence that sets XTerm modifyOtherKeys mode.
+// The mode argument specifies the mode to set.
+//
+// 0: Disable modifyOtherKeys mode.
+// 1: Enable modifyOtherKeys mode 1.
+// 2: Enable modifyOtherKeys mode 2.
+//
+// CSI > 4 ; mode m
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
+//
+// Deprecated: use [SetModifyOtherKeys1] or [SetModifyOtherKeys2] instead.
+func ModifyOtherKeys(mode int) string {
+ return "\x1b[>4;" + strconv.Itoa(mode) + "m"
+}
+
+// DisableModifyOtherKeys disables the modifyOtherKeys mode.
+//
+// CSI > 4 ; 0 m
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
+//
+// Deprecated: use [ResetModifyOtherKeys] instead.
+const DisableModifyOtherKeys = "\x1b[>4;0m"
+
+// EnableModifyOtherKeys1 enables the modifyOtherKeys mode 1.
+//
+// CSI > 4 ; 1 m
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
+//
+// Deprecated: use [SetModifyOtherKeys1] instead.
+const EnableModifyOtherKeys1 = "\x1b[>4;1m"
+
+// EnableModifyOtherKeys2 enables the modifyOtherKeys mode 2.
+//
+// CSI > 4 ; 2 m
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
+//
+// Deprecated: use [SetModifyOtherKeys2] instead.
+const EnableModifyOtherKeys2 = "\x1b[>4;2m"
+
+// RequestModifyOtherKeys requests the modifyOtherKeys mode.
+//
+// CSI ? 4 m
+//
+// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_
+// See: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys
+//
+// Deprecated: use [QueryModifyOtherKeys] instead.
+const RequestModifyOtherKeys = "\x1b[?4m"
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/LICENSE b/vendor/github.com/charmbracelet/x/cellbuf/LICENSE
new file mode 100644
index 0000000..65a5654
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 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.
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/buffer.go b/vendor/github.com/charmbracelet/x/cellbuf/buffer.go
new file mode 100644
index 0000000..790d1f7
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/buffer.go
@@ -0,0 +1,473 @@
+package cellbuf
+
+import (
+ "strings"
+
+ "github.com/mattn/go-runewidth"
+ "github.com/rivo/uniseg"
+)
+
+// NewCell returns a new cell. This is a convenience function that initializes a
+// new cell with the given content. The cell's width is determined by the
+// content using [runewidth.RuneWidth].
+// This will only account for the first combined rune in the content. If the
+// content is empty, it will return an empty cell with a width of 0.
+func NewCell(r rune, comb ...rune) (c *Cell) {
+ c = new(Cell)
+ c.Rune = r
+ c.Width = runewidth.RuneWidth(r)
+ for _, r := range comb {
+ if runewidth.RuneWidth(r) > 0 {
+ break
+ }
+ c.Comb = append(c.Comb, r)
+ }
+ c.Comb = comb
+ c.Width = runewidth.StringWidth(string(append([]rune{r}, comb...)))
+ return
+}
+
+// NewCellString returns a new cell with the given string content. This is a
+// convenience function that initializes a new cell with the given content. The
+// cell's width is determined by the content using [runewidth.StringWidth].
+// This will only use the first combined rune in the string. If the string is
+// empty, it will return an empty cell with a width of 0.
+func NewCellString(s string) (c *Cell) {
+ c = new(Cell)
+ for i, r := range s {
+ if i == 0 {
+ c.Rune = r
+ // We only care about the first rune's width
+ c.Width = runewidth.RuneWidth(r)
+ } else {
+ if runewidth.RuneWidth(r) > 0 {
+ break
+ }
+ c.Comb = append(c.Comb, r)
+ }
+ }
+ return
+}
+
+// NewGraphemeCell returns a new cell. This is a convenience function that
+// initializes a new cell with the given content. The cell's width is determined
+// by the content using [uniseg.FirstGraphemeClusterInString].
+// This is used when the content is a grapheme cluster i.e. a sequence of runes
+// that form a single visual unit.
+// This will only return the first grapheme cluster in the string. If the
+// string is empty, it will return an empty cell with a width of 0.
+func NewGraphemeCell(s string) (c *Cell) {
+ g, _, w, _ := uniseg.FirstGraphemeClusterInString(s, -1)
+ return newGraphemeCell(g, w)
+}
+
+func newGraphemeCell(s string, w int) (c *Cell) {
+ c = new(Cell)
+ c.Width = w
+ for i, r := range s {
+ if i == 0 {
+ c.Rune = r
+ } else {
+ c.Comb = append(c.Comb, r)
+ }
+ }
+ return
+}
+
+// Line represents a line in the terminal.
+// A nil cell represents an blank cell, a cell with a space character and a
+// width of 1.
+// If a cell has no content and a width of 0, it is a placeholder for a wide
+// cell.
+type Line []*Cell
+
+// Width returns the width of the line.
+func (l Line) Width() int {
+ return len(l)
+}
+
+// Len returns the length of the line.
+func (l Line) Len() int {
+ return len(l)
+}
+
+// String returns the string representation of the line. Any trailing spaces
+// are removed.
+func (l Line) String() (s string) {
+ for _, c := range l {
+ if c == nil {
+ s += " "
+ } else if c.Empty() {
+ continue
+ } else {
+ s += c.String()
+ }
+ }
+ s = strings.TrimRight(s, " ")
+ return
+}
+
+// At returns the cell at the given x position.
+// If the cell does not exist, it returns nil.
+func (l Line) At(x int) *Cell {
+ if x < 0 || x >= len(l) {
+ return nil
+ }
+
+ c := l[x]
+ if c == nil {
+ newCell := BlankCell
+ return &newCell
+ }
+
+ return c
+}
+
+// Set sets the cell at the given x position. If a wide cell is given, it will
+// set the cell and the following cells to [EmptyCell]. It returns true if the
+// cell was set.
+func (l Line) Set(x int, c *Cell) bool {
+ return l.set(x, c, true)
+}
+
+func (l Line) set(x int, c *Cell, clone bool) bool {
+ width := l.Width()
+ if x < 0 || x >= width {
+ return false
+ }
+
+ // When a wide cell is partially overwritten, we need
+ // to fill the rest of the cell with space cells to
+ // avoid rendering issues.
+ prev := l.At(x)
+ if prev != nil && prev.Width > 1 {
+ // Writing to the first wide cell
+ for j := 0; j < prev.Width && x+j < l.Width(); j++ {
+ l[x+j] = prev.Clone().Blank()
+ }
+ } else if prev != nil && prev.Width == 0 {
+ // Writing to wide cell placeholders
+ for j := 1; j < maxCellWidth && x-j >= 0; j++ {
+ wide := l.At(x - j)
+ if wide != nil && wide.Width > 1 && j < wide.Width {
+ for k := 0; k < wide.Width; k++ {
+ l[x-j+k] = wide.Clone().Blank()
+ }
+ break
+ }
+ }
+ }
+
+ if clone && c != nil {
+ // Clone the cell if not nil.
+ c = c.Clone()
+ }
+
+ if c != nil && x+c.Width > width {
+ // If the cell is too wide, we write blanks with the same style.
+ for i := 0; i < c.Width && x+i < width; i++ {
+ l[x+i] = c.Clone().Blank()
+ }
+ } else {
+ l[x] = c
+
+ // Mark wide cells with an empty cell zero width
+ // We set the wide cell down below
+ if c != nil && c.Width > 1 {
+ for j := 1; j < c.Width && x+j < l.Width(); j++ {
+ var wide Cell
+ l[x+j] = &wide
+ }
+ }
+ }
+
+ return true
+}
+
+// Buffer is a 2D grid of cells representing a screen or terminal.
+type Buffer struct {
+ // Lines holds the lines of the buffer.
+ Lines []Line
+}
+
+// NewBuffer creates a new buffer with the given width and height.
+// This is a convenience function that initializes a new buffer and resizes it.
+func NewBuffer(width int, height int) *Buffer {
+ b := new(Buffer)
+ b.Resize(width, height)
+ return b
+}
+
+// String returns the string representation of the buffer.
+func (b *Buffer) String() (s string) {
+ for i, l := range b.Lines {
+ s += l.String()
+ if i < len(b.Lines)-1 {
+ s += "\r\n"
+ }
+ }
+ return
+}
+
+// Line returns a pointer to the line at the given y position.
+// If the line does not exist, it returns nil.
+func (b *Buffer) Line(y int) Line {
+ if y < 0 || y >= len(b.Lines) {
+ return nil
+ }
+ return b.Lines[y]
+}
+
+// Cell implements Screen.
+func (b *Buffer) Cell(x int, y int) *Cell {
+ if y < 0 || y >= len(b.Lines) {
+ return nil
+ }
+ return b.Lines[y].At(x)
+}
+
+// maxCellWidth is the maximum width a terminal cell can get.
+const maxCellWidth = 4
+
+// SetCell sets the cell at the given x, y position.
+func (b *Buffer) SetCell(x, y int, c *Cell) bool {
+ return b.setCell(x, y, c, true)
+}
+
+// setCell sets the cell at the given x, y position. This will always clone and
+// allocates a new cell if c is not nil.
+func (b *Buffer) setCell(x, y int, c *Cell, clone bool) bool {
+ if y < 0 || y >= len(b.Lines) {
+ return false
+ }
+ return b.Lines[y].set(x, c, clone)
+}
+
+// Height implements Screen.
+func (b *Buffer) Height() int {
+ return len(b.Lines)
+}
+
+// Width implements Screen.
+func (b *Buffer) Width() int {
+ if len(b.Lines) == 0 {
+ return 0
+ }
+ return b.Lines[0].Width()
+}
+
+// Bounds returns the bounds of the buffer.
+func (b *Buffer) Bounds() Rectangle {
+ return Rect(0, 0, b.Width(), b.Height())
+}
+
+// Resize resizes the buffer to the given width and height.
+func (b *Buffer) Resize(width int, height int) {
+ if width == 0 || height == 0 {
+ b.Lines = nil
+ return
+ }
+
+ if width > b.Width() {
+ line := make(Line, width-b.Width())
+ for i := range b.Lines {
+ b.Lines[i] = append(b.Lines[i], line...)
+ }
+ } else if width < b.Width() {
+ for i := range b.Lines {
+ b.Lines[i] = b.Lines[i][:width]
+ }
+ }
+
+ if height > len(b.Lines) {
+ for i := len(b.Lines); i < height; i++ {
+ b.Lines = append(b.Lines, make(Line, width))
+ }
+ } else if height < len(b.Lines) {
+ b.Lines = b.Lines[:height]
+ }
+}
+
+// FillRect fills the buffer with the given cell and rectangle.
+func (b *Buffer) FillRect(c *Cell, rect Rectangle) {
+ cellWidth := 1
+ if c != nil && c.Width > 1 {
+ cellWidth = c.Width
+ }
+ for y := rect.Min.Y; y < rect.Max.Y; y++ {
+ for x := rect.Min.X; x < rect.Max.X; x += cellWidth {
+ b.setCell(x, y, c, false) //nolint:errcheck
+ }
+ }
+}
+
+// Fill fills the buffer with the given cell and rectangle.
+func (b *Buffer) Fill(c *Cell) {
+ b.FillRect(c, b.Bounds())
+}
+
+// Clear clears the buffer with space cells and rectangle.
+func (b *Buffer) Clear() {
+ b.ClearRect(b.Bounds())
+}
+
+// ClearRect clears the buffer with space cells within the specified
+// rectangles. Only cells within the rectangle's bounds are affected.
+func (b *Buffer) ClearRect(rect Rectangle) {
+ b.FillRect(nil, rect)
+}
+
+// InsertLine inserts n lines at the given line position, with the given
+// optional cell, within the specified rectangles. If no rectangles are
+// specified, it inserts lines in the entire buffer. Only cells within the
+// rectangle's horizontal bounds are affected. Lines are pushed out of the
+// rectangle bounds and lost. This follows terminal [ansi.IL] behavior.
+// It returns the pushed out lines.
+func (b *Buffer) InsertLine(y, n int, c *Cell) {
+ b.InsertLineRect(y, n, c, b.Bounds())
+}
+
+// InsertLineRect inserts new lines at the given line position, with the
+// given optional cell, within the rectangle bounds. Only cells within the
+// rectangle's horizontal bounds are affected. Lines are pushed out of the
+// rectangle bounds and lost. This follows terminal [ansi.IL] behavior.
+func (b *Buffer) InsertLineRect(y, n int, c *Cell, rect Rectangle) {
+ if n <= 0 || y < rect.Min.Y || y >= rect.Max.Y || y >= b.Height() {
+ return
+ }
+
+ // Limit number of lines to insert to available space
+ if y+n > rect.Max.Y {
+ n = rect.Max.Y - y
+ }
+
+ // Move existing lines down within the bounds
+ for i := rect.Max.Y - 1; i >= y+n; i-- {
+ for x := rect.Min.X; x < rect.Max.X; x++ {
+ // We don't need to clone c here because we're just moving lines down.
+ b.setCell(x, i, b.Lines[i-n][x], false)
+ }
+ }
+
+ // Clear the newly inserted lines within bounds
+ for i := y; i < y+n; i++ {
+ for x := rect.Min.X; x < rect.Max.X; x++ {
+ b.setCell(x, i, c, true)
+ }
+ }
+}
+
+// DeleteLineRect deletes lines at the given line position, with the given
+// optional cell, within the rectangle bounds. Only cells within the
+// rectangle's bounds are affected. Lines are shifted up within the bounds and
+// new blank lines are created at the bottom. This follows terminal [ansi.DL]
+// behavior.
+func (b *Buffer) DeleteLineRect(y, n int, c *Cell, rect Rectangle) {
+ if n <= 0 || y < rect.Min.Y || y >= rect.Max.Y || y >= b.Height() {
+ return
+ }
+
+ // Limit deletion count to available space in scroll region
+ if n > rect.Max.Y-y {
+ n = rect.Max.Y - y
+ }
+
+ // Shift cells up within the bounds
+ for dst := y; dst < rect.Max.Y-n; dst++ {
+ src := dst + n
+ for x := rect.Min.X; x < rect.Max.X; x++ {
+ // We don't need to clone c here because we're just moving cells up.
+ // b.lines[dst][x] = b.lines[src][x]
+ b.setCell(x, dst, b.Lines[src][x], false)
+ }
+ }
+
+ // Fill the bottom n lines with blank cells
+ for i := rect.Max.Y - n; i < rect.Max.Y; i++ {
+ for x := rect.Min.X; x < rect.Max.X; x++ {
+ b.setCell(x, i, c, true)
+ }
+ }
+}
+
+// DeleteLine deletes n lines at the given line position, with the given
+// optional cell, within the specified rectangles. If no rectangles are
+// specified, it deletes lines in the entire buffer.
+func (b *Buffer) DeleteLine(y, n int, c *Cell) {
+ b.DeleteLineRect(y, n, c, b.Bounds())
+}
+
+// InsertCell inserts new cells at the given position, with the given optional
+// cell, within the specified rectangles. If no rectangles are specified, it
+// inserts cells in the entire buffer. This follows terminal [ansi.ICH]
+// behavior.
+func (b *Buffer) InsertCell(x, y, n int, c *Cell) {
+ b.InsertCellRect(x, y, n, c, b.Bounds())
+}
+
+// InsertCellRect inserts new cells at the given position, with the given
+// optional cell, within the rectangle bounds. Only cells within the
+// rectangle's bounds are affected, following terminal [ansi.ICH] behavior.
+func (b *Buffer) InsertCellRect(x, y, n int, c *Cell, rect Rectangle) {
+ if n <= 0 || y < rect.Min.Y || y >= rect.Max.Y || y >= b.Height() ||
+ x < rect.Min.X || x >= rect.Max.X || x >= b.Width() {
+ return
+ }
+
+ // Limit number of cells to insert to available space
+ if x+n > rect.Max.X {
+ n = rect.Max.X - x
+ }
+
+ // Move existing cells within rectangle bounds to the right
+ for i := rect.Max.X - 1; i >= x+n && i-n >= rect.Min.X; i-- {
+ // We don't need to clone c here because we're just moving cells to the
+ // right.
+ // b.lines[y][i] = b.lines[y][i-n]
+ b.setCell(i, y, b.Lines[y][i-n], false)
+ }
+
+ // Clear the newly inserted cells within rectangle bounds
+ for i := x; i < x+n && i < rect.Max.X; i++ {
+ b.setCell(i, y, c, true)
+ }
+}
+
+// DeleteCell deletes cells at the given position, with the given optional
+// cell, within the specified rectangles. If no rectangles are specified, it
+// deletes cells in the entire buffer. This follows terminal [ansi.DCH]
+// behavior.
+func (b *Buffer) DeleteCell(x, y, n int, c *Cell) {
+ b.DeleteCellRect(x, y, n, c, b.Bounds())
+}
+
+// DeleteCellRect deletes cells at the given position, with the given
+// optional cell, within the rectangle bounds. Only cells within the
+// rectangle's bounds are affected, following terminal [ansi.DCH] behavior.
+func (b *Buffer) DeleteCellRect(x, y, n int, c *Cell, rect Rectangle) {
+ if n <= 0 || y < rect.Min.Y || y >= rect.Max.Y || y >= b.Height() ||
+ x < rect.Min.X || x >= rect.Max.X || x >= b.Width() {
+ return
+ }
+
+ // Calculate how many positions we can actually delete
+ remainingCells := rect.Max.X - x
+ if n > remainingCells {
+ n = remainingCells
+ }
+
+ // Shift the remaining cells to the left
+ for i := x; i < rect.Max.X-n; i++ {
+ if i+n < rect.Max.X {
+ // We don't need to clone c here because we're just moving cells to
+ // the left.
+ // b.lines[y][i] = b.lines[y][i+n]
+ b.setCell(i, y, b.Lines[y][i+n], false)
+ }
+ }
+
+ // Fill the vacated positions with the given cell
+ for i := rect.Max.X - n; i < rect.Max.X; i++ {
+ b.setCell(i, y, c, true)
+ }
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/cell.go b/vendor/github.com/charmbracelet/x/cellbuf/cell.go
new file mode 100644
index 0000000..991c919
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/cell.go
@@ -0,0 +1,503 @@
+package cellbuf
+
+import (
+ "github.com/charmbracelet/x/ansi"
+)
+
+var (
+ // BlankCell is a cell with a single space, width of 1, and no style or link.
+ BlankCell = Cell{Rune: ' ', Width: 1}
+
+ // EmptyCell is just an empty cell used for comparisons and as a placeholder
+ // for wide cells.
+ EmptyCell = Cell{}
+)
+
+// Cell represents a single cell in the terminal screen.
+type Cell struct {
+ // The style of the cell. Nil style means no style. Zero value prints a
+ // reset sequence.
+ Style Style
+
+ // Link is the hyperlink of the cell.
+ Link Link
+
+ // Comb is the combining runes of the cell. This is nil if the cell is a
+ // single rune or if it's a zero width cell that is part of a wider cell.
+ Comb []rune
+
+ // Width is the mono-space width of the grapheme cluster.
+ Width int
+
+ // Rune is the main rune of the cell. This is zero if the cell is part of a
+ // wider cell.
+ Rune rune
+}
+
+// Append appends runes to the cell without changing the width. This is useful
+// when we want to use the cell to store escape sequences or other runes that
+// don't affect the width of the cell.
+func (c *Cell) Append(r ...rune) {
+ for i, r := range r {
+ if i == 0 && c.Rune == 0 {
+ c.Rune = r
+ continue
+ }
+ c.Comb = append(c.Comb, r)
+ }
+}
+
+// String returns the string content of the cell excluding any styles, links,
+// and escape sequences.
+func (c Cell) String() string {
+ if c.Rune == 0 {
+ return ""
+ }
+ if len(c.Comb) == 0 {
+ return string(c.Rune)
+ }
+ return string(append([]rune{c.Rune}, c.Comb...))
+}
+
+// Equal returns whether the cell is equal to the other cell.
+func (c *Cell) Equal(o *Cell) bool {
+ return o != nil &&
+ c.Width == o.Width &&
+ c.Rune == o.Rune &&
+ runesEqual(c.Comb, o.Comb) &&
+ c.Style.Equal(&o.Style) &&
+ c.Link.Equal(&o.Link)
+}
+
+// Empty returns whether the cell is an empty cell. An empty cell is a cell
+// with a width of 0, a rune of 0, and no combining runes.
+func (c Cell) Empty() bool {
+ return c.Width == 0 &&
+ c.Rune == 0 &&
+ len(c.Comb) == 0
+}
+
+// Reset resets the cell to the default state zero value.
+func (c *Cell) Reset() {
+ c.Rune = 0
+ c.Comb = nil
+ c.Width = 0
+ c.Style.Reset()
+ c.Link.Reset()
+}
+
+// Clear returns whether the cell consists of only attributes that don't
+// affect appearance of a space character.
+func (c *Cell) Clear() bool {
+ return c.Rune == ' ' && len(c.Comb) == 0 && c.Width == 1 && c.Style.Clear() && c.Link.Empty()
+}
+
+// Clone returns a copy of the cell.
+func (c *Cell) Clone() (n *Cell) {
+ n = new(Cell)
+ *n = *c
+ return
+}
+
+// Blank makes the cell a blank cell by setting the rune to a space, comb to
+// nil, and the width to 1.
+func (c *Cell) Blank() *Cell {
+ c.Rune = ' '
+ c.Comb = nil
+ c.Width = 1
+ return c
+}
+
+// Link represents a hyperlink in the terminal screen.
+type Link struct {
+ URL string
+ Params string
+}
+
+// String returns a string representation of the hyperlink.
+func (h Link) String() string {
+ return h.URL
+}
+
+// Reset resets the hyperlink to the default state zero value.
+func (h *Link) Reset() {
+ h.URL = ""
+ h.Params = ""
+}
+
+// Equal returns whether the hyperlink is equal to the other hyperlink.
+func (h *Link) Equal(o *Link) bool {
+ return o != nil && h.URL == o.URL && h.Params == o.Params
+}
+
+// Empty returns whether the hyperlink is empty.
+func (h Link) Empty() bool {
+ return h.URL == "" && h.Params == ""
+}
+
+// AttrMask is a bitmask for text attributes that can change the look of text.
+// These attributes can be combined to create different styles.
+type AttrMask uint8
+
+// These are the available text attributes that can be combined to create
+// different styles.
+const (
+ BoldAttr AttrMask = 1 << iota
+ FaintAttr
+ ItalicAttr
+ SlowBlinkAttr
+ RapidBlinkAttr
+ ReverseAttr
+ ConcealAttr
+ StrikethroughAttr
+
+ ResetAttr AttrMask = 0
+)
+
+// UnderlineStyle is the style of underline to use for text.
+type UnderlineStyle = ansi.UnderlineStyle
+
+// These are the available underline styles.
+const (
+ NoUnderline = ansi.NoUnderlineStyle
+ SingleUnderline = ansi.SingleUnderlineStyle
+ DoubleUnderline = ansi.DoubleUnderlineStyle
+ CurlyUnderline = ansi.CurlyUnderlineStyle
+ DottedUnderline = ansi.DottedUnderlineStyle
+ DashedUnderline = ansi.DashedUnderlineStyle
+)
+
+// Style represents the Style of a cell.
+type Style struct {
+ Fg ansi.Color
+ Bg ansi.Color
+ Ul ansi.Color
+ Attrs AttrMask
+ UlStyle UnderlineStyle
+}
+
+// Sequence returns the ANSI sequence that sets the style.
+func (s Style) Sequence() string {
+ if s.Empty() {
+ return ansi.ResetStyle
+ }
+
+ var b ansi.Style
+
+ if s.Attrs != 0 {
+ if s.Attrs&BoldAttr != 0 {
+ b = b.Bold()
+ }
+ if s.Attrs&FaintAttr != 0 {
+ b = b.Faint()
+ }
+ if s.Attrs&ItalicAttr != 0 {
+ b = b.Italic()
+ }
+ if s.Attrs&SlowBlinkAttr != 0 {
+ b = b.SlowBlink()
+ }
+ if s.Attrs&RapidBlinkAttr != 0 {
+ b = b.RapidBlink()
+ }
+ if s.Attrs&ReverseAttr != 0 {
+ b = b.Reverse()
+ }
+ if s.Attrs&ConcealAttr != 0 {
+ b = b.Conceal()
+ }
+ if s.Attrs&StrikethroughAttr != 0 {
+ b = b.Strikethrough()
+ }
+ }
+ if s.UlStyle != NoUnderline {
+ switch s.UlStyle {
+ case SingleUnderline:
+ b = b.Underline()
+ case DoubleUnderline:
+ b = b.DoubleUnderline()
+ case CurlyUnderline:
+ b = b.CurlyUnderline()
+ case DottedUnderline:
+ b = b.DottedUnderline()
+ case DashedUnderline:
+ b = b.DashedUnderline()
+ }
+ }
+ if s.Fg != nil {
+ b = b.ForegroundColor(s.Fg)
+ }
+ if s.Bg != nil {
+ b = b.BackgroundColor(s.Bg)
+ }
+ if s.Ul != nil {
+ b = b.UnderlineColor(s.Ul)
+ }
+
+ return b.String()
+}
+
+// DiffSequence returns the ANSI sequence that sets the style as a diff from
+// another style.
+func (s Style) DiffSequence(o Style) string {
+ if o.Empty() {
+ return s.Sequence()
+ }
+
+ var b ansi.Style
+
+ if !colorEqual(s.Fg, o.Fg) {
+ b = b.ForegroundColor(s.Fg)
+ }
+
+ if !colorEqual(s.Bg, o.Bg) {
+ b = b.BackgroundColor(s.Bg)
+ }
+
+ if !colorEqual(s.Ul, o.Ul) {
+ b = b.UnderlineColor(s.Ul)
+ }
+
+ var (
+ noBlink bool
+ isNormal bool
+ )
+
+ if s.Attrs != o.Attrs {
+ if s.Attrs&BoldAttr != o.Attrs&BoldAttr {
+ if s.Attrs&BoldAttr != 0 {
+ b = b.Bold()
+ } else if !isNormal {
+ isNormal = true
+ b = b.NormalIntensity()
+ }
+ }
+ if s.Attrs&FaintAttr != o.Attrs&FaintAttr {
+ if s.Attrs&FaintAttr != 0 {
+ b = b.Faint()
+ } else if !isNormal {
+ b = b.NormalIntensity()
+ }
+ }
+ if s.Attrs&ItalicAttr != o.Attrs&ItalicAttr {
+ if s.Attrs&ItalicAttr != 0 {
+ b = b.Italic()
+ } else {
+ b = b.NoItalic()
+ }
+ }
+ if s.Attrs&SlowBlinkAttr != o.Attrs&SlowBlinkAttr {
+ if s.Attrs&SlowBlinkAttr != 0 {
+ b = b.SlowBlink()
+ } else if !noBlink {
+ noBlink = true
+ b = b.NoBlink()
+ }
+ }
+ if s.Attrs&RapidBlinkAttr != o.Attrs&RapidBlinkAttr {
+ if s.Attrs&RapidBlinkAttr != 0 {
+ b = b.RapidBlink()
+ } else if !noBlink {
+ b = b.NoBlink()
+ }
+ }
+ if s.Attrs&ReverseAttr != o.Attrs&ReverseAttr {
+ if s.Attrs&ReverseAttr != 0 {
+ b = b.Reverse()
+ } else {
+ b = b.NoReverse()
+ }
+ }
+ if s.Attrs&ConcealAttr != o.Attrs&ConcealAttr {
+ if s.Attrs&ConcealAttr != 0 {
+ b = b.Conceal()
+ } else {
+ b = b.NoConceal()
+ }
+ }
+ if s.Attrs&StrikethroughAttr != o.Attrs&StrikethroughAttr {
+ if s.Attrs&StrikethroughAttr != 0 {
+ b = b.Strikethrough()
+ } else {
+ b = b.NoStrikethrough()
+ }
+ }
+ }
+
+ if s.UlStyle != o.UlStyle {
+ b = b.UnderlineStyle(s.UlStyle)
+ }
+
+ return b.String()
+}
+
+// Equal returns true if the style is equal to the other style.
+func (s *Style) Equal(o *Style) bool {
+ return s.Attrs == o.Attrs &&
+ s.UlStyle == o.UlStyle &&
+ colorEqual(s.Fg, o.Fg) &&
+ colorEqual(s.Bg, o.Bg) &&
+ colorEqual(s.Ul, o.Ul)
+}
+
+func colorEqual(c, o ansi.Color) bool {
+ if c == nil && o == nil {
+ return true
+ }
+ if c == nil || o == nil {
+ return false
+ }
+ cr, cg, cb, ca := c.RGBA()
+ or, og, ob, oa := o.RGBA()
+ return cr == or && cg == og && cb == ob && ca == oa
+}
+
+// Bold sets the bold attribute.
+func (s *Style) Bold(v bool) *Style {
+ if v {
+ s.Attrs |= BoldAttr
+ } else {
+ s.Attrs &^= BoldAttr
+ }
+ return s
+}
+
+// Faint sets the faint attribute.
+func (s *Style) Faint(v bool) *Style {
+ if v {
+ s.Attrs |= FaintAttr
+ } else {
+ s.Attrs &^= FaintAttr
+ }
+ return s
+}
+
+// Italic sets the italic attribute.
+func (s *Style) Italic(v bool) *Style {
+ if v {
+ s.Attrs |= ItalicAttr
+ } else {
+ s.Attrs &^= ItalicAttr
+ }
+ return s
+}
+
+// SlowBlink sets the slow blink attribute.
+func (s *Style) SlowBlink(v bool) *Style {
+ if v {
+ s.Attrs |= SlowBlinkAttr
+ } else {
+ s.Attrs &^= SlowBlinkAttr
+ }
+ return s
+}
+
+// RapidBlink sets the rapid blink attribute.
+func (s *Style) RapidBlink(v bool) *Style {
+ if v {
+ s.Attrs |= RapidBlinkAttr
+ } else {
+ s.Attrs &^= RapidBlinkAttr
+ }
+ return s
+}
+
+// Reverse sets the reverse attribute.
+func (s *Style) Reverse(v bool) *Style {
+ if v {
+ s.Attrs |= ReverseAttr
+ } else {
+ s.Attrs &^= ReverseAttr
+ }
+ return s
+}
+
+// Conceal sets the conceal attribute.
+func (s *Style) Conceal(v bool) *Style {
+ if v {
+ s.Attrs |= ConcealAttr
+ } else {
+ s.Attrs &^= ConcealAttr
+ }
+ return s
+}
+
+// Strikethrough sets the strikethrough attribute.
+func (s *Style) Strikethrough(v bool) *Style {
+ if v {
+ s.Attrs |= StrikethroughAttr
+ } else {
+ s.Attrs &^= StrikethroughAttr
+ }
+ return s
+}
+
+// UnderlineStyle sets the underline style.
+func (s *Style) UnderlineStyle(style UnderlineStyle) *Style {
+ s.UlStyle = style
+ return s
+}
+
+// Underline sets the underline attribute.
+// This is a syntactic sugar for [UnderlineStyle].
+func (s *Style) Underline(v bool) *Style {
+ if v {
+ return s.UnderlineStyle(SingleUnderline)
+ }
+ return s.UnderlineStyle(NoUnderline)
+}
+
+// Foreground sets the foreground color.
+func (s *Style) Foreground(c ansi.Color) *Style {
+ s.Fg = c
+ return s
+}
+
+// Background sets the background color.
+func (s *Style) Background(c ansi.Color) *Style {
+ s.Bg = c
+ return s
+}
+
+// UnderlineColor sets the underline color.
+func (s *Style) UnderlineColor(c ansi.Color) *Style {
+ s.Ul = c
+ return s
+}
+
+// Reset resets the style to default.
+func (s *Style) Reset() *Style {
+ s.Fg = nil
+ s.Bg = nil
+ s.Ul = nil
+ s.Attrs = ResetAttr
+ s.UlStyle = NoUnderline
+ return s
+}
+
+// Empty returns true if the style is empty.
+func (s *Style) Empty() bool {
+ return s.Fg == nil && s.Bg == nil && s.Ul == nil && s.Attrs == ResetAttr && s.UlStyle == NoUnderline
+}
+
+// Clear returns whether the style consists of only attributes that don't
+// affect appearance of a space character.
+func (s *Style) Clear() bool {
+ return s.UlStyle == NoUnderline &&
+ s.Attrs&^(BoldAttr|FaintAttr|ItalicAttr|SlowBlinkAttr|RapidBlinkAttr) == 0 &&
+ s.Fg == nil &&
+ s.Bg == nil &&
+ s.Ul == nil
+}
+
+func runesEqual(a, b []rune) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i, r := range a {
+ if r != b[i] {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/errors.go b/vendor/github.com/charmbracelet/x/cellbuf/errors.go
new file mode 100644
index 0000000..64258fe
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/errors.go
@@ -0,0 +1,6 @@
+package cellbuf
+
+import "errors"
+
+// ErrOutOfBounds is returned when the given x, y position is out of bounds.
+var ErrOutOfBounds = errors.New("out of bounds")
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/geom.go b/vendor/github.com/charmbracelet/x/cellbuf/geom.go
new file mode 100644
index 0000000..c12e6fb
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/geom.go
@@ -0,0 +1,21 @@
+package cellbuf
+
+import (
+ "image"
+)
+
+// Position represents an x, y position.
+type Position = image.Point
+
+// Pos is a shorthand for Position{X: x, Y: y}.
+func Pos(x, y int) Position {
+ return image.Pt(x, y)
+}
+
+// Rectange represents a rectangle.
+type Rectangle = image.Rectangle
+
+// Rect is a shorthand for Rectangle.
+func Rect(x, y, w, h int) Rectangle {
+ return image.Rect(x, y, x+w, y+h)
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/hardscroll.go b/vendor/github.com/charmbracelet/x/cellbuf/hardscroll.go
new file mode 100644
index 0000000..402ac06
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/hardscroll.go
@@ -0,0 +1,272 @@
+package cellbuf
+
+import (
+ "strings"
+
+ "github.com/charmbracelet/x/ansi"
+)
+
+// scrollOptimize optimizes the screen to transform the old buffer into the new
+// buffer.
+func (s *Screen) scrollOptimize() {
+ height := s.newbuf.Height()
+ if s.oldnum == nil || len(s.oldnum) < height {
+ s.oldnum = make([]int, height)
+ }
+
+ // Calculate the indices
+ s.updateHashmap()
+ if len(s.hashtab) < height {
+ return
+ }
+
+ // Pass 1 - from top to bottom scrolling up
+ for i := 0; i < height; {
+ for i < height && (s.oldnum[i] == newIndex || s.oldnum[i] <= i) {
+ i++
+ }
+ if i >= height {
+ break
+ }
+
+ shift := s.oldnum[i] - i // shift > 0
+ start := i
+
+ i++
+ for i < height && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift {
+ i++
+ }
+ end := i - 1 + shift
+
+ if !s.scrolln(shift, start, end, height-1) {
+ continue
+ }
+ }
+
+ // Pass 2 - from bottom to top scrolling down
+ for i := height - 1; i >= 0; {
+ for i >= 0 && (s.oldnum[i] == newIndex || s.oldnum[i] >= i) {
+ i--
+ }
+ if i < 0 {
+ break
+ }
+
+ shift := s.oldnum[i] - i // shift < 0
+ end := i
+
+ i--
+ for i >= 0 && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift {
+ i--
+ }
+
+ start := i + 1 - (-shift)
+ if !s.scrolln(shift, start, end, height-1) {
+ continue
+ }
+ }
+}
+
+// scrolln scrolls the screen up by n lines.
+func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
+ const (
+ nonDestScrollRegion = false
+ memoryBelow = false
+ )
+
+ blank := s.clearBlank()
+ if n > 0 {
+ // Scroll up (forward)
+ v = s.scrollUp(n, top, bot, 0, maxY, blank)
+ if !v {
+ s.buf.WriteString(ansi.SetTopBottomMargins(top+1, bot+1))
+
+ // XXX: How should we handle this in inline mode when not using alternate screen?
+ s.cur.X, s.cur.Y = -1, -1
+ v = s.scrollUp(n, top, bot, top, bot, blank)
+ s.buf.WriteString(ansi.SetTopBottomMargins(1, maxY+1))
+ s.cur.X, s.cur.Y = -1, -1
+ }
+
+ if !v {
+ v = s.scrollIdl(n, top, bot-n+1, blank)
+ }
+
+ // Clear newly shifted-in lines.
+ if v &&
+ (nonDestScrollRegion || (memoryBelow && bot == maxY)) {
+ if bot == maxY {
+ s.move(0, bot-n+1)
+ s.clearToBottom(nil)
+ } else {
+ for i := 0; i < n; i++ {
+ s.move(0, bot-i)
+ s.clearToEnd(nil, false)
+ }
+ }
+ }
+ } else if n < 0 {
+ // Scroll down (backward)
+ v = s.scrollDown(-n, top, bot, 0, maxY, blank)
+ if !v {
+ s.buf.WriteString(ansi.SetTopBottomMargins(top+1, bot+1))
+
+ // XXX: How should we handle this in inline mode when not using alternate screen?
+ s.cur.X, s.cur.Y = -1, -1
+ v = s.scrollDown(-n, top, bot, top, bot, blank)
+ s.buf.WriteString(ansi.SetTopBottomMargins(1, maxY+1))
+ s.cur.X, s.cur.Y = -1, -1
+
+ if !v {
+ v = s.scrollIdl(-n, bot+n+1, top, blank)
+ }
+
+ // Clear newly shifted-in lines.
+ if v &&
+ (nonDestScrollRegion || (memoryBelow && top == 0)) {
+ for i := 0; i < -n; i++ {
+ s.move(0, top+i)
+ s.clearToEnd(nil, false)
+ }
+ }
+ }
+ }
+
+ if !v {
+ return
+ }
+
+ s.scrollBuffer(s.curbuf, n, top, bot, blank)
+
+ // shift hash values too, they can be reused
+ s.scrollOldhash(n, top, bot)
+
+ return true
+}
+
+// scrollBuffer scrolls the buffer by n lines.
+func (s *Screen) scrollBuffer(b *Buffer, n, top, bot int, blank *Cell) {
+ if top < 0 || bot < top || bot >= b.Height() {
+ // Nothing to scroll
+ return
+ }
+
+ if n < 0 {
+ // shift n lines downwards
+ limit := top - n
+ for line := bot; line >= limit && line >= 0 && line >= top; line-- {
+ copy(b.Lines[line], b.Lines[line+n])
+ }
+ for line := top; line < limit && line <= b.Height()-1 && line <= bot; line++ {
+ b.FillRect(blank, Rect(0, line, b.Width(), 1))
+ }
+ }
+
+ if n > 0 {
+ // shift n lines upwards
+ limit := bot - n
+ for line := top; line <= limit && line <= b.Height()-1 && line <= bot; line++ {
+ copy(b.Lines[line], b.Lines[line+n])
+ }
+ for line := bot; line > limit && line >= 0 && line >= top; line-- {
+ b.FillRect(blank, Rect(0, line, b.Width(), 1))
+ }
+ }
+
+ s.touchLine(b.Width(), b.Height(), top, bot-top+1, true)
+}
+
+// touchLine marks the line as touched.
+func (s *Screen) touchLine(width, height, y, n int, changed bool) {
+ if n < 0 || y < 0 || y >= height {
+ return // Nothing to touch
+ }
+
+ for i := y; i < y+n && i < height; i++ {
+ if changed {
+ s.touch[i] = lineData{firstCell: 0, lastCell: width - 1}
+ } else {
+ delete(s.touch, i)
+ }
+ }
+}
+
+// scrollUp scrolls the screen up by n lines.
+func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
+ if n == 1 && top == minY && bot == maxY {
+ s.move(0, bot)
+ s.updatePen(blank)
+ s.buf.WriteByte('\n')
+ } else if n == 1 && bot == maxY {
+ s.move(0, top)
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.DeleteLine(1))
+ } else if top == minY && bot == maxY {
+ if s.xtermLike {
+ s.move(0, bot)
+ } else {
+ s.move(0, top)
+ }
+ s.updatePen(blank)
+ if s.xtermLike {
+ s.buf.WriteString(ansi.ScrollUp(n))
+ } else {
+ s.buf.WriteString(strings.Repeat("\n", n))
+ }
+ } else if bot == maxY {
+ s.move(0, top)
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.DeleteLine(n))
+ } else {
+ return false
+ }
+ return true
+}
+
+// scrollDown scrolls the screen down by n lines.
+func (s *Screen) scrollDown(n, top, bot, minY, maxY int, blank *Cell) bool {
+ if n == 1 && top == minY && bot == maxY {
+ s.move(0, top)
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.ReverseIndex)
+ } else if n == 1 && bot == maxY {
+ s.move(0, top)
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.InsertLine(1))
+ } else if top == minY && bot == maxY {
+ s.move(0, top)
+ s.updatePen(blank)
+ if s.xtermLike {
+ s.buf.WriteString(ansi.ScrollDown(n))
+ } else {
+ s.buf.WriteString(strings.Repeat(ansi.ReverseIndex, n))
+ }
+ } else if bot == maxY {
+ s.move(0, top)
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.InsertLine(n))
+ } else {
+ return false
+ }
+ return true
+}
+
+// scrollIdl scrolls the screen n lines by using [ansi.DL] at del and using
+// [ansi.IL] at ins.
+func (s *Screen) scrollIdl(n, del, ins int, blank *Cell) bool {
+ if n < 0 {
+ return false
+ }
+
+ // Delete lines
+ s.move(0, del)
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.DeleteLine(n))
+
+ // Insert lines
+ s.move(0, ins)
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.InsertLine(n))
+
+ return true
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/hashmap.go b/vendor/github.com/charmbracelet/x/cellbuf/hashmap.go
new file mode 100644
index 0000000..0d25b54
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/hashmap.go
@@ -0,0 +1,301 @@
+package cellbuf
+
+import (
+ "github.com/charmbracelet/x/ansi"
+)
+
+// hash returns the hash value of a [Line].
+func hash(l Line) (h uint64) {
+ for _, c := range l {
+ var r rune
+ if c == nil {
+ r = ansi.SP
+ } else {
+ r = c.Rune
+ }
+ h += (h << 5) + uint64(r)
+ }
+ return
+}
+
+// hashmap represents a single [Line] hash.
+type hashmap struct {
+ value uint64
+ oldcount, newcount int
+ oldindex, newindex int
+}
+
+// The value used to indicate lines created by insertions and scrolls.
+const newIndex = -1
+
+// updateHashmap updates the hashmap with the new hash value.
+func (s *Screen) updateHashmap() {
+ height := s.newbuf.Height()
+ if len(s.oldhash) >= height && len(s.newhash) >= height {
+ // rehash changed lines
+ for i := 0; i < height; i++ {
+ _, ok := s.touch[i]
+ if ok {
+ s.oldhash[i] = hash(s.curbuf.Line(i))
+ s.newhash[i] = hash(s.newbuf.Line(i))
+ }
+ }
+ } else {
+ // rehash all
+ if len(s.oldhash) != height {
+ s.oldhash = make([]uint64, height)
+ }
+ if len(s.newhash) != height {
+ s.newhash = make([]uint64, height)
+ }
+ for i := 0; i < height; i++ {
+ s.oldhash[i] = hash(s.curbuf.Line(i))
+ s.newhash[i] = hash(s.newbuf.Line(i))
+ }
+ }
+
+ s.hashtab = make([]hashmap, height*2)
+ for i := 0; i < height; i++ {
+ hashval := s.oldhash[i]
+
+ // Find matching hash or empty slot
+ idx := 0
+ for idx < len(s.hashtab) && s.hashtab[idx].value != 0 {
+ if s.hashtab[idx].value == hashval {
+ break
+ }
+ idx++
+ }
+
+ s.hashtab[idx].value = hashval // in case this is a new hash
+ s.hashtab[idx].oldcount++
+ s.hashtab[idx].oldindex = i
+ }
+ for i := 0; i < height; i++ {
+ hashval := s.newhash[i]
+
+ // Find matching hash or empty slot
+ idx := 0
+ for idx < len(s.hashtab) && s.hashtab[idx].value != 0 {
+ if s.hashtab[idx].value == hashval {
+ break
+ }
+ idx++
+ }
+
+ s.hashtab[idx].value = hashval // in case this is a new hash
+ s.hashtab[idx].newcount++
+ s.hashtab[idx].newindex = i
+
+ s.oldnum[i] = newIndex // init old indices slice
+ }
+
+ // Mark line pair corresponding to unique hash pairs.
+ for i := 0; i < len(s.hashtab) && s.hashtab[i].value != 0; i++ {
+ hsp := &s.hashtab[i]
+ if hsp.oldcount == 1 && hsp.newcount == 1 && hsp.oldindex != hsp.newindex {
+ s.oldnum[hsp.newindex] = hsp.oldindex
+ }
+ }
+
+ s.growHunks()
+
+ // Eliminate bad or impossible shifts. This includes removing those hunks
+ // which could not grow because of conflicts, as well those which are to be
+ // moved too far, they are likely to destroy more than carry.
+ for i := 0; i < height; {
+ var start, shift, size int
+ for i < height && s.oldnum[i] == newIndex {
+ i++
+ }
+ if i >= height {
+ break
+ }
+ start = i
+ shift = s.oldnum[i] - i
+ i++
+ for i < height && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift {
+ i++
+ }
+ size = i - start
+ if size < 3 || size+min(size/8, 2) < abs(shift) {
+ for start < i {
+ s.oldnum[start] = newIndex
+ start++
+ }
+ }
+ }
+
+ // After clearing invalid hunks, try grow the rest.
+ s.growHunks()
+}
+
+// scrollOldhash
+func (s *Screen) scrollOldhash(n, top, bot int) {
+ if len(s.oldhash) == 0 {
+ return
+ }
+
+ size := bot - top + 1 - abs(n)
+ if n > 0 {
+ // Move existing hashes up
+ copy(s.oldhash[top:], s.oldhash[top+n:top+n+size])
+ // Recalculate hashes for newly shifted-in lines
+ for i := bot; i > bot-n; i-- {
+ s.oldhash[i] = hash(s.curbuf.Line(i))
+ }
+ } else {
+ // Move existing hashes down
+ copy(s.oldhash[top-n:], s.oldhash[top:top+size])
+ // Recalculate hashes for newly shifted-in lines
+ for i := top; i < top-n; i++ {
+ s.oldhash[i] = hash(s.curbuf.Line(i))
+ }
+ }
+}
+
+func (s *Screen) growHunks() {
+ var (
+ backLimit int // limits for cells to fill
+ backRefLimit int // limit for references
+ i int
+ nextHunk int
+ )
+
+ height := s.newbuf.Height()
+ for i < height && s.oldnum[i] == newIndex {
+ i++
+ }
+ for ; i < height; i = nextHunk {
+ var (
+ forwardLimit int
+ forwardRefLimit int
+ end int
+ start = i
+ shift = s.oldnum[i] - i
+ )
+
+ // get forward limit
+ i = start + 1
+ for i < height &&
+ s.oldnum[i] != newIndex &&
+ s.oldnum[i]-i == shift {
+ i++
+ }
+
+ end = i
+ for i < height && s.oldnum[i] == newIndex {
+ i++
+ }
+
+ nextHunk = i
+ forwardLimit = i
+ if i >= height || s.oldnum[i] >= i {
+ forwardRefLimit = i
+ } else {
+ forwardRefLimit = s.oldnum[i]
+ }
+
+ i = start - 1
+
+ // grow back
+ if shift < 0 {
+ backLimit = backRefLimit + (-shift)
+ }
+ for i >= backLimit {
+ if s.newhash[i] == s.oldhash[i+shift] ||
+ s.costEffective(i+shift, i, shift < 0) {
+ s.oldnum[i] = i + shift
+ } else {
+ break
+ }
+ i--
+ }
+
+ i = end
+ // grow forward
+ if shift > 0 {
+ forwardLimit = forwardRefLimit - shift
+ }
+ for i < forwardLimit {
+ if s.newhash[i] == s.oldhash[i+shift] ||
+ s.costEffective(i+shift, i, shift > 0) {
+ s.oldnum[i] = i + shift
+ } else {
+ break
+ }
+ i++
+ }
+
+ backLimit = i
+ backRefLimit = backLimit
+ if shift > 0 {
+ backRefLimit += shift
+ }
+ }
+}
+
+// costEffective returns true if the cost of moving line 'from' to line 'to' seems to be
+// cost effective. 'blank' indicates whether the line 'to' would become blank.
+func (s *Screen) costEffective(from, to int, blank bool) bool {
+ if from == to {
+ return false
+ }
+
+ newFrom := s.oldnum[from]
+ if newFrom == newIndex {
+ newFrom = from
+ }
+
+ // On the left side of >= is the cost before moving. On the right side --
+ // cost after moving.
+
+ // Calculate costs before moving.
+ var costBeforeMove int
+ if blank {
+ // Cost of updating blank line at destination.
+ costBeforeMove = s.updateCostBlank(s.newbuf.Line(to))
+ } else {
+ // Cost of updating exiting line at destination.
+ costBeforeMove = s.updateCost(s.curbuf.Line(to), s.newbuf.Line(to))
+ }
+
+ // Add cost of updating source line
+ costBeforeMove += s.updateCost(s.curbuf.Line(newFrom), s.newbuf.Line(from))
+
+ // Calculate costs after moving.
+ var costAfterMove int
+ if newFrom == from {
+ // Source becomes blank after move
+ costAfterMove = s.updateCostBlank(s.newbuf.Line(from))
+ } else {
+ // Source gets updated from another line
+ costAfterMove = s.updateCost(s.curbuf.Line(newFrom), s.newbuf.Line(from))
+ }
+
+ // Add cost of moving source line to destination
+ costAfterMove += s.updateCost(s.curbuf.Line(from), s.newbuf.Line(to))
+
+ // Return true if moving is cost effective (costs less or equal)
+ return costBeforeMove >= costAfterMove
+}
+
+func (s *Screen) updateCost(from, to Line) (cost int) {
+ var fidx, tidx int
+ for i := s.newbuf.Width() - 1; i > 0; i, fidx, tidx = i-1, fidx+1, tidx+1 {
+ if !cellEqual(from.At(fidx), to.At(tidx)) {
+ cost++
+ }
+ }
+ return
+}
+
+func (s *Screen) updateCostBlank(to Line) (cost int) {
+ var tidx int
+ for i := s.newbuf.Width() - 1; i > 0; i, tidx = i-1, tidx+1 {
+ if !cellEqual(nil, to.At(tidx)) {
+ cost++
+ }
+ }
+ return
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/link.go b/vendor/github.com/charmbracelet/x/cellbuf/link.go
new file mode 100644
index 0000000..112f8e8
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/link.go
@@ -0,0 +1,14 @@
+package cellbuf
+
+import (
+ "github.com/charmbracelet/colorprofile"
+)
+
+// Convert converts a hyperlink to respect the given color profile.
+func ConvertLink(h Link, p colorprofile.Profile) Link {
+ if p == colorprofile.NoTTY {
+ return Link{}
+ }
+
+ return h
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/screen.go b/vendor/github.com/charmbracelet/x/cellbuf/screen.go
new file mode 100644
index 0000000..963b9ca
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/screen.go
@@ -0,0 +1,1457 @@
+package cellbuf
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "os"
+ "strings"
+ "sync"
+
+ "github.com/charmbracelet/colorprofile"
+ "github.com/charmbracelet/x/ansi"
+ "github.com/charmbracelet/x/term"
+)
+
+// ErrInvalidDimensions is returned when the dimensions of a window are invalid
+// for the operation.
+var ErrInvalidDimensions = errors.New("invalid dimensions")
+
+// notLocal returns whether the coordinates are not considered local movement
+// using the defined thresholds.
+// This takes the number of columns, and the coordinates of the current and
+// target positions.
+func notLocal(cols, fx, fy, tx, ty int) bool {
+ // The typical distance for a [ansi.CUP] sequence. Anything less than this
+ // is considered local movement.
+ const longDist = 8 - 1
+ return (tx > longDist) &&
+ (tx < cols-1-longDist) &&
+ (abs(ty-fy)+abs(tx-fx) > longDist)
+}
+
+// relativeCursorMove returns the relative cursor movement sequence using one or two
+// of the following sequences [ansi.CUU], [ansi.CUD], [ansi.CUF], [ansi.CUB],
+// [ansi.VPA], [ansi.HPA].
+// When overwrite is true, this will try to optimize the sequence by using the
+// screen cells values to move the cursor instead of using escape sequences.
+func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBackspace bool) string {
+ var seq strings.Builder
+
+ width, height := s.newbuf.Width(), s.newbuf.Height()
+ if ty != fy {
+ var yseq string
+ if s.xtermLike && !s.opts.RelativeCursor {
+ yseq = ansi.VerticalPositionAbsolute(ty + 1)
+ }
+
+ // OPTIM: Use [ansi.LF] and [ansi.ReverseIndex] as optimizations.
+
+ if ty > fy {
+ n := ty - fy
+ if cud := ansi.CursorDown(n); yseq == "" || len(cud) < len(yseq) {
+ yseq = cud
+ }
+ shouldScroll := !s.opts.AltScreen && fy+n >= s.scrollHeight
+ if lf := strings.Repeat("\n", n); shouldScroll || (fy+n < height && len(lf) < len(yseq)) {
+ // TODO: Ensure we're not unintentionally scrolling the screen down.
+ yseq = lf
+ s.scrollHeight = max(s.scrollHeight, fy+n)
+ }
+ } else if ty < fy {
+ n := fy - ty
+ if cuu := ansi.CursorUp(n); yseq == "" || len(cuu) < len(yseq) {
+ yseq = cuu
+ }
+ if n == 1 && fy-1 > 0 {
+ // TODO: Ensure we're not unintentionally scrolling the screen up.
+ yseq = ansi.ReverseIndex
+ }
+ }
+
+ seq.WriteString(yseq)
+ }
+
+ if tx != fx {
+ var xseq string
+ if s.xtermLike && !s.opts.RelativeCursor {
+ xseq = ansi.HorizontalPositionAbsolute(tx + 1)
+ }
+
+ if tx > fx {
+ n := tx - fx
+ if useTabs {
+ var tabs int
+ var col int
+ for col = fx; s.tabs.Next(col) <= tx; col = s.tabs.Next(col) {
+ tabs++
+ if col == s.tabs.Next(col) || col >= width-1 {
+ break
+ }
+ }
+
+ if tabs > 0 {
+ cht := ansi.CursorHorizontalForwardTab(tabs)
+ tab := strings.Repeat("\t", tabs)
+ if false && s.xtermLike && len(cht) < len(tab) {
+ // TODO: The linux console and some terminals such as
+ // Alacritty don't support [ansi.CHT]. Enable this when
+ // we have a way to detect this, or after 5 years when
+ // we're sure everyone has updated their terminals :P
+ seq.WriteString(cht)
+ } else {
+ seq.WriteString(tab)
+ }
+
+ n = tx - col
+ fx = col
+ }
+ }
+
+ if cuf := ansi.CursorForward(n); xseq == "" || len(cuf) < len(xseq) {
+ xseq = cuf
+ }
+
+ // If we have no attribute and style changes, overwrite is cheaper.
+ var ovw string
+ if overwrite && ty >= 0 {
+ for i := 0; i < n; i++ {
+ cell := s.newbuf.Cell(fx+i, ty)
+ if cell != nil && cell.Width > 0 {
+ i += cell.Width - 1
+ if !cell.Style.Equal(&s.cur.Style) || !cell.Link.Equal(&s.cur.Link) {
+ overwrite = false
+ break
+ }
+ }
+ }
+ }
+
+ if overwrite && ty >= 0 {
+ for i := 0; i < n; i++ {
+ cell := s.newbuf.Cell(fx+i, ty)
+ if cell != nil && cell.Width > 0 {
+ ovw += cell.String()
+ i += cell.Width - 1
+ } else {
+ ovw += " "
+ }
+ }
+ }
+
+ if overwrite && len(ovw) < len(xseq) {
+ xseq = ovw
+ }
+ } else if tx < fx {
+ n := fx - tx
+ if useTabs && s.xtermLike {
+ // VT100 does not support backward tabs [ansi.CBT].
+
+ col := fx
+
+ var cbt int // cursor backward tabs count
+ for s.tabs.Prev(col) >= tx {
+ col = s.tabs.Prev(col)
+ cbt++
+ if col == s.tabs.Prev(col) || col <= 0 {
+ break
+ }
+ }
+
+ if cbt > 0 {
+ seq.WriteString(ansi.CursorBackwardTab(cbt))
+ n = col - tx
+ }
+ }
+
+ if cub := ansi.CursorBackward(n); xseq == "" || len(cub) < len(xseq) {
+ xseq = cub
+ }
+
+ if useBackspace && n < len(xseq) {
+ xseq = strings.Repeat("\b", n)
+ }
+ }
+
+ seq.WriteString(xseq)
+ }
+
+ return seq.String()
+}
+
+// moveCursor moves and returns the cursor movement sequence to move the cursor
+// to the specified position.
+// When overwrite is true, this will try to optimize the sequence by using the
+// screen cells values to move the cursor instead of using escape sequences.
+func moveCursor(s *Screen, x, y int, overwrite bool) (seq string) {
+ fx, fy := s.cur.X, s.cur.Y
+
+ if !s.opts.RelativeCursor {
+ // Method #0: Use [ansi.CUP] if the distance is long.
+ seq = ansi.CursorPosition(x+1, y+1)
+ if fx == -1 || fy == -1 || notLocal(s.newbuf.Width(), fx, fy, x, y) {
+ return
+ }
+ }
+
+ // Optimize based on options.
+ trials := 0
+ if s.opts.HardTabs {
+ trials |= 2 // 0b10 in binary
+ }
+ if s.opts.Backspace {
+ trials |= 1 // 0b01 in binary
+ }
+
+ // Try all possible combinations of hard tabs and backspace optimizations.
+ for i := 0; i <= trials; i++ {
+ // Skip combinations that are not enabled.
+ if i & ^trials != 0 {
+ continue
+ }
+
+ useHardTabs := i&2 != 0
+ useBackspace := i&1 != 0
+
+ // Method #1: Use local movement sequences.
+ nseq := relativeCursorMove(s, fx, fy, x, y, overwrite, useHardTabs, useBackspace)
+ if (i == 0 && len(seq) == 0) || len(nseq) < len(seq) {
+ seq = nseq
+ }
+
+ // Method #2: Use [ansi.CR] and local movement sequences.
+ nseq = "\r" + relativeCursorMove(s, 0, fy, x, y, overwrite, useHardTabs, useBackspace)
+ if len(nseq) < len(seq) {
+ seq = nseq
+ }
+
+ if !s.opts.RelativeCursor {
+ // Method #3: Use [ansi.CursorHomePosition] and local movement sequences.
+ nseq = ansi.CursorHomePosition + relativeCursorMove(s, 0, 0, x, y, overwrite, useHardTabs, useBackspace)
+ if len(nseq) < len(seq) {
+ seq = nseq
+ }
+ }
+ }
+
+ return
+}
+
+// moveCursor moves the cursor to the specified position.
+func (s *Screen) moveCursor(x, y int, overwrite bool) {
+ if !s.opts.AltScreen && s.cur.X == -1 && s.cur.Y == -1 {
+ // First cursor movement in inline mode, move the cursor to the first
+ // column before moving to the target position.
+ s.buf.WriteByte('\r') //nolint:errcheck
+ s.cur.X, s.cur.Y = 0, 0
+ }
+ s.buf.WriteString(moveCursor(s, x, y, overwrite)) //nolint:errcheck
+ s.cur.X, s.cur.Y = x, y
+}
+
+func (s *Screen) move(x, y int) {
+ // XXX: Make sure we use the max height and width of the buffer in case
+ // we're in the middle of a resize operation.
+ width := max(s.newbuf.Width(), s.curbuf.Width())
+ height := max(s.newbuf.Height(), s.curbuf.Height())
+
+ if width > 0 && x >= width {
+ // Handle autowrap
+ y += (x / width)
+ x %= width
+ }
+
+ // XXX: Disable styles if there's any
+ // Some move operations such as [ansi.LF] can apply styles to the new
+ // cursor position, thus, we need to reset the styles before moving the
+ // cursor.
+ blank := s.clearBlank()
+ resetPen := y != s.cur.Y && !blank.Equal(&BlankCell)
+ if resetPen {
+ s.updatePen(nil)
+ }
+
+ // Reset wrap around (phantom cursor) state
+ if s.atPhantom {
+ s.cur.X = 0
+ s.buf.WriteByte('\r') //nolint:errcheck
+ s.atPhantom = false // reset phantom cell state
+ }
+
+ // TODO: Investigate if we need to handle this case and/or if we need the
+ // following code.
+ //
+ // if width > 0 && s.cur.X >= width {
+ // l := (s.cur.X + 1) / width
+ //
+ // s.cur.Y += l
+ // if height > 0 && s.cur.Y >= height {
+ // l -= s.cur.Y - height - 1
+ // }
+ //
+ // if l > 0 {
+ // s.cur.X = 0
+ // s.buf.WriteString("\r" + strings.Repeat("\n", l)) //nolint:errcheck
+ // }
+ // }
+
+ if height > 0 {
+ if s.cur.Y > height-1 {
+ s.cur.Y = height - 1
+ }
+ if y > height-1 {
+ y = height - 1
+ }
+ }
+
+ if x == s.cur.X && y == s.cur.Y {
+ // We give up later because we need to run checks for the phantom cell
+ // and others before we can determine if we can give up.
+ return
+ }
+
+ // We set the new cursor in [Screen.moveCursor].
+ s.moveCursor(x, y, true) // Overwrite cells if possible
+}
+
+// Cursor represents a terminal Cursor.
+type Cursor struct {
+ Style
+ Link
+ Position
+}
+
+// ScreenOptions are options for the screen.
+type ScreenOptions struct {
+ // Term is the terminal type to use when writing to the screen. When empty,
+ // `$TERM` is used from [os.Getenv].
+ Term string
+ // Profile is the color profile to use when writing to the screen.
+ Profile colorprofile.Profile
+ // RelativeCursor is whether to use relative cursor movements. This is
+ // useful when alt-screen is not used or when using inline mode.
+ RelativeCursor bool
+ // AltScreen is whether to use the alternate screen buffer.
+ AltScreen bool
+ // ShowCursor is whether to show the cursor.
+ ShowCursor bool
+ // HardTabs is whether to use hard tabs to optimize cursor movements.
+ HardTabs bool
+ // Backspace is whether to use backspace characters to move the cursor.
+ Backspace bool
+}
+
+// lineData represents the metadata for a line.
+type lineData struct {
+ // first and last changed cell indices
+ firstCell, lastCell int
+ // old index used for scrolling
+ oldIndex int //nolint:unused
+}
+
+// Screen represents the terminal screen.
+type Screen struct {
+ w io.Writer
+ buf *bytes.Buffer // buffer for writing to the screen
+ curbuf *Buffer // the current buffer
+ newbuf *Buffer // the new buffer
+ tabs *TabStops
+ touch map[int]lineData
+ queueAbove []string // the queue of strings to write above the screen
+ oldhash, newhash []uint64 // the old and new hash values for each line
+ hashtab []hashmap // the hashmap table
+ oldnum []int // old indices from previous hash
+ cur, saved Cursor // the current and saved cursors
+ opts ScreenOptions
+ mu sync.Mutex
+ method ansi.Method
+ scrollHeight int // keeps track of how many lines we've scrolled down (inline mode)
+ altScreenMode bool // whether alternate screen mode is enabled
+ cursorHidden bool // whether text cursor mode is enabled
+ clear bool // whether to force clear the screen
+ xtermLike bool // whether to use xterm-like optimizations, otherwise, it uses vt100 only
+ queuedText bool // whether we have queued non-zero width text queued up
+ atPhantom bool // whether the cursor is out of bounds and at a phantom cell
+}
+
+// SetMethod sets the method used to calculate the width of cells.
+func (s *Screen) SetMethod(method ansi.Method) {
+ s.method = method
+}
+
+// UseBackspaces sets whether to use backspace characters to move the cursor.
+func (s *Screen) UseBackspaces(v bool) {
+ s.opts.Backspace = v
+}
+
+// UseHardTabs sets whether to use hard tabs to optimize cursor movements.
+func (s *Screen) UseHardTabs(v bool) {
+ s.opts.HardTabs = v
+}
+
+// SetColorProfile sets the color profile to use when writing to the screen.
+func (s *Screen) SetColorProfile(p colorprofile.Profile) {
+ s.opts.Profile = p
+}
+
+// SetRelativeCursor sets whether to use relative cursor movements.
+func (s *Screen) SetRelativeCursor(v bool) {
+ s.opts.RelativeCursor = v
+}
+
+// EnterAltScreen enters the alternate screen buffer.
+func (s *Screen) EnterAltScreen() {
+ s.opts.AltScreen = true
+ s.clear = true
+ s.saved = s.cur
+}
+
+// ExitAltScreen exits the alternate screen buffer.
+func (s *Screen) ExitAltScreen() {
+ s.opts.AltScreen = false
+ s.clear = true
+ s.cur = s.saved
+}
+
+// ShowCursor shows the cursor.
+func (s *Screen) ShowCursor() {
+ s.opts.ShowCursor = true
+}
+
+// HideCursor hides the cursor.
+func (s *Screen) HideCursor() {
+ s.opts.ShowCursor = false
+}
+
+// Bounds implements Window.
+func (s *Screen) Bounds() Rectangle {
+ // Always return the new buffer bounds.
+ return s.newbuf.Bounds()
+}
+
+// Cell implements Window.
+func (s *Screen) Cell(x int, y int) *Cell {
+ return s.newbuf.Cell(x, y)
+}
+
+// Redraw forces a full redraw of the screen.
+func (s *Screen) Redraw() {
+ s.mu.Lock()
+ s.clear = true
+ s.mu.Unlock()
+}
+
+// Clear clears the screen with blank cells. This is a convenience method for
+// [Screen.Fill] with a nil cell.
+func (s *Screen) Clear() bool {
+ return s.ClearRect(s.newbuf.Bounds())
+}
+
+// ClearRect clears the given rectangle with blank cells. This is a convenience
+// method for [Screen.FillRect] with a nil cell.
+func (s *Screen) ClearRect(r Rectangle) bool {
+ return s.FillRect(nil, r)
+}
+
+// SetCell implements Window.
+func (s *Screen) SetCell(x int, y int, cell *Cell) (v bool) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ cellWidth := 1
+ if cell != nil {
+ cellWidth = cell.Width
+ }
+ if prev := s.curbuf.Cell(x, y); !cellEqual(prev, cell) {
+ chg, ok := s.touch[y]
+ if !ok {
+ chg = lineData{firstCell: x, lastCell: x + cellWidth}
+ } else {
+ chg.firstCell = min(chg.firstCell, x)
+ chg.lastCell = max(chg.lastCell, x+cellWidth)
+ }
+ s.touch[y] = chg
+ }
+
+ return s.newbuf.SetCell(x, y, cell)
+}
+
+// Fill implements Window.
+func (s *Screen) Fill(cell *Cell) bool {
+ return s.FillRect(cell, s.newbuf.Bounds())
+}
+
+// FillRect implements Window.
+func (s *Screen) FillRect(cell *Cell, r Rectangle) bool {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.newbuf.FillRect(cell, r)
+ for i := r.Min.Y; i < r.Max.Y; i++ {
+ s.touch[i] = lineData{firstCell: r.Min.X, lastCell: r.Max.X}
+ }
+ return true
+}
+
+// isXtermLike returns whether the terminal is xterm-like. This means that the
+// terminal supports ECMA-48 and ANSI X3.64 escape sequences.
+// TODO: Should this be a lookup table into each $TERM terminfo database? Like
+// we could keep a map of ANSI escape sequence to terminfo capability name and
+// check if the database supports the escape sequence. Instead of keeping a
+// list of terminal names here.
+func isXtermLike(termtype string) (v bool) {
+ parts := strings.Split(termtype, "-")
+ if len(parts) == 0 {
+ return
+ }
+
+ switch parts[0] {
+ case
+ "alacritty",
+ "contour",
+ "foot",
+ "ghostty",
+ "kitty",
+ "linux",
+ "rio",
+ "screen",
+ "st",
+ "tmux",
+ "wezterm",
+ "xterm":
+ v = true
+ }
+
+ return
+}
+
+// NewScreen creates a new Screen.
+func NewScreen(w io.Writer, width, height int, opts *ScreenOptions) (s *Screen) {
+ s = new(Screen)
+ s.w = w
+ if opts != nil {
+ s.opts = *opts
+ }
+
+ if s.opts.Term == "" {
+ s.opts.Term = os.Getenv("TERM")
+ }
+
+ if width <= 0 || height <= 0 {
+ if f, ok := w.(term.File); ok {
+ width, height, _ = term.GetSize(f.Fd())
+ }
+ }
+ if width < 0 {
+ width = 0
+ }
+ if height < 0 {
+ height = 0
+ }
+
+ s.buf = new(bytes.Buffer)
+ s.xtermLike = isXtermLike(s.opts.Term)
+ s.curbuf = NewBuffer(width, height)
+ s.newbuf = NewBuffer(width, height)
+ s.cur = Cursor{Position: Pos(-1, -1)} // start at -1 to force a move
+ s.saved = s.cur
+ s.reset()
+
+ return
+}
+
+// Width returns the width of the screen.
+func (s *Screen) Width() int {
+ return s.newbuf.Width()
+}
+
+// Height returns the height of the screen.
+func (s *Screen) Height() int {
+ return s.newbuf.Height()
+}
+
+// cellEqual returns whether the two cells are equal. A nil cell is considered
+// a [BlankCell].
+func cellEqual(a, b *Cell) bool {
+ if a == b {
+ return true
+ }
+ if a == nil {
+ a = &BlankCell
+ }
+ if b == nil {
+ b = &BlankCell
+ }
+ return a.Equal(b)
+}
+
+// putCell draws a cell at the current cursor position.
+func (s *Screen) putCell(cell *Cell) {
+ width, height := s.newbuf.Width(), s.newbuf.Height()
+ if s.opts.AltScreen && s.cur.X == width-1 && s.cur.Y == height-1 {
+ s.putCellLR(cell)
+ } else {
+ s.putAttrCell(cell)
+ }
+}
+
+// wrapCursor wraps the cursor to the next line.
+//
+//nolint:unused
+func (s *Screen) wrapCursor() {
+ const autoRightMargin = true
+ if autoRightMargin {
+ // Assume we have auto wrap mode enabled.
+ s.cur.X = 0
+ s.cur.Y++
+ } else {
+ s.cur.X--
+ }
+}
+
+func (s *Screen) putAttrCell(cell *Cell) {
+ if cell != nil && cell.Empty() {
+ // XXX: Zero width cells are special and should not be written to the
+ // screen no matter what other attributes they have.
+ // Zero width cells are used for wide characters that are split into
+ // multiple cells.
+ return
+ }
+
+ if cell == nil {
+ cell = s.clearBlank()
+ }
+
+ // We're at pending wrap state (phantom cell), incoming cell should
+ // wrap.
+ if s.atPhantom {
+ s.wrapCursor()
+ s.atPhantom = false
+ }
+
+ s.updatePen(cell)
+ s.buf.WriteRune(cell.Rune) //nolint:errcheck
+ for _, c := range cell.Comb {
+ s.buf.WriteRune(c) //nolint:errcheck
+ }
+
+ s.cur.X += cell.Width
+
+ if cell.Width > 0 {
+ s.queuedText = true
+ }
+
+ if s.cur.X >= s.newbuf.Width() {
+ s.atPhantom = true
+ }
+}
+
+// putCellLR draws a cell at the lower right corner of the screen.
+func (s *Screen) putCellLR(cell *Cell) {
+ // Optimize for the lower right corner cell.
+ curX := s.cur.X
+ if cell == nil || !cell.Empty() {
+ s.buf.WriteString(ansi.ResetAutoWrapMode) //nolint:errcheck
+ s.putAttrCell(cell)
+ // Writing to lower-right corner cell should not wrap.
+ s.atPhantom = false
+ s.cur.X = curX
+ s.buf.WriteString(ansi.SetAutoWrapMode) //nolint:errcheck
+ }
+}
+
+// updatePen updates the cursor pen styles.
+func (s *Screen) updatePen(cell *Cell) {
+ if cell == nil {
+ cell = &BlankCell
+ }
+
+ if s.opts.Profile != 0 {
+ // Downsample colors to the given color profile.
+ cell.Style = ConvertStyle(cell.Style, s.opts.Profile)
+ cell.Link = ConvertLink(cell.Link, s.opts.Profile)
+ }
+
+ if !cell.Style.Equal(&s.cur.Style) {
+ seq := cell.Style.DiffSequence(s.cur.Style)
+ if cell.Style.Empty() && len(seq) > len(ansi.ResetStyle) {
+ seq = ansi.ResetStyle
+ }
+ s.buf.WriteString(seq) //nolint:errcheck
+ s.cur.Style = cell.Style
+ }
+ if !cell.Link.Equal(&s.cur.Link) {
+ s.buf.WriteString(ansi.SetHyperlink(cell.Link.URL, cell.Link.Params)) //nolint:errcheck
+ s.cur.Link = cell.Link
+ }
+}
+
+// emitRange emits a range of cells to the buffer. It it equivalent to calling
+// [Screen.putCell] for each cell in the range. This is optimized to use
+// [ansi.ECH] and [ansi.REP].
+// Returns whether the cursor is at the end of interval or somewhere in the
+// middle.
+func (s *Screen) emitRange(line Line, n int) (eoi bool) {
+ for n > 0 {
+ var count int
+ for n > 1 && !cellEqual(line.At(0), line.At(1)) {
+ s.putCell(line.At(0))
+ line = line[1:]
+ n--
+ }
+
+ cell0 := line[0]
+ if n == 1 {
+ s.putCell(cell0)
+ return false
+ }
+
+ count = 2
+ for count < n && cellEqual(line.At(count), cell0) {
+ count++
+ }
+
+ ech := ansi.EraseCharacter(count)
+ cup := ansi.CursorPosition(s.cur.X+count, s.cur.Y)
+ rep := ansi.RepeatPreviousCharacter(count)
+ if s.xtermLike && count > len(ech)+len(cup) && cell0 != nil && cell0.Clear() {
+ s.updatePen(cell0)
+ s.buf.WriteString(ech) //nolint:errcheck
+
+ // If this is the last cell, we don't need to move the cursor.
+ if count < n {
+ s.move(s.cur.X+count, s.cur.Y)
+ } else {
+ return true // cursor in the middle
+ }
+ } else if s.xtermLike && count > len(rep) &&
+ (cell0 == nil || (len(cell0.Comb) == 0 && cell0.Rune < 256)) {
+ // We only support ASCII characters. Most terminals will handle
+ // non-ASCII characters correctly, but some might not, ahem xterm.
+ //
+ // NOTE: [ansi.REP] only repeats the last rune and won't work
+ // if the last cell contains multiple runes.
+
+ wrapPossible := s.cur.X+count >= s.newbuf.Width()
+ repCount := count
+ if wrapPossible {
+ repCount--
+ }
+
+ s.updatePen(cell0)
+ s.putCell(cell0)
+ repCount-- // cell0 is a single width cell ASCII character
+
+ s.buf.WriteString(ansi.RepeatPreviousCharacter(repCount)) //nolint:errcheck
+ s.cur.X += repCount
+ if wrapPossible {
+ s.putCell(cell0)
+ }
+ } else {
+ for i := 0; i < count; i++ {
+ s.putCell(line.At(i))
+ }
+ }
+
+ line = line[clamp(count, 0, len(line)):]
+ n -= count
+ }
+
+ return
+}
+
+// putRange puts a range of cells from the old line to the new line.
+// Returns whether the cursor is at the end of interval or somewhere in the
+// middle.
+func (s *Screen) putRange(oldLine, newLine Line, y, start, end int) (eoi bool) {
+ inline := min(len(ansi.CursorPosition(start+1, y+1)),
+ min(len(ansi.HorizontalPositionAbsolute(start+1)),
+ len(ansi.CursorForward(start+1))))
+ if (end - start + 1) > inline {
+ var j, same int
+ for j, same = start, 0; j <= end; j++ {
+ oldCell, newCell := oldLine.At(j), newLine.At(j)
+ if same == 0 && oldCell != nil && oldCell.Empty() {
+ continue
+ }
+ if cellEqual(oldCell, newCell) {
+ same++
+ } else {
+ if same > end-start {
+ s.emitRange(newLine[start:], j-same-start)
+ s.move(j, y)
+ start = j
+ }
+ same = 0
+ }
+ }
+
+ i := s.emitRange(newLine[start:], j-same-start)
+
+ // Always return 1 for the next [Screen.move] after a [Screen.putRange] if
+ // we found identical characters at end of interval.
+ if same == 0 {
+ return i
+ }
+ return true
+ }
+
+ return s.emitRange(newLine[start:], end-start+1)
+}
+
+// clearToEnd clears the screen from the current cursor position to the end of
+// line.
+func (s *Screen) clearToEnd(blank *Cell, force bool) { //nolint:unparam
+ if s.cur.Y >= 0 {
+ curline := s.curbuf.Line(s.cur.Y)
+ for j := s.cur.X; j < s.curbuf.Width(); j++ {
+ if j >= 0 {
+ c := curline.At(j)
+ if !cellEqual(c, blank) {
+ curline.Set(j, blank)
+ force = true
+ }
+ }
+ }
+ }
+
+ if force {
+ s.updatePen(blank)
+ count := s.newbuf.Width() - s.cur.X
+ if s.el0Cost() <= count {
+ s.buf.WriteString(ansi.EraseLineRight) //nolint:errcheck
+ } else {
+ for i := 0; i < count; i++ {
+ s.putCell(blank)
+ }
+ }
+ }
+}
+
+// clearBlank returns a blank cell based on the current cursor background color.
+func (s *Screen) clearBlank() *Cell {
+ c := BlankCell
+ if !s.cur.Style.Empty() || !s.cur.Link.Empty() {
+ c.Style = s.cur.Style
+ c.Link = s.cur.Link
+ }
+ return &c
+}
+
+// insertCells inserts the count cells pointed by the given line at the current
+// cursor position.
+func (s *Screen) insertCells(line Line, count int) {
+ if s.xtermLike {
+ // Use [ansi.ICH] as an optimization.
+ s.buf.WriteString(ansi.InsertCharacter(count)) //nolint:errcheck
+ } else {
+ // Otherwise, use [ansi.IRM] mode.
+ s.buf.WriteString(ansi.SetInsertReplaceMode) //nolint:errcheck
+ }
+
+ for i := 0; count > 0; i++ {
+ s.putAttrCell(line[i])
+ count--
+ }
+
+ if !s.xtermLike {
+ s.buf.WriteString(ansi.ResetInsertReplaceMode) //nolint:errcheck
+ }
+}
+
+// el0Cost returns the cost of using [ansi.EL] 0 i.e. [ansi.EraseLineRight]. If
+// this terminal supports background color erase, it can be cheaper to use
+// [ansi.EL] 0 i.e. [ansi.EraseLineRight] to clear
+// trailing spaces.
+func (s *Screen) el0Cost() int {
+ if s.xtermLike {
+ return 0
+ }
+ return len(ansi.EraseLineRight)
+}
+
+// transformLine transforms the given line in the current window to the
+// corresponding line in the new window. It uses [ansi.ICH] and [ansi.DCH] to
+// insert or delete characters.
+func (s *Screen) transformLine(y int) {
+ var firstCell, oLastCell, nLastCell int // first, old last, new last index
+ oldLine := s.curbuf.Line(y)
+ newLine := s.newbuf.Line(y)
+
+ // Find the first changed cell in the line
+ var lineChanged bool
+ for i := 0; i < s.newbuf.Width(); i++ {
+ if !cellEqual(newLine.At(i), oldLine.At(i)) {
+ lineChanged = true
+ break
+ }
+ }
+
+ const ceolStandoutGlitch = false
+ if ceolStandoutGlitch && lineChanged {
+ s.move(0, y)
+ s.clearToEnd(nil, false)
+ s.putRange(oldLine, newLine, y, 0, s.newbuf.Width()-1)
+ } else {
+ blank := newLine.At(0)
+
+ // It might be cheaper to clear leading spaces with [ansi.EL] 1 i.e.
+ // [ansi.EraseLineLeft].
+ if blank == nil || blank.Clear() {
+ var oFirstCell, nFirstCell int
+ for oFirstCell = 0; oFirstCell < s.curbuf.Width(); oFirstCell++ {
+ if !cellEqual(oldLine.At(oFirstCell), blank) {
+ break
+ }
+ }
+ for nFirstCell = 0; nFirstCell < s.newbuf.Width(); nFirstCell++ {
+ if !cellEqual(newLine.At(nFirstCell), blank) {
+ break
+ }
+ }
+
+ if nFirstCell == oFirstCell {
+ firstCell = nFirstCell
+
+ // Find the first differing cell
+ for firstCell < s.newbuf.Width() &&
+ cellEqual(oldLine.At(firstCell), newLine.At(firstCell)) {
+ firstCell++
+ }
+ } else if oFirstCell > nFirstCell {
+ firstCell = nFirstCell
+ } else if oFirstCell < nFirstCell {
+ firstCell = oFirstCell
+ el1Cost := len(ansi.EraseLineLeft)
+ if el1Cost < nFirstCell-oFirstCell {
+ if nFirstCell >= s.newbuf.Width() {
+ s.move(0, y)
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.EraseLineRight) //nolint:errcheck
+ } else {
+ s.move(nFirstCell-1, y)
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.EraseLineLeft) //nolint:errcheck
+ }
+
+ for firstCell < nFirstCell {
+ oldLine.Set(firstCell, blank)
+ firstCell++
+ }
+ }
+ }
+ } else {
+ // Find the first differing cell
+ for firstCell < s.newbuf.Width() && cellEqual(newLine.At(firstCell), oldLine.At(firstCell)) {
+ firstCell++
+ }
+ }
+
+ // If we didn't find one, we're done
+ if firstCell >= s.newbuf.Width() {
+ return
+ }
+
+ blank = newLine.At(s.newbuf.Width() - 1)
+ if blank != nil && !blank.Clear() {
+ // Find the last differing cell
+ nLastCell = s.newbuf.Width() - 1
+ for nLastCell > firstCell && cellEqual(newLine.At(nLastCell), oldLine.At(nLastCell)) {
+ nLastCell--
+ }
+
+ if nLastCell >= firstCell {
+ s.move(firstCell, y)
+ s.putRange(oldLine, newLine, y, firstCell, nLastCell)
+ if firstCell < len(oldLine) && firstCell < len(newLine) {
+ copy(oldLine[firstCell:], newLine[firstCell:])
+ } else {
+ copy(oldLine, newLine)
+ }
+ }
+
+ return
+ }
+
+ // Find last non-blank cell in the old line.
+ oLastCell = s.curbuf.Width() - 1
+ for oLastCell > firstCell && cellEqual(oldLine.At(oLastCell), blank) {
+ oLastCell--
+ }
+
+ // Find last non-blank cell in the new line.
+ nLastCell = s.newbuf.Width() - 1
+ for nLastCell > firstCell && cellEqual(newLine.At(nLastCell), blank) {
+ nLastCell--
+ }
+
+ if nLastCell == firstCell && s.el0Cost() < oLastCell-nLastCell {
+ s.move(firstCell, y)
+ if !cellEqual(newLine.At(firstCell), blank) {
+ s.putCell(newLine.At(firstCell))
+ }
+ s.clearToEnd(blank, false)
+ } else if nLastCell != oLastCell &&
+ !cellEqual(newLine.At(nLastCell), oldLine.At(oLastCell)) {
+ s.move(firstCell, y)
+ if oLastCell-nLastCell > s.el0Cost() {
+ if s.putRange(oldLine, newLine, y, firstCell, nLastCell) {
+ s.move(nLastCell+1, y)
+ }
+ s.clearToEnd(blank, false)
+ } else {
+ n := max(nLastCell, oLastCell)
+ s.putRange(oldLine, newLine, y, firstCell, n)
+ }
+ } else {
+ nLastNonBlank := nLastCell
+ oLastNonBlank := oLastCell
+
+ // Find the last cells that really differ.
+ // Can be -1 if no cells differ.
+ for cellEqual(newLine.At(nLastCell), oldLine.At(oLastCell)) {
+ if !cellEqual(newLine.At(nLastCell-1), oldLine.At(oLastCell-1)) {
+ break
+ }
+ nLastCell--
+ oLastCell--
+ if nLastCell == -1 || oLastCell == -1 {
+ break
+ }
+ }
+
+ n := min(oLastCell, nLastCell)
+ if n >= firstCell {
+ s.move(firstCell, y)
+ s.putRange(oldLine, newLine, y, firstCell, n)
+ }
+
+ if oLastCell < nLastCell {
+ m := max(nLastNonBlank, oLastNonBlank)
+ if n != 0 {
+ for n > 0 {
+ wide := newLine.At(n + 1)
+ if wide == nil || !wide.Empty() {
+ break
+ }
+ n--
+ oLastCell--
+ }
+ } else if n >= firstCell && newLine.At(n) != nil && newLine.At(n).Width > 1 {
+ next := newLine.At(n + 1)
+ for next != nil && next.Empty() {
+ n++
+ oLastCell++
+ }
+ }
+
+ s.move(n+1, y)
+ ichCost := 3 + nLastCell - oLastCell
+ if s.xtermLike && (nLastCell < nLastNonBlank || ichCost > (m-n)) {
+ s.putRange(oldLine, newLine, y, n+1, m)
+ } else {
+ s.insertCells(newLine[n+1:], nLastCell-oLastCell)
+ }
+ } else if oLastCell > nLastCell {
+ s.move(n+1, y)
+ dchCost := 3 + oLastCell - nLastCell
+ if dchCost > len(ansi.EraseLineRight)+nLastNonBlank-(n+1) {
+ if s.putRange(oldLine, newLine, y, n+1, nLastNonBlank) {
+ s.move(nLastNonBlank+1, y)
+ }
+ s.clearToEnd(blank, false)
+ } else {
+ s.updatePen(blank)
+ s.deleteCells(oLastCell - nLastCell)
+ }
+ }
+ }
+ }
+
+ // Update the old line with the new line
+ if firstCell < len(oldLine) && firstCell < len(newLine) {
+ copy(oldLine[firstCell:], newLine[firstCell:])
+ } else {
+ copy(oldLine, newLine)
+ }
+}
+
+// deleteCells deletes the count cells at the current cursor position and moves
+// the rest of the line to the left. This is equivalent to [ansi.DCH].
+func (s *Screen) deleteCells(count int) {
+ // [ansi.DCH] will shift in cells from the right margin so we need to
+ // ensure that they are the right style.
+ s.buf.WriteString(ansi.DeleteCharacter(count)) //nolint:errcheck
+}
+
+// clearToBottom clears the screen from the current cursor position to the end
+// of the screen.
+func (s *Screen) clearToBottom(blank *Cell) {
+ row, col := s.cur.Y, s.cur.X
+ if row < 0 {
+ row = 0
+ }
+
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.EraseScreenBelow) //nolint:errcheck
+ // Clear the rest of the current line
+ s.curbuf.ClearRect(Rect(col, row, s.curbuf.Width()-col, 1))
+ // Clear everything below the current line
+ s.curbuf.ClearRect(Rect(0, row+1, s.curbuf.Width(), s.curbuf.Height()-row-1))
+}
+
+// clearBottom tests if clearing the end of the screen would satisfy part of
+// the screen update. Scan backwards through lines in the screen checking if
+// each is blank and one or more are changed.
+// It returns the top line.
+func (s *Screen) clearBottom(total int) (top int) {
+ if total <= 0 {
+ return
+ }
+
+ top = total
+ last := s.newbuf.Width()
+ blank := s.clearBlank()
+ canClearWithBlank := blank == nil || blank.Clear()
+
+ if canClearWithBlank {
+ var row int
+ for row = total - 1; row >= 0; row-- {
+ oldLine := s.curbuf.Line(row)
+ newLine := s.newbuf.Line(row)
+
+ var col int
+ ok := true
+ for col = 0; ok && col < last; col++ {
+ ok = cellEqual(newLine.At(col), blank)
+ }
+ if !ok {
+ break
+ }
+
+ for col = 0; ok && col < last; col++ {
+ ok = len(oldLine) == last && cellEqual(oldLine.At(col), blank)
+ }
+ if !ok {
+ top = row
+ }
+ }
+
+ if top < total {
+ s.move(0, top-1) // top is 1-based
+ s.clearToBottom(blank)
+ if s.oldhash != nil && s.newhash != nil &&
+ row < len(s.oldhash) && row < len(s.newhash) {
+ for row := top; row < s.newbuf.Height(); row++ {
+ s.oldhash[row] = s.newhash[row]
+ }
+ }
+ }
+ }
+
+ return
+}
+
+// clearScreen clears the screen and put cursor at home.
+func (s *Screen) clearScreen(blank *Cell) {
+ s.updatePen(blank)
+ s.buf.WriteString(ansi.CursorHomePosition) //nolint:errcheck
+ s.buf.WriteString(ansi.EraseEntireScreen) //nolint:errcheck
+ s.cur.X, s.cur.Y = 0, 0
+ s.curbuf.Fill(blank)
+}
+
+// clearBelow clears everything below and including the row.
+func (s *Screen) clearBelow(blank *Cell, row int) {
+ s.move(0, row)
+ s.clearToBottom(blank)
+}
+
+// clearUpdate forces a screen redraw.
+func (s *Screen) clearUpdate() {
+ blank := s.clearBlank()
+ var nonEmpty int
+ if s.opts.AltScreen {
+ // XXX: We're using the maximum height of the two buffers to ensure
+ // we write newly added lines to the screen in [Screen.transformLine].
+ nonEmpty = max(s.curbuf.Height(), s.newbuf.Height())
+ s.clearScreen(blank)
+ } else {
+ nonEmpty = s.newbuf.Height()
+ s.clearBelow(blank, 0)
+ }
+ nonEmpty = s.clearBottom(nonEmpty)
+ for i := 0; i < nonEmpty; i++ {
+ s.transformLine(i)
+ }
+}
+
+// Flush flushes the buffer to the screen.
+func (s *Screen) Flush() (err error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.flush()
+}
+
+func (s *Screen) flush() (err error) {
+ // Write the buffer
+ if s.buf.Len() > 0 {
+ _, err = s.w.Write(s.buf.Bytes()) //nolint:errcheck
+ if err == nil {
+ s.buf.Reset()
+ }
+ }
+
+ return
+}
+
+// Render renders changes of the screen to the internal buffer. Call
+// [Screen.Flush] to flush pending changes to the screen.
+func (s *Screen) Render() {
+ s.mu.Lock()
+ s.render()
+ s.mu.Unlock()
+}
+
+func (s *Screen) render() {
+ // Do we need to render anything?
+ if s.opts.AltScreen == s.altScreenMode &&
+ !s.opts.ShowCursor == s.cursorHidden &&
+ !s.clear &&
+ len(s.touch) == 0 &&
+ len(s.queueAbove) == 0 {
+ return
+ }
+
+ // TODO: Investigate whether this is necessary. Theoretically, terminals
+ // can add/remove tab stops and we should be able to handle that. We could
+ // use [ansi.DECTABSR] to read the tab stops, but that's not implemented in
+ // most terminals :/
+ // // Are we using hard tabs? If so, ensure tabs are using the
+ // // default interval using [ansi.DECST8C].
+ // if s.opts.HardTabs && !s.initTabs {
+ // s.buf.WriteString(ansi.SetTabEvery8Columns)
+ // s.initTabs = true
+ // }
+
+ // Do we need alt-screen mode?
+ if s.opts.AltScreen != s.altScreenMode {
+ if s.opts.AltScreen {
+ s.buf.WriteString(ansi.SetAltScreenSaveCursorMode)
+ } else {
+ s.buf.WriteString(ansi.ResetAltScreenSaveCursorMode)
+ }
+ s.altScreenMode = s.opts.AltScreen
+ }
+
+ // Do we need text cursor mode?
+ if !s.opts.ShowCursor != s.cursorHidden {
+ s.cursorHidden = !s.opts.ShowCursor
+ if s.cursorHidden {
+ s.buf.WriteString(ansi.HideCursor)
+ }
+ }
+
+ // Do we have queued strings to write above the screen?
+ if len(s.queueAbove) > 0 {
+ // TODO: Use scrolling region if available.
+ // TODO: Use [Screen.Write] [io.Writer] interface.
+
+ // We need to scroll the screen up by the number of lines in the queue.
+ // We can't use [ansi.SU] because we want the cursor to move down until
+ // it reaches the bottom of the screen.
+ s.move(0, s.newbuf.Height()-1)
+ s.buf.WriteString(strings.Repeat("\n", len(s.queueAbove)))
+ s.cur.Y += len(s.queueAbove)
+ // XXX: Now go to the top of the screen, insert new lines, and write
+ // the queued strings. It is important to use [Screen.moveCursor]
+ // instead of [Screen.move] because we don't want to perform any checks
+ // on the cursor position.
+ s.moveCursor(0, 0, false)
+ s.buf.WriteString(ansi.InsertLine(len(s.queueAbove)))
+ for _, line := range s.queueAbove {
+ s.buf.WriteString(line + "\r\n")
+ }
+
+ // Clear the queue
+ s.queueAbove = s.queueAbove[:0]
+ }
+
+ var nonEmpty int
+
+ // XXX: In inline mode, after a screen resize, we need to clear the extra
+ // lines at the bottom of the screen. This is because in inline mode, we
+ // don't use the full screen height and the current buffer size might be
+ // larger than the new buffer size.
+ partialClear := !s.opts.AltScreen && s.cur.X != -1 && s.cur.Y != -1 &&
+ s.curbuf.Width() == s.newbuf.Width() &&
+ s.curbuf.Height() > 0 &&
+ s.curbuf.Height() > s.newbuf.Height()
+
+ if !s.clear && partialClear {
+ s.clearBelow(nil, s.newbuf.Height()-1)
+ }
+
+ if s.clear {
+ s.clearUpdate()
+ s.clear = false
+ } else if len(s.touch) > 0 {
+ if s.opts.AltScreen {
+ // Optimize scrolling for the alternate screen buffer.
+ // TODO: Should we optimize for inline mode as well? If so, we need
+ // to know the actual cursor position to use [ansi.DECSTBM].
+ s.scrollOptimize()
+ }
+
+ var changedLines int
+ var i int
+
+ if s.opts.AltScreen {
+ nonEmpty = min(s.curbuf.Height(), s.newbuf.Height())
+ } else {
+ nonEmpty = s.newbuf.Height()
+ }
+
+ nonEmpty = s.clearBottom(nonEmpty)
+ for i = 0; i < nonEmpty; i++ {
+ _, ok := s.touch[i]
+ if ok {
+ s.transformLine(i)
+ changedLines++
+ }
+ }
+ }
+
+ // Sync windows and screen
+ s.touch = make(map[int]lineData, s.newbuf.Height())
+
+ if s.curbuf.Width() != s.newbuf.Width() || s.curbuf.Height() != s.newbuf.Height() {
+ // Resize the old buffer to match the new buffer.
+ _, oldh := s.curbuf.Width(), s.curbuf.Height()
+ s.curbuf.Resize(s.newbuf.Width(), s.newbuf.Height())
+ // Sync new lines to old lines
+ for i := oldh - 1; i < s.newbuf.Height(); i++ {
+ copy(s.curbuf.Line(i), s.newbuf.Line(i))
+ }
+ }
+
+ s.updatePen(nil) // nil indicates a blank cell with no styles
+
+ // Do we have enough changes to justify toggling the cursor?
+ if s.buf.Len() > 1 && s.opts.ShowCursor && !s.cursorHidden && s.queuedText {
+ nb := new(bytes.Buffer)
+ nb.Grow(s.buf.Len() + len(ansi.HideCursor) + len(ansi.ShowCursor))
+ nb.WriteString(ansi.HideCursor)
+ nb.Write(s.buf.Bytes())
+ nb.WriteString(ansi.ShowCursor)
+ *s.buf = *nb
+ }
+
+ s.queuedText = false
+}
+
+// Close writes the final screen update and resets the screen.
+func (s *Screen) Close() (err error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.render()
+ s.updatePen(nil)
+ // Go to the bottom of the screen
+ s.move(0, s.newbuf.Height()-1)
+
+ if s.altScreenMode {
+ s.buf.WriteString(ansi.ResetAltScreenSaveCursorMode)
+ s.altScreenMode = false
+ }
+
+ if s.cursorHidden {
+ s.buf.WriteString(ansi.ShowCursor)
+ s.cursorHidden = false
+ }
+
+ // Write the buffer
+ err = s.flush()
+ if err != nil {
+ return
+ }
+
+ s.reset()
+ return
+}
+
+// reset resets the screen to its initial state.
+func (s *Screen) reset() {
+ s.scrollHeight = 0
+ s.cursorHidden = false
+ s.altScreenMode = false
+ s.touch = make(map[int]lineData, s.newbuf.Height())
+ if s.curbuf != nil {
+ s.curbuf.Clear()
+ }
+ if s.newbuf != nil {
+ s.newbuf.Clear()
+ }
+ s.buf.Reset()
+ s.tabs = DefaultTabStops(s.newbuf.Width())
+ s.oldhash, s.newhash = nil, nil
+
+ // We always disable HardTabs when termtype is "linux".
+ if strings.HasPrefix(s.opts.Term, "linux") {
+ s.opts.HardTabs = false
+ }
+}
+
+// Resize resizes the screen.
+func (s *Screen) Resize(width, height int) bool {
+ oldw := s.newbuf.Width()
+ oldh := s.newbuf.Height()
+
+ if s.opts.AltScreen || width != oldw {
+ // We only clear the whole screen if the width changes. Adding/removing
+ // rows is handled by the [Screen.render] and [Screen.transformLine]
+ // methods.
+ s.clear = true
+ }
+
+ // Clear new columns and lines
+ if width > oldh {
+ s.ClearRect(Rect(max(oldw-1, 0), 0, width-oldw, height))
+ } else if width < oldw {
+ s.ClearRect(Rect(max(width-1, 0), 0, oldw-width, height))
+ }
+
+ if height > oldh {
+ s.ClearRect(Rect(0, max(oldh-1, 0), width, height-oldh))
+ } else if height < oldh {
+ s.ClearRect(Rect(0, max(height-1, 0), width, oldh-height))
+ }
+
+ s.mu.Lock()
+ s.newbuf.Resize(width, height)
+ s.tabs.Resize(width)
+ s.oldhash, s.newhash = nil, nil
+ s.scrollHeight = 0 // reset scroll lines
+ s.mu.Unlock()
+
+ return true
+}
+
+// MoveTo moves the cursor to the given position.
+func (s *Screen) MoveTo(x, y int) {
+ s.mu.Lock()
+ s.move(x, y)
+ s.mu.Unlock()
+}
+
+// InsertAbove inserts string above the screen. The inserted string is not
+// managed by the screen. This does nothing when alternate screen mode is
+// enabled.
+func (s *Screen) InsertAbove(str string) {
+ if s.opts.AltScreen {
+ return
+ }
+ s.mu.Lock()
+ for _, line := range strings.Split(str, "\n") {
+ s.queueAbove = append(s.queueAbove, s.method.Truncate(line, s.Width(), ""))
+ }
+ s.mu.Unlock()
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/sequence.go b/vendor/github.com/charmbracelet/x/cellbuf/sequence.go
new file mode 100644
index 0000000..613eefe
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/sequence.go
@@ -0,0 +1,131 @@
+package cellbuf
+
+import (
+ "bytes"
+ "image/color"
+
+ "github.com/charmbracelet/x/ansi"
+)
+
+// ReadStyle reads a Select Graphic Rendition (SGR) escape sequences from a
+// list of parameters.
+func ReadStyle(params ansi.Params, pen *Style) {
+ if len(params) == 0 {
+ pen.Reset()
+ return
+ }
+
+ for i := 0; i < len(params); i++ {
+ param, hasMore, _ := params.Param(i, 0)
+ switch param {
+ case 0: // Reset
+ pen.Reset()
+ case 1: // Bold
+ pen.Bold(true)
+ case 2: // Dim/Faint
+ pen.Faint(true)
+ case 3: // Italic
+ pen.Italic(true)
+ case 4: // Underline
+ nextParam, _, ok := params.Param(i+1, 0)
+ if hasMore && ok { // Only accept subparameters i.e. separated by ":"
+ switch nextParam {
+ case 0, 1, 2, 3, 4, 5:
+ i++
+ switch nextParam {
+ case 0: // No Underline
+ pen.UnderlineStyle(NoUnderline)
+ case 1: // Single Underline
+ pen.UnderlineStyle(SingleUnderline)
+ case 2: // Double Underline
+ pen.UnderlineStyle(DoubleUnderline)
+ case 3: // Curly Underline
+ pen.UnderlineStyle(CurlyUnderline)
+ case 4: // Dotted Underline
+ pen.UnderlineStyle(DottedUnderline)
+ case 5: // Dashed Underline
+ pen.UnderlineStyle(DashedUnderline)
+ }
+ }
+ } else {
+ // Single Underline
+ pen.Underline(true)
+ }
+ case 5: // Slow Blink
+ pen.SlowBlink(true)
+ case 6: // Rapid Blink
+ pen.RapidBlink(true)
+ case 7: // Reverse
+ pen.Reverse(true)
+ case 8: // Conceal
+ pen.Conceal(true)
+ case 9: // Crossed-out/Strikethrough
+ pen.Strikethrough(true)
+ case 22: // Normal Intensity (not bold or faint)
+ pen.Bold(false).Faint(false)
+ case 23: // Not italic, not Fraktur
+ pen.Italic(false)
+ case 24: // Not underlined
+ pen.Underline(false)
+ case 25: // Blink off
+ pen.SlowBlink(false).RapidBlink(false)
+ case 27: // Positive (not reverse)
+ pen.Reverse(false)
+ case 28: // Reveal
+ pen.Conceal(false)
+ case 29: // Not crossed out
+ pen.Strikethrough(false)
+ case 30, 31, 32, 33, 34, 35, 36, 37: // Set foreground
+ pen.Foreground(ansi.Black + ansi.BasicColor(param-30)) //nolint:gosec
+ case 38: // Set foreground 256 or truecolor
+ var c color.Color
+ n := ReadStyleColor(params[i:], &c)
+ if n > 0 {
+ pen.Foreground(c)
+ i += n - 1
+ }
+ case 39: // Default foreground
+ pen.Foreground(nil)
+ case 40, 41, 42, 43, 44, 45, 46, 47: // Set background
+ pen.Background(ansi.Black + ansi.BasicColor(param-40)) //nolint:gosec
+ case 48: // Set background 256 or truecolor
+ var c color.Color
+ n := ReadStyleColor(params[i:], &c)
+ if n > 0 {
+ pen.Background(c)
+ i += n - 1
+ }
+ case 49: // Default Background
+ pen.Background(nil)
+ case 58: // Set underline color
+ var c color.Color
+ n := ReadStyleColor(params[i:], &c)
+ if n > 0 {
+ pen.UnderlineColor(c)
+ i += n - 1
+ }
+ case 59: // Default underline color
+ pen.UnderlineColor(nil)
+ case 90, 91, 92, 93, 94, 95, 96, 97: // Set bright foreground
+ pen.Foreground(ansi.BrightBlack + ansi.BasicColor(param-90)) //nolint:gosec
+ case 100, 101, 102, 103, 104, 105, 106, 107: // Set bright background
+ pen.Background(ansi.BrightBlack + ansi.BasicColor(param-100)) //nolint:gosec
+ }
+ }
+}
+
+// ReadLink reads a hyperlink escape sequence from a data buffer.
+func ReadLink(p []byte, link *Link) {
+ params := bytes.Split(p, []byte{';'})
+ if len(params) != 3 {
+ return
+ }
+ link.Params = string(params[1])
+ link.URL = string(params[2])
+}
+
+// ReadStyleColor reads a color from a list of parameters.
+// See [ansi.ReadStyleColor] for more information.
+func ReadStyleColor(params ansi.Params, c *color.Color) int {
+ return ansi.ReadStyleColor(params, c)
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/style.go b/vendor/github.com/charmbracelet/x/cellbuf/style.go
new file mode 100644
index 0000000..82c4afb
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/style.go
@@ -0,0 +1,31 @@
+package cellbuf
+
+import (
+ "github.com/charmbracelet/colorprofile"
+)
+
+// Convert converts a style to respect the given color profile.
+func ConvertStyle(s Style, p colorprofile.Profile) Style {
+ switch p {
+ case colorprofile.TrueColor:
+ return s
+ case colorprofile.Ascii:
+ s.Fg = nil
+ s.Bg = nil
+ s.Ul = nil
+ case colorprofile.NoTTY:
+ return Style{}
+ }
+
+ if s.Fg != nil {
+ s.Fg = p.Convert(s.Fg)
+ }
+ if s.Bg != nil {
+ s.Bg = p.Convert(s.Bg)
+ }
+ if s.Ul != nil {
+ s.Ul = p.Convert(s.Ul)
+ }
+
+ return s
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/tabstop.go b/vendor/github.com/charmbracelet/x/cellbuf/tabstop.go
new file mode 100644
index 0000000..24eec44
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/tabstop.go
@@ -0,0 +1,137 @@
+package cellbuf
+
+// DefaultTabInterval is the default tab interval.
+const DefaultTabInterval = 8
+
+// TabStops represents horizontal line tab stops.
+type TabStops struct {
+ stops []int
+ interval int
+ width int
+}
+
+// NewTabStops creates a new set of tab stops from a number of columns and an
+// interval.
+func NewTabStops(width, interval int) *TabStops {
+ ts := new(TabStops)
+ ts.interval = interval
+ ts.width = width
+ ts.stops = make([]int, (width+(interval-1))/interval)
+ ts.init(0, width)
+ return ts
+}
+
+// DefaultTabStops creates a new set of tab stops with the default interval.
+func DefaultTabStops(cols int) *TabStops {
+ return NewTabStops(cols, DefaultTabInterval)
+}
+
+// Resize resizes the tab stops to the given width.
+func (ts *TabStops) Resize(width int) {
+ if width == ts.width {
+ return
+ }
+
+ if width < ts.width {
+ size := (width + (ts.interval - 1)) / ts.interval
+ ts.stops = ts.stops[:size]
+ } else {
+ size := (width - ts.width + (ts.interval - 1)) / ts.interval
+ ts.stops = append(ts.stops, make([]int, size)...)
+ }
+
+ ts.init(ts.width, width)
+ ts.width = width
+}
+
+// IsStop returns true if the given column is a tab stop.
+func (ts TabStops) IsStop(col int) bool {
+ mask := ts.mask(col)
+ i := col >> 3
+ if i < 0 || i >= len(ts.stops) {
+ return false
+ }
+ return ts.stops[i]&mask != 0
+}
+
+// Next returns the next tab stop after the given column.
+func (ts TabStops) Next(col int) int {
+ return ts.Find(col, 1)
+}
+
+// Prev returns the previous tab stop before the given column.
+func (ts TabStops) Prev(col int) int {
+ return ts.Find(col, -1)
+}
+
+// Find returns the prev/next tab stop before/after the given column and delta.
+// If delta is positive, it returns the next tab stop after the given column.
+// If delta is negative, it returns the previous tab stop before the given column.
+// If delta is zero, it returns the given column.
+func (ts TabStops) Find(col, delta int) int {
+ if delta == 0 {
+ return col
+ }
+
+ var prev bool
+ count := delta
+ if count < 0 {
+ count = -count
+ prev = true
+ }
+
+ for count > 0 {
+ if !prev {
+ if col >= ts.width-1 {
+ return col
+ }
+
+ col++
+ } else {
+ if col < 1 {
+ return col
+ }
+
+ col--
+ }
+
+ if ts.IsStop(col) {
+ count--
+ }
+ }
+
+ return col
+}
+
+// Set adds a tab stop at the given column.
+func (ts *TabStops) Set(col int) {
+ mask := ts.mask(col)
+ ts.stops[col>>3] |= mask
+}
+
+// Reset removes the tab stop at the given column.
+func (ts *TabStops) Reset(col int) {
+ mask := ts.mask(col)
+ ts.stops[col>>3] &= ^mask
+}
+
+// Clear removes all tab stops.
+func (ts *TabStops) Clear() {
+ ts.stops = make([]int, len(ts.stops))
+}
+
+// mask returns the mask for the given column.
+func (ts *TabStops) mask(col int) int {
+ return 1 << (col & (ts.interval - 1))
+}
+
+// init initializes the tab stops starting from col until width.
+func (ts *TabStops) init(col, width int) {
+ for x := col; x < width; x++ {
+ if x%ts.interval == 0 {
+ ts.Set(x)
+ } else {
+ ts.Reset(x)
+ }
+ }
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/utils.go b/vendor/github.com/charmbracelet/x/cellbuf/utils.go
new file mode 100644
index 0000000..b0452fa
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/utils.go
@@ -0,0 +1,38 @@
+package cellbuf
+
+import (
+ "strings"
+)
+
+// Height returns the height of a string.
+func Height(s string) int {
+ return strings.Count(s, "\n") + 1
+}
+
+func min(a, b int) int { //nolint:predeclared
+ if a > b {
+ return b
+ }
+ return a
+}
+
+func max(a, b int) int { //nolint:predeclared
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func clamp(v, low, high int) int {
+ if high < low {
+ low, high = high, low
+ }
+ return min(high, max(low, v))
+}
+
+func abs(a int) int {
+ if a < 0 {
+ return -a
+ }
+ return a
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/wrap.go b/vendor/github.com/charmbracelet/x/cellbuf/wrap.go
new file mode 100644
index 0000000..59a2a33
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/wrap.go
@@ -0,0 +1,178 @@
+package cellbuf
+
+import (
+ "bytes"
+ "unicode"
+ "unicode/utf8"
+
+ "github.com/charmbracelet/x/ansi"
+)
+
+// Wrap returns a string that is wrapped to the specified limit applying any
+// ANSI escape sequences in the string. It tries to wrap the string at word
+// boundaries, but will break words if necessary.
+//
+// The breakpoints string is a list of characters that are considered
+// breakpoints for word wrapping. A hyphen (-) is always considered a
+// breakpoint.
+//
+// Note: breakpoints must be a string of 1-cell wide rune characters.
+func Wrap(s string, limit int, breakpoints string) string {
+ if len(s) == 0 {
+ return ""
+ }
+
+ if limit < 1 {
+ return s
+ }
+
+ p := ansi.GetParser()
+ defer ansi.PutParser(p)
+
+ var (
+ buf bytes.Buffer
+ word bytes.Buffer
+ space bytes.Buffer
+ style, curStyle Style
+ link, curLink Link
+ curWidth int
+ wordLen int
+ )
+
+ addSpace := func() {
+ curWidth += space.Len()
+ buf.Write(space.Bytes())
+ space.Reset()
+ }
+
+ addWord := func() {
+ if word.Len() == 0 {
+ return
+ }
+
+ curLink = link
+ curStyle = style
+
+ addSpace()
+ curWidth += wordLen
+ buf.Write(word.Bytes())
+ word.Reset()
+ wordLen = 0
+ }
+
+ addNewline := func() {
+ if !curStyle.Empty() {
+ buf.WriteString(ansi.ResetStyle)
+ }
+ if !curLink.Empty() {
+ buf.WriteString(ansi.ResetHyperlink())
+ }
+ buf.WriteByte('\n')
+ if !curLink.Empty() {
+ buf.WriteString(ansi.SetHyperlink(curLink.URL, curLink.Params))
+ }
+ if !curStyle.Empty() {
+ buf.WriteString(curStyle.Sequence())
+ }
+ curWidth = 0
+ space.Reset()
+ }
+
+ var state byte
+ for len(s) > 0 {
+ seq, width, n, newState := ansi.DecodeSequence(s, state, p)
+ switch width {
+ case 0:
+ if ansi.Equal(seq, "\t") {
+ addWord()
+ space.WriteString(seq)
+ break
+ } else if ansi.Equal(seq, "\n") {
+ if wordLen == 0 {
+ if curWidth+space.Len() > limit {
+ curWidth = 0
+ } else {
+ // preserve whitespaces
+ buf.Write(space.Bytes())
+ }
+ space.Reset()
+ }
+
+ addWord()
+ addNewline()
+ break
+ } else if ansi.HasCsiPrefix(seq) && p.Command() == 'm' {
+ // SGR style sequence [ansi.SGR]
+ ReadStyle(p.Params(), &style)
+ } else if ansi.HasOscPrefix(seq) && p.Command() == 8 {
+ // Hyperlink sequence [ansi.SetHyperlink]
+ ReadLink(p.Data(), &link)
+ }
+
+ word.WriteString(seq)
+ default:
+ if len(seq) == 1 {
+ // ASCII
+ r, _ := utf8.DecodeRuneInString(seq)
+ if unicode.IsSpace(r) {
+ addWord()
+ space.WriteRune(r)
+ break
+ } else if r == '-' || runeContainsAny(r, breakpoints) {
+ addSpace()
+ if curWidth+wordLen+width <= limit {
+ addWord()
+ buf.WriteString(seq)
+ curWidth += width
+ break
+ }
+ }
+ }
+
+ if wordLen+width > limit {
+ // Hardwrap the word if it's too long
+ addWord()
+ }
+
+ word.WriteString(seq)
+ wordLen += width
+
+ if curWidth+wordLen+space.Len() > limit {
+ addNewline()
+ }
+ }
+
+ s = s[n:]
+ state = newState
+ }
+
+ if wordLen == 0 {
+ if curWidth+space.Len() > limit {
+ curWidth = 0
+ } else {
+ // preserve whitespaces
+ buf.Write(space.Bytes())
+ }
+ space.Reset()
+ }
+
+ addWord()
+
+ if !curLink.Empty() {
+ buf.WriteString(ansi.ResetHyperlink())
+ }
+ if !curStyle.Empty() {
+ buf.WriteString(ansi.ResetStyle)
+ }
+
+ return buf.String()
+}
+
+func runeContainsAny[T string | []rune](r rune, s T) bool {
+ for _, c := range []rune(s) {
+ if c == r {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/charmbracelet/x/cellbuf/writer.go b/vendor/github.com/charmbracelet/x/cellbuf/writer.go
new file mode 100644
index 0000000..ae8b2a8
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/cellbuf/writer.go
@@ -0,0 +1,339 @@
+package cellbuf
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+
+ "github.com/charmbracelet/x/ansi"
+)
+
+// CellBuffer is a cell buffer that represents a set of cells in a screen or a
+// grid.
+type CellBuffer interface {
+ // Cell returns the cell at the given position.
+ Cell(x, y int) *Cell
+ // SetCell sets the cell at the given position to the given cell. It
+ // returns whether the cell was set successfully.
+ SetCell(x, y int, c *Cell) bool
+ // Bounds returns the bounds of the cell buffer.
+ Bounds() Rectangle
+}
+
+// FillRect fills the rectangle within the cell buffer with the given cell.
+// This will not fill cells outside the bounds of the cell buffer.
+func FillRect(s CellBuffer, c *Cell, rect Rectangle) {
+ for y := rect.Min.Y; y < rect.Max.Y; y++ {
+ for x := rect.Min.X; x < rect.Max.X; x++ {
+ s.SetCell(x, y, c) //nolint:errcheck
+ }
+ }
+}
+
+// Fill fills the cell buffer with the given cell.
+func Fill(s CellBuffer, c *Cell) {
+ FillRect(s, c, s.Bounds())
+}
+
+// ClearRect clears the rectangle within the cell buffer with blank cells.
+func ClearRect(s CellBuffer, rect Rectangle) {
+ FillRect(s, nil, rect)
+}
+
+// Clear clears the cell buffer with blank cells.
+func Clear(s CellBuffer) {
+ Fill(s, nil)
+}
+
+// SetContentRect clears the rectangle within the cell buffer with blank cells,
+// and sets the given string as its content. If the height or width of the
+// string exceeds the height or width of the cell buffer, it will be truncated.
+func SetContentRect(s CellBuffer, str string, rect Rectangle) {
+ // Replace all "\n" with "\r\n" to ensure the cursor is reset to the start
+ // of the line. Make sure we don't replace "\r\n" with "\r\r\n".
+ str = strings.ReplaceAll(str, "\r\n", "\n")
+ str = strings.ReplaceAll(str, "\n", "\r\n")
+ ClearRect(s, rect)
+ printString(s, ansi.GraphemeWidth, rect.Min.X, rect.Min.Y, rect, str, true, "")
+}
+
+// SetContent clears the cell buffer with blank cells, and sets the given string
+// as its content. If the height or width of the string exceeds the height or
+// width of the cell buffer, it will be truncated.
+func SetContent(s CellBuffer, str string) {
+ SetContentRect(s, str, s.Bounds())
+}
+
+// Render returns a string representation of the grid with ANSI escape sequences.
+func Render(d CellBuffer) string {
+ var buf bytes.Buffer
+ height := d.Bounds().Dy()
+ for y := 0; y < height; y++ {
+ _, line := RenderLine(d, y)
+ buf.WriteString(line)
+ if y < height-1 {
+ buf.WriteString("\r\n")
+ }
+ }
+ return buf.String()
+}
+
+// RenderLine returns a string representation of the yth line of the grid along
+// with the width of the line.
+func RenderLine(d CellBuffer, n int) (w int, line string) {
+ var pen Style
+ var link Link
+ var buf bytes.Buffer
+ var pendingLine string
+ var pendingWidth int // this ignores space cells until we hit a non-space cell
+
+ writePending := func() {
+ // If there's no pending line, we don't need to do anything.
+ if len(pendingLine) == 0 {
+ return
+ }
+ buf.WriteString(pendingLine)
+ w += pendingWidth
+ pendingWidth = 0
+ pendingLine = ""
+ }
+
+ for x := 0; x < d.Bounds().Dx(); x++ {
+ if cell := d.Cell(x, n); cell != nil && cell.Width > 0 {
+ // Convert the cell's style and link to the given color profile.
+ cellStyle := cell.Style
+ cellLink := cell.Link
+ if cellStyle.Empty() && !pen.Empty() {
+ writePending()
+ buf.WriteString(ansi.ResetStyle) //nolint:errcheck
+ pen.Reset()
+ }
+ if !cellStyle.Equal(&pen) {
+ writePending()
+ seq := cellStyle.DiffSequence(pen)
+ buf.WriteString(seq) // nolint:errcheck
+ pen = cellStyle
+ }
+
+ // Write the URL escape sequence
+ if cellLink != link && link.URL != "" {
+ writePending()
+ buf.WriteString(ansi.ResetHyperlink()) //nolint:errcheck
+ link.Reset()
+ }
+ if cellLink != link {
+ writePending()
+ buf.WriteString(ansi.SetHyperlink(cellLink.URL, cellLink.Params)) //nolint:errcheck
+ link = cellLink
+ }
+
+ // We only write the cell content if it's not empty. If it is, we
+ // append it to the pending line and width to be evaluated later.
+ if cell.Equal(&BlankCell) {
+ pendingLine += cell.String()
+ pendingWidth += cell.Width
+ } else {
+ writePending()
+ buf.WriteString(cell.String())
+ w += cell.Width
+ }
+ }
+ }
+ if link.URL != "" {
+ buf.WriteString(ansi.ResetHyperlink()) //nolint:errcheck
+ }
+ if !pen.Empty() {
+ buf.WriteString(ansi.ResetStyle) //nolint:errcheck
+ }
+ return w, strings.TrimRight(buf.String(), " ") // Trim trailing spaces
+}
+
+// ScreenWriter represents a writer that writes to a [Screen] parsing ANSI
+// escape sequences and Unicode characters and converting them into cells that
+// can be written to a cell [Buffer].
+type ScreenWriter struct {
+ *Screen
+}
+
+// NewScreenWriter creates a new ScreenWriter that writes to the given Screen.
+// This is a convenience function for creating a ScreenWriter.
+func NewScreenWriter(s *Screen) *ScreenWriter {
+ return &ScreenWriter{s}
+}
+
+// Write writes the given bytes to the screen.
+// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
+// sequences.
+func (s *ScreenWriter) Write(p []byte) (n int, err error) {
+ printString(s.Screen, s.method,
+ s.cur.X, s.cur.Y, s.Bounds(),
+ p, false, "")
+ return len(p), nil
+}
+
+// SetContent clears the screen with blank cells, and sets the given string as
+// its content. If the height or width of the string exceeds the height or
+// width of the screen, it will be truncated.
+//
+// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape sequences.
+func (s *ScreenWriter) SetContent(str string) {
+ s.SetContentRect(str, s.Bounds())
+}
+
+// SetContentRect clears the rectangle within the screen with blank cells, and
+// sets the given string as its content. If the height or width of the string
+// exceeds the height or width of the screen, it will be truncated.
+//
+// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
+// sequences.
+func (s *ScreenWriter) SetContentRect(str string, rect Rectangle) {
+ // Replace all "\n" with "\r\n" to ensure the cursor is reset to the start
+ // of the line. Make sure we don't replace "\r\n" with "\r\r\n".
+ str = strings.ReplaceAll(str, "\r\n", "\n")
+ str = strings.ReplaceAll(str, "\n", "\r\n")
+ s.ClearRect(rect)
+ printString(s.Screen, s.method,
+ rect.Min.X, rect.Min.Y, rect,
+ str, true, "")
+}
+
+// Print prints the string at the current cursor position. It will wrap the
+// string to the width of the screen if it exceeds the width of the screen.
+// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
+// sequences.
+func (s *ScreenWriter) Print(str string, v ...interface{}) {
+ if len(v) > 0 {
+ str = fmt.Sprintf(str, v...)
+ }
+ printString(s.Screen, s.method,
+ s.cur.X, s.cur.Y, s.Bounds(),
+ str, false, "")
+}
+
+// PrintAt prints the string at the given position. It will wrap the string to
+// the width of the screen if it exceeds the width of the screen.
+// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
+// sequences.
+func (s *ScreenWriter) PrintAt(x, y int, str string, v ...interface{}) {
+ if len(v) > 0 {
+ str = fmt.Sprintf(str, v...)
+ }
+ printString(s.Screen, s.method,
+ x, y, s.Bounds(),
+ str, false, "")
+}
+
+// PrintCrop prints the string at the current cursor position and truncates the
+// text if it exceeds the width of the screen. Use tail to specify a string to
+// append if the string is truncated.
+// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
+// sequences.
+func (s *ScreenWriter) PrintCrop(str string, tail string) {
+ printString(s.Screen, s.method,
+ s.cur.X, s.cur.Y, s.Bounds(),
+ str, true, tail)
+}
+
+// PrintCropAt prints the string at the given position and truncates the text
+// if it exceeds the width of the screen. Use tail to specify a string to append
+// if the string is truncated.
+// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
+// sequences.
+func (s *ScreenWriter) PrintCropAt(x, y int, str string, tail string) {
+ printString(s.Screen, s.method,
+ x, y, s.Bounds(),
+ str, true, tail)
+}
+
+// printString draws a string starting at the given position.
+func printString[T []byte | string](
+ s CellBuffer,
+ m ansi.Method,
+ x, y int,
+ bounds Rectangle, str T,
+ truncate bool, tail string,
+) {
+ p := ansi.GetParser()
+ defer ansi.PutParser(p)
+
+ var tailc Cell
+ if truncate && len(tail) > 0 {
+ if m == ansi.WcWidth {
+ tailc = *NewCellString(tail)
+ } else {
+ tailc = *NewGraphemeCell(tail)
+ }
+ }
+
+ decoder := ansi.DecodeSequenceWc[T]
+ if m == ansi.GraphemeWidth {
+ decoder = ansi.DecodeSequence[T]
+ }
+
+ var cell Cell
+ var style Style
+ var link Link
+ var state byte
+ for len(str) > 0 {
+ seq, width, n, newState := decoder(str, state, p)
+
+ switch width {
+ case 1, 2, 3, 4: // wide cells can go up to 4 cells wide
+ cell.Width += width
+ cell.Append([]rune(string(seq))...)
+
+ if !truncate && x+cell.Width > bounds.Max.X && y+1 < bounds.Max.Y {
+ // Wrap the string to the width of the window
+ x = bounds.Min.X
+ y++
+ }
+ if Pos(x, y).In(bounds) {
+ if truncate && tailc.Width > 0 && x+cell.Width > bounds.Max.X-tailc.Width {
+ // Truncate the string and append the tail if any.
+ cell := tailc
+ cell.Style = style
+ cell.Link = link
+ s.SetCell(x, y, &cell)
+ x += tailc.Width
+ } else {
+ // Print the cell to the screen
+ cell.Style = style
+ cell.Link = link
+ s.SetCell(x, y, &cell) //nolint:errcheck
+ x += width
+ }
+ }
+
+ // String is too long for the line, truncate it.
+ // Make sure we reset the cell for the next iteration.
+ cell.Reset()
+ default:
+ // Valid sequences always have a non-zero Cmd.
+ // TODO: Handle cursor movement and other sequences
+ switch {
+ case ansi.HasCsiPrefix(seq) && p.Command() == 'm':
+ // SGR - Select Graphic Rendition
+ ReadStyle(p.Params(), &style)
+ case ansi.HasOscPrefix(seq) && p.Command() == 8:
+ // Hyperlinks
+ ReadLink(p.Data(), &link)
+ case ansi.Equal(seq, T("\n")):
+ y++
+ case ansi.Equal(seq, T("\r")):
+ x = bounds.Min.X
+ default:
+ cell.Append([]rune(string(seq))...)
+ }
+ }
+
+ // Advance the state and data
+ state = newState
+ str = str[n:]
+ }
+
+ // Make sure to set the last cell if it's not empty.
+ if !cell.Empty() {
+ s.SetCell(x, y, &cell) //nolint:errcheck
+ cell.Reset()
+ }
+}
diff --git a/vendor/github.com/charmbracelet/x/term/LICENSE b/vendor/github.com/charmbracelet/x/term/LICENSE
new file mode 100644
index 0000000..65a5654
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/term/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 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.
diff --git a/vendor/github.com/charmbracelet/x/term/term.go b/vendor/github.com/charmbracelet/x/term/term.go
new file mode 100644
index 0000000..58d6522
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/term/term.go
@@ -0,0 +1,49 @@
+package term
+
+// State contains platform-specific state of a terminal.
+type State struct {
+ state
+}
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd uintptr) bool {
+ return isTerminal(fd)
+}
+
+// MakeRaw puts the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd uintptr) (*State, error) {
+ return makeRaw(fd)
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd uintptr) (*State, error) {
+ return getState(fd)
+}
+
+// SetState sets the given state of the terminal.
+func SetState(fd uintptr, state *State) error {
+ return setState(fd, state)
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd uintptr, oldState *State) error {
+ return restore(fd, oldState)
+}
+
+// GetSize returns the visible dimensions of the given terminal.
+//
+// These dimensions don't include any scrollback buffer height.
+func GetSize(fd uintptr) (width, height int, err error) {
+ return getSize(fd)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd uintptr) ([]byte, error) {
+ return readPassword(fd)
+}
diff --git a/vendor/github.com/charmbracelet/x/term/term_other.go b/vendor/github.com/charmbracelet/x/term/term_other.go
new file mode 100644
index 0000000..092c7e9
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/term/term_other.go
@@ -0,0 +1,39 @@
+//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !zos && !windows && !solaris && !plan9
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!zos,!windows,!solaris,!plan9
+
+package term
+
+import (
+ "fmt"
+ "runtime"
+)
+
+type state struct{}
+
+func isTerminal(fd uintptr) bool {
+ return false
+}
+
+func makeRaw(fd uintptr) (*State, error) {
+ return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func getState(fd uintptr) (*State, error) {
+ return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func restore(fd uintptr, state *State) error {
+ return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func getSize(fd uintptr) (width, height int, err error) {
+ return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func setState(fd uintptr, state *State) error {
+ return fmt.Errorf("terminal: SetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+func readPassword(fd uintptr) ([]byte, error) {
+ return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
diff --git a/vendor/github.com/charmbracelet/x/term/term_unix.go b/vendor/github.com/charmbracelet/x/term/term_unix.go
new file mode 100644
index 0000000..1459cb1
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/term/term_unix.go
@@ -0,0 +1,96 @@
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
+
+package term
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+type state struct {
+ unix.Termios
+}
+
+func isTerminal(fd uintptr) bool {
+ _, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
+ return err == nil
+}
+
+func makeRaw(fd uintptr) (*State, error) {
+ termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
+ if err != nil {
+ return nil, err
+ }
+
+ oldState := State{state{Termios: *termios}}
+
+ // This attempts to replicate the behaviour documented for cfmakeraw in
+ // the termios(3) manpage.
+ termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
+ termios.Oflag &^= unix.OPOST
+ termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
+ termios.Cflag &^= unix.CSIZE | unix.PARENB
+ termios.Cflag |= unix.CS8
+ termios.Cc[unix.VMIN] = 1
+ termios.Cc[unix.VTIME] = 0
+ if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios); err != nil {
+ return nil, err
+ }
+
+ return &oldState, nil
+}
+
+func setState(fd uintptr, state *State) error {
+ var termios *unix.Termios
+ if state != nil {
+ termios = &state.Termios
+ }
+ return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
+}
+
+func getState(fd uintptr) (*State, error) {
+ termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
+ if err != nil {
+ return nil, err
+ }
+
+ return &State{state{Termios: *termios}}, nil
+}
+
+func restore(fd uintptr, state *State) error {
+ return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.Termios)
+}
+
+func getSize(fd uintptr) (width, height int, err error) {
+ ws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
+ if err != nil {
+ return 0, 0, err
+ }
+ return int(ws.Col), int(ws.Row), nil
+}
+
+// passwordReader is an io.Reader that reads from a specific file descriptor.
+type passwordReader int
+
+func (r passwordReader) Read(buf []byte) (int, error) {
+ return unix.Read(int(r), buf)
+}
+
+func readPassword(fd uintptr) ([]byte, error) {
+ termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
+ if err != nil {
+ return nil, err
+ }
+
+ newState := *termios
+ newState.Lflag &^= unix.ECHO
+ newState.Lflag |= unix.ICANON | unix.ISIG
+ newState.Iflag |= unix.ICRNL
+ if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &newState); err != nil {
+ return nil, err
+ }
+
+ defer unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
+
+ return readPasswordLine(passwordReader(fd))
+}
diff --git a/vendor/github.com/charmbracelet/x/term/term_unix_bsd.go b/vendor/github.com/charmbracelet/x/term/term_unix_bsd.go
new file mode 100644
index 0000000..b435031
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/term/term_unix_bsd.go
@@ -0,0 +1,11 @@
+//go:build darwin || dragonfly || freebsd || netbsd || openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package term
+
+import "golang.org/x/sys/unix"
+
+const (
+ ioctlReadTermios = unix.TIOCGETA
+ ioctlWriteTermios = unix.TIOCSETA
+)
diff --git a/vendor/github.com/charmbracelet/x/term/term_unix_other.go b/vendor/github.com/charmbracelet/x/term/term_unix_other.go
new file mode 100644
index 0000000..ee2a29e
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/term/term_unix_other.go
@@ -0,0 +1,11 @@
+//go:build aix || linux || solaris || zos
+// +build aix linux solaris zos
+
+package term
+
+import "golang.org/x/sys/unix"
+
+const (
+ ioctlReadTermios = unix.TCGETS
+ ioctlWriteTermios = unix.TCSETS
+)
diff --git a/vendor/github.com/charmbracelet/x/term/term_windows.go b/vendor/github.com/charmbracelet/x/term/term_windows.go
new file mode 100644
index 0000000..fe7afde
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/term/term_windows.go
@@ -0,0 +1,87 @@
+//go:build windows
+// +build windows
+
+package term
+
+import (
+ "os"
+
+ "golang.org/x/sys/windows"
+)
+
+type state struct {
+ Mode uint32
+}
+
+func isTerminal(fd uintptr) bool {
+ var st uint32
+ err := windows.GetConsoleMode(windows.Handle(fd), &st)
+ return err == nil
+}
+
+func makeRaw(fd uintptr) (*State, error) {
+ var st uint32
+ if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+ return nil, err
+ }
+ raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
+ raw |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
+ if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
+ return nil, err
+ }
+ return &State{state{st}}, nil
+}
+
+func setState(fd uintptr, state *State) error {
+ var mode uint32
+ if state != nil {
+ mode = state.Mode
+ }
+ return windows.SetConsoleMode(windows.Handle(fd), mode)
+}
+
+func getState(fd uintptr) (*State, error) {
+ var st uint32
+ if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+ return nil, err
+ }
+ return &State{state{st}}, nil
+}
+
+func restore(fd uintptr, state *State) error {
+ return windows.SetConsoleMode(windows.Handle(fd), state.Mode)
+}
+
+func getSize(fd uintptr) (width, height int, err error) {
+ var info windows.ConsoleScreenBufferInfo
+ if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
+ return 0, 0, err
+ }
+ return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1), nil
+}
+
+func readPassword(fd uintptr) ([]byte, error) {
+ var st uint32
+ if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+ return nil, err
+ }
+ old := st
+
+ st &^= (windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT)
+ st |= (windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_PROCESSED_INPUT)
+ if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
+ return nil, err
+ }
+
+ defer windows.SetConsoleMode(windows.Handle(fd), old)
+
+ var h windows.Handle
+ p, _ := windows.GetCurrentProcess()
+ if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
+ return nil, err
+ }
+
+ f := os.NewFile(uintptr(h), "stdin")
+ defer f.Close()
+ return readPasswordLine(f)
+}
diff --git a/vendor/github.com/charmbracelet/x/term/terminal.go b/vendor/github.com/charmbracelet/x/term/terminal.go
new file mode 100644
index 0000000..8963163
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/term/terminal.go
@@ -0,0 +1,12 @@
+package term
+
+import (
+ "io"
+)
+
+// File represents a file that has a file descriptor and can be read from,
+// written to, and closed.
+type File interface {
+ io.ReadWriteCloser
+ Fd() uintptr
+}
diff --git a/vendor/github.com/charmbracelet/x/term/util.go b/vendor/github.com/charmbracelet/x/term/util.go
new file mode 100644
index 0000000..b731341
--- /dev/null
+++ b/vendor/github.com/charmbracelet/x/term/util.go
@@ -0,0 +1,47 @@
+package term
+
+import (
+ "io"
+ "runtime"
+)
+
+// readPasswordLine reads from reader until it finds \n or io.EOF.
+// The slice returned does not include the \n.
+// readPasswordLine also ignores any \r it finds.
+// Windows uses \r as end of line. So, on Windows, readPasswordLine
+// reads until it finds \r and ignores any \n it finds during processing.
+func readPasswordLine(reader io.Reader) ([]byte, error) {
+ var buf [1]byte
+ var ret []byte
+
+ for {
+ n, err := reader.Read(buf[:])
+ if n > 0 {
+ switch buf[0] {
+ case '\b':
+ if len(ret) > 0 {
+ ret = ret[:len(ret)-1]
+ }
+ case '\n':
+ if runtime.GOOS != "windows" {
+ return ret, nil
+ }
+ // otherwise ignore \n
+ case '\r':
+ if runtime.GOOS == "windows" {
+ return ret, nil
+ }
+ // otherwise ignore \r
+ default:
+ ret = append(ret, buf[0])
+ }
+ continue
+ }
+ if err != nil {
+ if err == io.EOF && len(ret) > 0 {
+ return ret, nil
+ }
+ return ret, err
+ }
+ }
+}