diff --git a/go.mod b/go.mod index ee81b55b..8af87fcc 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c github.com/AlecAivazis/survey/v2 v2.3.7 - github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/lipgloss v1.1.0 github.com/charmbracelet/log v0.4.2 @@ -16,7 +15,6 @@ require ( github.com/docker/cli v28.4.0+incompatible github.com/docker/docker v28.4.0+incompatible github.com/docker/go-units v0.5.0 - github.com/evertras/bubble-table v0.19.2 github.com/go-git/go-git/v5 v5.16.2 github.com/google/go-cmp v0.7.0 github.com/leonelquinteros/gotext v1.7.2 @@ -35,7 +33,6 @@ require ( github.com/BurntSushi/toml v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect - github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -44,6 +41,7 @@ require ( github.com/charmbracelet/colorprofile v0.3.2 // indirect github.com/charmbracelet/x/ansi v0.10.2 // indirect github.com/charmbracelet/x/cellbuf v0.0.13 // indirect + github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect @@ -94,7 +92,6 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect diff --git a/go.sum b/go.sum index 6e15adf5..13d10638 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,6 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= @@ -135,8 +133,6 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= -github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI= @@ -373,8 +369,6 @@ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evertras/bubble-table v0.19.2 h1:u77oiM6JlRR+CvS5FZc3Hz+J6iEsvEDcR5kO8OFb1Yw= -github.com/evertras/bubble-table v0.19.2/go.mod h1:ifHujS1YxwnYSOgcR2+m3GnJ84f7CVU/4kUOxUCjEbQ= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= @@ -640,7 +634,6 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= @@ -702,8 +695,6 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -818,8 +809,6 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= diff --git a/vendor/github.com/atotto/clipboard/.travis.yml b/vendor/github.com/atotto/clipboard/.travis.yml deleted file mode 100644 index 23f21d83..00000000 --- a/vendor/github.com/atotto/clipboard/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: go - -os: - - linux - - osx - - windows - -go: - - go1.13.x - - go1.x - -services: - - xvfb - -before_install: - - export DISPLAY=:99.0 - -script: - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install xsel; fi - - go test -v . - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install xclip; fi - - go test -v . diff --git a/vendor/github.com/atotto/clipboard/LICENSE b/vendor/github.com/atotto/clipboard/LICENSE deleted file mode 100644 index dee3257b..00000000 --- a/vendor/github.com/atotto/clipboard/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2013 Ato Araki. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of @atotto. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/atotto/clipboard/README.md b/vendor/github.com/atotto/clipboard/README.md deleted file mode 100644 index 41fdd57b..00000000 --- a/vendor/github.com/atotto/clipboard/README.md +++ /dev/null @@ -1,48 +0,0 @@ -[![Build Status](https://travis-ci.org/atotto/clipboard.svg?branch=master)](https://travis-ci.org/atotto/clipboard) - -[![GoDoc](https://godoc.org/github.com/atotto/clipboard?status.svg)](http://godoc.org/github.com/atotto/clipboard) - -# Clipboard for Go - -Provide copying and pasting to the Clipboard for Go. - -Build: - - $ go get github.com/atotto/clipboard - -Platforms: - -* OSX -* Windows 7 (probably work on other Windows) -* Linux, Unix (requires 'xclip' or 'xsel' command to be installed) - - -Document: - -* http://godoc.org/github.com/atotto/clipboard - -Notes: - -* Text string only -* UTF-8 text encoding only (no conversion) - -TODO: - -* Clipboard watcher(?) - -## Commands: - -paste shell command: - - $ go get github.com/atotto/clipboard/cmd/gopaste - $ # example: - $ gopaste > document.txt - -copy shell command: - - $ go get github.com/atotto/clipboard/cmd/gocopy - $ # example: - $ cat document.txt | gocopy - - - diff --git a/vendor/github.com/atotto/clipboard/clipboard.go b/vendor/github.com/atotto/clipboard/clipboard.go deleted file mode 100644 index d7907d3a..00000000 --- a/vendor/github.com/atotto/clipboard/clipboard.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 @atotto. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package clipboard read/write on clipboard -package clipboard - -// ReadAll read string from clipboard -func ReadAll() (string, error) { - return readAll() -} - -// WriteAll write string to clipboard -func WriteAll(text string) error { - return writeAll(text) -} - -// Unsupported might be set true during clipboard init, to help callers decide -// whether or not to offer clipboard options. -var Unsupported bool diff --git a/vendor/github.com/atotto/clipboard/clipboard_darwin.go b/vendor/github.com/atotto/clipboard/clipboard_darwin.go deleted file mode 100644 index 6f33078d..00000000 --- a/vendor/github.com/atotto/clipboard/clipboard_darwin.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2013 @atotto. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin - -package clipboard - -import ( - "os/exec" -) - -var ( - pasteCmdArgs = "pbpaste" - copyCmdArgs = "pbcopy" -) - -func getPasteCommand() *exec.Cmd { - return exec.Command(pasteCmdArgs) -} - -func getCopyCommand() *exec.Cmd { - return exec.Command(copyCmdArgs) -} - -func readAll() (string, error) { - pasteCmd := getPasteCommand() - out, err := pasteCmd.Output() - if err != nil { - return "", err - } - return string(out), nil -} - -func writeAll(text string) error { - copyCmd := getCopyCommand() - in, err := copyCmd.StdinPipe() - if err != nil { - return err - } - - if err := copyCmd.Start(); err != nil { - return err - } - if _, err := in.Write([]byte(text)); err != nil { - return err - } - if err := in.Close(); err != nil { - return err - } - return copyCmd.Wait() -} diff --git a/vendor/github.com/atotto/clipboard/clipboard_plan9.go b/vendor/github.com/atotto/clipboard/clipboard_plan9.go deleted file mode 100644 index 9d2fef4e..00000000 --- a/vendor/github.com/atotto/clipboard/clipboard_plan9.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2013 @atotto. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build plan9 - -package clipboard - -import ( - "os" - "io/ioutil" -) - -func readAll() (string, error) { - f, err := os.Open("/dev/snarf") - if err != nil { - return "", err - } - defer f.Close() - - str, err := ioutil.ReadAll(f) - if err != nil { - return "", err - } - - return string(str), nil -} - -func writeAll(text string) error { - f, err := os.OpenFile("/dev/snarf", os.O_WRONLY, 0666) - if err != nil { - return err - } - defer f.Close() - - _, err = f.Write([]byte(text)) - if err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/atotto/clipboard/clipboard_unix.go b/vendor/github.com/atotto/clipboard/clipboard_unix.go deleted file mode 100644 index d9f6a561..00000000 --- a/vendor/github.com/atotto/clipboard/clipboard_unix.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2013 @atotto. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd linux netbsd openbsd solaris dragonfly - -package clipboard - -import ( - "errors" - "os" - "os/exec" -) - -const ( - xsel = "xsel" - xclip = "xclip" - powershellExe = "powershell.exe" - clipExe = "clip.exe" - wlcopy = "wl-copy" - wlpaste = "wl-paste" - termuxClipboardGet = "termux-clipboard-get" - termuxClipboardSet = "termux-clipboard-set" -) - -var ( - Primary bool - trimDos bool - - pasteCmdArgs []string - copyCmdArgs []string - - xselPasteArgs = []string{xsel, "--output", "--clipboard"} - xselCopyArgs = []string{xsel, "--input", "--clipboard"} - - xclipPasteArgs = []string{xclip, "-out", "-selection", "clipboard"} - xclipCopyArgs = []string{xclip, "-in", "-selection", "clipboard"} - - powershellExePasteArgs = []string{powershellExe, "Get-Clipboard"} - clipExeCopyArgs = []string{clipExe} - - wlpasteArgs = []string{wlpaste, "--no-newline"} - wlcopyArgs = []string{wlcopy} - - termuxPasteArgs = []string{termuxClipboardGet} - termuxCopyArgs = []string{termuxClipboardSet} - - missingCommands = errors.New("No clipboard utilities available. Please install xsel, xclip, wl-clipboard or Termux:API add-on for termux-clipboard-get/set.") -) - -func init() { - if os.Getenv("WAYLAND_DISPLAY") != "" { - pasteCmdArgs = wlpasteArgs - copyCmdArgs = wlcopyArgs - - if _, err := exec.LookPath(wlcopy); err == nil { - if _, err := exec.LookPath(wlpaste); err == nil { - return - } - } - } - - pasteCmdArgs = xclipPasteArgs - copyCmdArgs = xclipCopyArgs - - if _, err := exec.LookPath(xclip); err == nil { - return - } - - pasteCmdArgs = xselPasteArgs - copyCmdArgs = xselCopyArgs - - if _, err := exec.LookPath(xsel); err == nil { - return - } - - pasteCmdArgs = termuxPasteArgs - copyCmdArgs = termuxCopyArgs - - if _, err := exec.LookPath(termuxClipboardSet); err == nil { - if _, err := exec.LookPath(termuxClipboardGet); err == nil { - return - } - } - - pasteCmdArgs = powershellExePasteArgs - copyCmdArgs = clipExeCopyArgs - trimDos = true - - if _, err := exec.LookPath(clipExe); err == nil { - if _, err := exec.LookPath(powershellExe); err == nil { - return - } - } - - Unsupported = true -} - -func getPasteCommand() *exec.Cmd { - if Primary { - pasteCmdArgs = pasteCmdArgs[:1] - } - return exec.Command(pasteCmdArgs[0], pasteCmdArgs[1:]...) -} - -func getCopyCommand() *exec.Cmd { - if Primary { - copyCmdArgs = copyCmdArgs[:1] - } - return exec.Command(copyCmdArgs[0], copyCmdArgs[1:]...) -} - -func readAll() (string, error) { - if Unsupported { - return "", missingCommands - } - pasteCmd := getPasteCommand() - out, err := pasteCmd.Output() - if err != nil { - return "", err - } - result := string(out) - if trimDos && len(result) > 1 { - result = result[:len(result)-2] - } - return result, nil -} - -func writeAll(text string) error { - if Unsupported { - return missingCommands - } - copyCmd := getCopyCommand() - in, err := copyCmd.StdinPipe() - if err != nil { - return err - } - - if err := copyCmd.Start(); err != nil { - return err - } - if _, err := in.Write([]byte(text)); err != nil { - return err - } - if err := in.Close(); err != nil { - return err - } - return copyCmd.Wait() -} diff --git a/vendor/github.com/atotto/clipboard/clipboard_windows.go b/vendor/github.com/atotto/clipboard/clipboard_windows.go deleted file mode 100644 index 253bb932..00000000 --- a/vendor/github.com/atotto/clipboard/clipboard_windows.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2013 @atotto. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build windows - -package clipboard - -import ( - "runtime" - "syscall" - "time" - "unsafe" -) - -const ( - cfUnicodetext = 13 - gmemMoveable = 0x0002 -) - -var ( - user32 = syscall.MustLoadDLL("user32") - isClipboardFormatAvailable = user32.MustFindProc("IsClipboardFormatAvailable") - openClipboard = user32.MustFindProc("OpenClipboard") - closeClipboard = user32.MustFindProc("CloseClipboard") - emptyClipboard = user32.MustFindProc("EmptyClipboard") - getClipboardData = user32.MustFindProc("GetClipboardData") - setClipboardData = user32.MustFindProc("SetClipboardData") - - kernel32 = syscall.NewLazyDLL("kernel32") - globalAlloc = kernel32.NewProc("GlobalAlloc") - globalFree = kernel32.NewProc("GlobalFree") - globalLock = kernel32.NewProc("GlobalLock") - globalUnlock = kernel32.NewProc("GlobalUnlock") - lstrcpy = kernel32.NewProc("lstrcpyW") -) - -// waitOpenClipboard opens the clipboard, waiting for up to a second to do so. -func waitOpenClipboard() error { - started := time.Now() - limit := started.Add(time.Second) - var r uintptr - var err error - for time.Now().Before(limit) { - r, _, err = openClipboard.Call(0) - if r != 0 { - return nil - } - time.Sleep(time.Millisecond) - } - return err -} - -func readAll() (string, error) { - // LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution). - // Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock. - runtime.LockOSThread() - defer runtime.UnlockOSThread() - if formatAvailable, _, err := isClipboardFormatAvailable.Call(cfUnicodetext); formatAvailable == 0 { - return "", err - } - err := waitOpenClipboard() - if err != nil { - return "", err - } - - h, _, err := getClipboardData.Call(cfUnicodetext) - if h == 0 { - _, _, _ = closeClipboard.Call() - return "", err - } - - l, _, err := globalLock.Call(h) - if l == 0 { - _, _, _ = closeClipboard.Call() - return "", err - } - - text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:]) - - r, _, err := globalUnlock.Call(h) - if r == 0 { - _, _, _ = closeClipboard.Call() - return "", err - } - - closed, _, err := closeClipboard.Call() - if closed == 0 { - return "", err - } - return text, nil -} - -func writeAll(text string) error { - // LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution). - // Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock. - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - err := waitOpenClipboard() - if err != nil { - return err - } - - r, _, err := emptyClipboard.Call(0) - if r == 0 { - _, _, _ = closeClipboard.Call() - return err - } - - data := syscall.StringToUTF16(text) - - // "If the hMem parameter identifies a memory object, the object must have - // been allocated using the function with the GMEM_MOVEABLE flag." - h, _, err := globalAlloc.Call(gmemMoveable, uintptr(len(data)*int(unsafe.Sizeof(data[0])))) - if h == 0 { - _, _, _ = closeClipboard.Call() - return err - } - defer func() { - if h != 0 { - globalFree.Call(h) - } - }() - - l, _, err := globalLock.Call(h) - if l == 0 { - _, _, _ = closeClipboard.Call() - return err - } - - r, _, err = lstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0]))) - if r == 0 { - _, _, _ = closeClipboard.Call() - return err - } - - r, _, err = globalUnlock.Call(h) - if r == 0 { - if err.(syscall.Errno) != 0 { - _, _, _ = closeClipboard.Call() - return err - } - } - - r, _, err = setClipboardData.Call(cfUnicodetext, h) - if r == 0 { - _, _, _ = closeClipboard.Call() - return err - } - h = 0 // suppress deferred cleanup - closed, _, err := closeClipboard.Call() - if closed == 0 { - return err - } - return nil -} diff --git a/vendor/github.com/charmbracelet/bubbles/LICENSE b/vendor/github.com/charmbracelet/bubbles/LICENSE deleted file mode 100644 index 31d76c1c..00000000 --- a/vendor/github.com/charmbracelet/bubbles/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020-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/bubbles/cursor/cursor.go b/vendor/github.com/charmbracelet/bubbles/cursor/cursor.go deleted file mode 100644 index d101332b..00000000 --- a/vendor/github.com/charmbracelet/bubbles/cursor/cursor.go +++ /dev/null @@ -1,219 +0,0 @@ -// Package cursor provides cursor functionality for Bubble Tea applications. -package cursor - -import ( - "context" - "time" - - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -const defaultBlinkSpeed = time.Millisecond * 530 - -// initialBlinkMsg initializes cursor blinking. -type initialBlinkMsg struct{} - -// BlinkMsg signals that the cursor should blink. It contains metadata that -// allows us to tell if the blink message is the one we're expecting. -type BlinkMsg struct { - id int - tag int -} - -// blinkCanceled is sent when a blink operation is canceled. -type blinkCanceled struct{} - -// blinkCtx manages cursor blinking. -type blinkCtx struct { - ctx context.Context - cancel context.CancelFunc -} - -// Mode describes the behavior of the cursor. -type Mode int - -// Available cursor modes. -const ( - CursorBlink Mode = iota - CursorStatic - CursorHide -) - -// String returns the cursor mode in a human-readable format. This method is -// provisional and for informational purposes only. -func (c Mode) String() string { - return [...]string{ - "blink", - "static", - "hidden", - }[c] -} - -// Model is the Bubble Tea model for this cursor element. -type Model struct { - BlinkSpeed time.Duration - // Style for styling the cursor block. - Style lipgloss.Style - // TextStyle is the style used for the cursor when it is hidden (when blinking). - // I.e. displaying normal text. - TextStyle lipgloss.Style - - // char is the character under the cursor - char string - // The ID of this Model as it relates to other cursors - id int - // focus indicates whether the containing input is focused - focus bool - // Cursor Blink state. - Blink bool - // Used to manage cursor blink - blinkCtx *blinkCtx - // The ID of the blink message we're expecting to receive. - blinkTag int - // mode determines the behavior of the cursor - mode Mode -} - -// New creates a new model with default settings. -func New() Model { - return Model{ - BlinkSpeed: defaultBlinkSpeed, - - Blink: true, - mode: CursorBlink, - - blinkCtx: &blinkCtx{ - ctx: context.Background(), - }, - } -} - -// Update updates the cursor. -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { - switch msg := msg.(type) { - case initialBlinkMsg: - // We accept all initialBlinkMsgs generated by the Blink command. - - if m.mode != CursorBlink || !m.focus { - return m, nil - } - - cmd := m.BlinkCmd() - return m, cmd - - case tea.FocusMsg: - return m, m.Focus() - - case tea.BlurMsg: - m.Blur() - return m, nil - - case BlinkMsg: - // We're choosy about whether to accept blinkMsgs so that our cursor - // only exactly when it should. - - // Is this model blink-able? - if m.mode != CursorBlink || !m.focus { - return m, nil - } - - // Were we expecting this blink message? - if msg.id != m.id || msg.tag != m.blinkTag { - return m, nil - } - - var cmd tea.Cmd - if m.mode == CursorBlink { - m.Blink = !m.Blink - cmd = m.BlinkCmd() - } - return m, cmd - - case blinkCanceled: // no-op - return m, nil - } - return m, nil -} - -// Mode returns the model's cursor mode. For available cursor modes, see -// type Mode. -func (m Model) Mode() Mode { - return m.mode -} - -// SetMode sets the model's cursor mode. This method returns a command. -// -// For available cursor modes, see type CursorMode. -func (m *Model) SetMode(mode Mode) tea.Cmd { - // Adjust the mode value if it's value is out of range - if mode < CursorBlink || mode > CursorHide { - return nil - } - m.mode = mode - m.Blink = m.mode == CursorHide || !m.focus - if mode == CursorBlink { - return Blink - } - return nil -} - -// BlinkCmd is a command used to manage cursor blinking. -func (m *Model) BlinkCmd() tea.Cmd { - if m.mode != CursorBlink { - return nil - } - - if m.blinkCtx != nil && m.blinkCtx.cancel != nil { - m.blinkCtx.cancel() - } - - ctx, cancel := context.WithTimeout(m.blinkCtx.ctx, m.BlinkSpeed) - m.blinkCtx.cancel = cancel - - m.blinkTag++ - - return func() tea.Msg { - defer cancel() - <-ctx.Done() - if ctx.Err() == context.DeadlineExceeded { - return BlinkMsg{id: m.id, tag: m.blinkTag} - } - return blinkCanceled{} - } -} - -// Blink is a command used to initialize cursor blinking. -func Blink() tea.Msg { - return initialBlinkMsg{} -} - -// Focus focuses the cursor to allow it to blink if desired. -func (m *Model) Focus() tea.Cmd { - m.focus = true - m.Blink = m.mode == CursorHide // show the cursor unless we've explicitly hidden it - - if m.mode == CursorBlink && m.focus { - return m.BlinkCmd() - } - return nil -} - -// Blur blurs the cursor. -func (m *Model) Blur() { - m.focus = false - m.Blink = true -} - -// SetChar sets the character under the cursor. -func (m *Model) SetChar(char string) { - m.char = char -} - -// View displays the cursor. -func (m Model) View() string { - if m.Blink { - return m.TextStyle.Inline(true).Render(m.char) - } - return m.Style.Inline(true).Reverse(true).Render(m.char) -} diff --git a/vendor/github.com/charmbracelet/bubbles/key/key.go b/vendor/github.com/charmbracelet/bubbles/key/key.go deleted file mode 100644 index 0682665d..00000000 --- a/vendor/github.com/charmbracelet/bubbles/key/key.go +++ /dev/null @@ -1,140 +0,0 @@ -// Package key provides some types and functions for generating user-definable -// keymappings useful in Bubble Tea components. There are a few different ways -// you can define a keymapping with this package. Here's one example: -// -// type KeyMap struct { -// Up key.Binding -// Down key.Binding -// } -// -// var DefaultKeyMap = KeyMap{ -// Up: key.NewBinding( -// key.WithKeys("k", "up"), // actual keybindings -// key.WithHelp("↑/k", "move up"), // corresponding help text -// ), -// Down: key.NewBinding( -// key.WithKeys("j", "down"), -// key.WithHelp("↓/j", "move down"), -// ), -// } -// -// func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { -// switch msg := msg.(type) { -// case tea.KeyMsg: -// switch { -// case key.Matches(msg, DefaultKeyMap.Up): -// // The user pressed up -// case key.Matches(msg, DefaultKeyMap.Down): -// // The user pressed down -// } -// } -// -// // ... -// } -// -// The help information, which is not used in the example above, can be used -// to render help text for keystrokes in your views. -package key - -import "fmt" - -// Binding describes a set of keybindings and, optionally, their associated -// help text. -type Binding struct { - keys []string - help Help - disabled bool -} - -// BindingOpt is an initialization option for a keybinding. It's used as an -// argument to NewBinding. -type BindingOpt func(*Binding) - -// NewBinding returns a new keybinding from a set of BindingOpt options. -func NewBinding(opts ...BindingOpt) Binding { - b := &Binding{} - for _, opt := range opts { - opt(b) - } - return *b -} - -// WithKeys initializes a keybinding with the given keystrokes. -func WithKeys(keys ...string) BindingOpt { - return func(b *Binding) { - b.keys = keys - } -} - -// WithHelp initializes a keybinding with the given help text. -func WithHelp(key, desc string) BindingOpt { - return func(b *Binding) { - b.help = Help{Key: key, Desc: desc} - } -} - -// WithDisabled initializes a disabled keybinding. -func WithDisabled() BindingOpt { - return func(b *Binding) { - b.disabled = true - } -} - -// SetKeys sets the keys for the keybinding. -func (b *Binding) SetKeys(keys ...string) { - b.keys = keys -} - -// Keys returns the keys for the keybinding. -func (b Binding) Keys() []string { - return b.keys -} - -// SetHelp sets the help text for the keybinding. -func (b *Binding) SetHelp(key, desc string) { - b.help = Help{Key: key, Desc: desc} -} - -// Help returns the Help information for the keybinding. -func (b Binding) Help() Help { - return b.help -} - -// Enabled returns whether or not the keybinding is enabled. Disabled -// keybindings won't be activated and won't show up in help. Keybindings are -// enabled by default. -func (b Binding) Enabled() bool { - return !b.disabled && b.keys != nil -} - -// SetEnabled enables or disables the keybinding. -func (b *Binding) SetEnabled(v bool) { - b.disabled = !v -} - -// Unbind removes the keys and help from this binding, effectively nullifying -// it. This is a step beyond disabling it, since applications can enable -// or disable key bindings based on application state. -func (b *Binding) Unbind() { - b.keys = nil - b.help = Help{} -} - -// Help is help information for a given keybinding. -type Help struct { - Key string - Desc string -} - -// Matches checks if the given key matches the given bindings. -func Matches[Key fmt.Stringer](k Key, b ...Binding) bool { - keys := k.String() - for _, binding := range b { - for _, v := range binding.keys { - if keys == v && binding.Enabled() { - return true - } - } - } - return false -} diff --git a/vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go b/vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go deleted file mode 100644 index 82ea90a2..00000000 --- a/vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go +++ /dev/null @@ -1,102 +0,0 @@ -// Package runeutil provides a utility function for use in Bubbles -// that can process Key messages containing runes. -package runeutil - -import ( - "unicode" - "unicode/utf8" -) - -// Sanitizer is a helper for bubble widgets that want to process -// Runes from input key messages. -type Sanitizer interface { - // Sanitize removes control characters from runes in a KeyRunes - // message, and optionally replaces newline/carriage return/tabs by a - // specified character. - // - // The rune array is modified in-place if possible. In that case, the - // returned slice is the original slice shortened after the control - // characters have been removed/translated. - Sanitize(runes []rune) []rune -} - -// NewSanitizer constructs a rune sanitizer. -func NewSanitizer(opts ...Option) Sanitizer { - s := sanitizer{ - replaceNewLine: []rune("\n"), - replaceTab: []rune(" "), - } - for _, o := range opts { - s = o(s) - } - return &s -} - -// Option is the type of option that can be passed to Sanitize(). -type Option func(sanitizer) sanitizer - -// ReplaceTabs replaces tabs by the specified string. -func ReplaceTabs(tabRepl string) Option { - return func(s sanitizer) sanitizer { - s.replaceTab = []rune(tabRepl) - return s - } -} - -// ReplaceNewlines replaces newline characters by the specified string. -func ReplaceNewlines(nlRepl string) Option { - return func(s sanitizer) sanitizer { - s.replaceNewLine = []rune(nlRepl) - return s - } -} - -func (s *sanitizer) Sanitize(runes []rune) []rune { - // dstrunes are where we are storing the result. - dstrunes := runes[:0:len(runes)] - // copied indicates whether dstrunes is an alias of runes - // or a copy. We need a copy when dst moves past src. - // We use this as an optimization to avoid allocating - // a new rune slice in the common case where the output - // is smaller or equal to the input. - copied := false - - for src := 0; src < len(runes); src++ { - r := runes[src] - switch { - case r == utf8.RuneError: - // skip - - case r == '\r' || r == '\n': - if len(dstrunes)+len(s.replaceNewLine) > src && !copied { - dst := len(dstrunes) - dstrunes = make([]rune, dst, len(runes)+len(s.replaceNewLine)) - copy(dstrunes, runes[:dst]) - copied = true - } - dstrunes = append(dstrunes, s.replaceNewLine...) - - case r == '\t': - if len(dstrunes)+len(s.replaceTab) > src && !copied { - dst := len(dstrunes) - dstrunes = make([]rune, dst, len(runes)+len(s.replaceTab)) - copy(dstrunes, runes[:dst]) - copied = true - } - dstrunes = append(dstrunes, s.replaceTab...) - - case unicode.IsControl(r): - // Other control characters: skip. - - default: - // Keep the character. - dstrunes = append(dstrunes, runes[src]) - } - } - return dstrunes -} - -type sanitizer struct { - replaceNewLine []rune - replaceTab []rune -} diff --git a/vendor/github.com/charmbracelet/bubbles/spinner/spinner.go b/vendor/github.com/charmbracelet/bubbles/spinner/spinner.go deleted file mode 100644 index b2d24b09..00000000 --- a/vendor/github.com/charmbracelet/bubbles/spinner/spinner.go +++ /dev/null @@ -1,224 +0,0 @@ -// Package spinner provides a spinner component for Bubble Tea applications. -package spinner - -import ( - "sync/atomic" - "time" - - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -// Internal ID management. Used during animating to ensure that frame messages -// are received only by spinner components that sent them. -var lastID int64 - -func nextID() int { - return int(atomic.AddInt64(&lastID, 1)) -} - -// Spinner is a set of frames used in animating the spinner. -type Spinner struct { - Frames []string - FPS time.Duration -} - -// Some spinners to choose from. You could also make your own. -var ( - Line = Spinner{ - Frames: []string{"|", "/", "-", "\\"}, - FPS: time.Second / 10, //nolint:mnd - } - Dot = Spinner{ - Frames: []string{"⣾ ", "⣽ ", "⣻ ", "⢿ ", "⡿ ", "⣟ ", "⣯ ", "⣷ "}, - FPS: time.Second / 10, //nolint:mnd - } - MiniDot = Spinner{ - Frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}, - FPS: time.Second / 12, //nolint:mnd - } - Jump = Spinner{ - Frames: []string{"⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"}, - FPS: time.Second / 10, //nolint:mnd - } - Pulse = Spinner{ - Frames: []string{"█", "▓", "▒", "░"}, - FPS: time.Second / 8, //nolint:mnd - } - Points = Spinner{ - Frames: []string{"∙∙∙", "●∙∙", "∙●∙", "∙∙●"}, - FPS: time.Second / 7, //nolint:mnd - } - Globe = Spinner{ - Frames: []string{"🌍", "🌎", "🌏"}, - FPS: time.Second / 4, //nolint:mnd - } - Moon = Spinner{ - Frames: []string{"🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"}, - FPS: time.Second / 8, //nolint:mnd - } - Monkey = Spinner{ - Frames: []string{"🙈", "🙉", "🙊"}, - FPS: time.Second / 3, //nolint:mnd - } - Meter = Spinner{ - Frames: []string{ - "▱▱▱", - "▰▱▱", - "▰▰▱", - "▰▰▰", - "▰▰▱", - "▰▱▱", - "▱▱▱", - }, - FPS: time.Second / 7, //nolint:mnd - } - Hamburger = Spinner{ - Frames: []string{"☱", "☲", "☴", "☲"}, - FPS: time.Second / 3, //nolint:mnd - } - Ellipsis = Spinner{ - Frames: []string{"", ".", "..", "..."}, - FPS: time.Second / 3, //nolint:mnd - } -) - -// Model contains the state for the spinner. Use New to create new models -// rather than using Model as a struct literal. -type Model struct { - // Spinner settings to use. See type Spinner. - Spinner Spinner - - // Style sets the styling for the spinner. Most of the time you'll just - // want foreground and background coloring, and potentially some padding. - // - // For an introduction to styling with Lip Gloss see: - // https://github.com/charmbracelet/lipgloss - Style lipgloss.Style - - frame int - id int - tag int -} - -// ID returns the spinner's unique ID. -func (m Model) ID() int { - return m.id -} - -// New returns a model with default values. -func New(opts ...Option) Model { - m := Model{ - Spinner: Line, - id: nextID(), - } - - for _, opt := range opts { - opt(&m) - } - - return m -} - -// NewModel returns a model with default values. -// -// Deprecated: use [New] instead. -var NewModel = New - -// TickMsg indicates that the timer has ticked and we should render a frame. -type TickMsg struct { - Time time.Time - tag int - ID int -} - -// Update is the Tea update function. -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { - switch msg := msg.(type) { - case TickMsg: - // If an ID is set, and the ID doesn't belong to this spinner, reject - // the message. - if msg.ID > 0 && msg.ID != m.id { - return m, nil - } - - // If a tag is set, and it's not the one we expect, reject the message. - // This prevents the spinner from receiving too many messages and - // thus spinning too fast. - if msg.tag > 0 && msg.tag != m.tag { - return m, nil - } - - m.frame++ - if m.frame >= len(m.Spinner.Frames) { - m.frame = 0 - } - - m.tag++ - return m, m.tick(m.id, m.tag) - default: - return m, nil - } -} - -// View renders the model's view. -func (m Model) View() string { - if m.frame >= len(m.Spinner.Frames) { - return "(error)" - } - - return m.Style.Render(m.Spinner.Frames[m.frame]) -} - -// Tick is the command used to advance the spinner one frame. Use this command -// to effectively start the spinner. -func (m Model) Tick() tea.Msg { - return TickMsg{ - // The time at which the tick occurred. - Time: time.Now(), - - // The ID of the spinner that this message belongs to. This can be - // helpful when routing messages, however bear in mind that spinners - // will ignore messages that don't contain ID by default. - ID: m.id, - - tag: m.tag, - } -} - -func (m Model) tick(id, tag int) tea.Cmd { - return tea.Tick(m.Spinner.FPS, func(t time.Time) tea.Msg { - return TickMsg{ - Time: t, - ID: id, - tag: tag, - } - }) -} - -// Tick is the command used to advance the spinner one frame. Use this command -// to effectively start the spinner. -// -// Deprecated: Use [Model.Tick] instead. -func Tick() tea.Msg { - return TickMsg{Time: time.Now()} -} - -// Option is used to set options in New. For example: -// -// spinner := New(WithSpinner(Dot)) -type Option func(*Model) - -// WithSpinner is an option to set the spinner. -func WithSpinner(spinner Spinner) Option { - return func(m *Model) { - m.Spinner = spinner - } -} - -// WithStyle is an option to set the spinner style. -func WithStyle(style lipgloss.Style) Option { - return func(m *Model) { - m.Style = style - } -} diff --git a/vendor/github.com/charmbracelet/bubbles/textinput/textinput.go b/vendor/github.com/charmbracelet/bubbles/textinput/textinput.go deleted file mode 100644 index 84cbbc93..00000000 --- a/vendor/github.com/charmbracelet/bubbles/textinput/textinput.go +++ /dev/null @@ -1,898 +0,0 @@ -// Package textinput provides a text input component for Bubble Tea -// applications. -package textinput - -import ( - "reflect" - "strings" - "time" - "unicode" - - "github.com/atotto/clipboard" - "github.com/charmbracelet/bubbles/cursor" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/runeutil" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - rw "github.com/mattn/go-runewidth" - "github.com/rivo/uniseg" -) - -// Internal messages for clipboard operations. -type ( - pasteMsg string - pasteErrMsg struct{ error } -) - -// EchoMode sets the input behavior of the text input field. -type EchoMode int - -const ( - // EchoNormal displays text as is. This is the default behavior. - EchoNormal EchoMode = iota - - // EchoPassword displays the EchoCharacter mask instead of actual - // characters. This is commonly used for password fields. - EchoPassword - - // EchoNone displays nothing as characters are entered. This is commonly - // seen for password fields on the command line. - EchoNone -) - -// ValidateFunc is a function that returns an error if the input is invalid. -type ValidateFunc func(string) error - -// KeyMap is the key bindings for different actions within the textinput. -type KeyMap struct { - CharacterForward key.Binding - CharacterBackward key.Binding - WordForward key.Binding - WordBackward key.Binding - DeleteWordBackward key.Binding - DeleteWordForward key.Binding - DeleteAfterCursor key.Binding - DeleteBeforeCursor key.Binding - DeleteCharacterBackward key.Binding - DeleteCharacterForward key.Binding - LineStart key.Binding - LineEnd key.Binding - Paste key.Binding - AcceptSuggestion key.Binding - NextSuggestion key.Binding - PrevSuggestion key.Binding -} - -// DefaultKeyMap is the default set of key bindings for navigating and acting -// upon the textinput. -var DefaultKeyMap = KeyMap{ - CharacterForward: key.NewBinding(key.WithKeys("right", "ctrl+f")), - CharacterBackward: key.NewBinding(key.WithKeys("left", "ctrl+b")), - WordForward: key.NewBinding(key.WithKeys("alt+right", "ctrl+right", "alt+f")), - WordBackward: key.NewBinding(key.WithKeys("alt+left", "ctrl+left", "alt+b")), - DeleteWordBackward: key.NewBinding(key.WithKeys("alt+backspace", "ctrl+w")), - DeleteWordForward: key.NewBinding(key.WithKeys("alt+delete", "alt+d")), - DeleteAfterCursor: key.NewBinding(key.WithKeys("ctrl+k")), - DeleteBeforeCursor: key.NewBinding(key.WithKeys("ctrl+u")), - DeleteCharacterBackward: key.NewBinding(key.WithKeys("backspace", "ctrl+h")), - DeleteCharacterForward: key.NewBinding(key.WithKeys("delete", "ctrl+d")), - LineStart: key.NewBinding(key.WithKeys("home", "ctrl+a")), - LineEnd: key.NewBinding(key.WithKeys("end", "ctrl+e")), - Paste: key.NewBinding(key.WithKeys("ctrl+v")), - AcceptSuggestion: key.NewBinding(key.WithKeys("tab")), - NextSuggestion: key.NewBinding(key.WithKeys("down", "ctrl+n")), - PrevSuggestion: key.NewBinding(key.WithKeys("up", "ctrl+p")), -} - -// Model is the Bubble Tea model for this text input element. -type Model struct { - Err error - - // General settings. - Prompt string - Placeholder string - EchoMode EchoMode - EchoCharacter rune - Cursor cursor.Model - - // Deprecated: use [cursor.BlinkSpeed] instead. - BlinkSpeed time.Duration - - // Styles. These will be applied as inline styles. - // - // For an introduction to styling with Lip Gloss see: - // https://github.com/charmbracelet/lipgloss - PromptStyle lipgloss.Style - TextStyle lipgloss.Style - PlaceholderStyle lipgloss.Style - CompletionStyle lipgloss.Style - - // Deprecated: use Cursor.Style instead. - CursorStyle lipgloss.Style - - // CharLimit is the maximum amount of characters this input element will - // accept. If 0 or less, there's no limit. - CharLimit int - - // Width is the maximum number of characters that can be displayed at once. - // It essentially treats the text field like a horizontally scrolling - // viewport. If 0 or less this setting is ignored. - Width int - - // KeyMap encodes the keybindings recognized by the widget. - KeyMap KeyMap - - // Underlying text value. - value []rune - - // focus indicates whether user input focus should be on this input - // component. When false, ignore keyboard input and hide the cursor. - focus bool - - // Cursor position. - pos int - - // Used to emulate a viewport when width is set and the content is - // overflowing. - offset int - offsetRight int - - // Validate is a function that checks whether or not the text within the - // input is valid. If it is not valid, the `Err` field will be set to the - // error returned by the function. If the function is not defined, all - // input is considered valid. - Validate ValidateFunc - - // rune sanitizer for input. - rsan runeutil.Sanitizer - - // Should the input suggest to complete - ShowSuggestions bool - - // suggestions is a list of suggestions that may be used to complete the - // input. - suggestions [][]rune - matchedSuggestions [][]rune - currentSuggestionIndex int -} - -// New creates a new model with default settings. -func New() Model { - return Model{ - Prompt: "> ", - EchoCharacter: '*', - CharLimit: 0, - PlaceholderStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("240")), - ShowSuggestions: false, - CompletionStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("240")), - Cursor: cursor.New(), - KeyMap: DefaultKeyMap, - - suggestions: [][]rune{}, - value: nil, - focus: false, - pos: 0, - } -} - -// NewModel creates a new model with default settings. -// -// Deprecated: Use [New] instead. -var NewModel = New - -// SetValue sets the value of the text input. -func (m *Model) SetValue(s string) { - // Clean up any special characters in the input provided by the - // caller. This avoids bugs due to e.g. tab characters and whatnot. - runes := m.san().Sanitize([]rune(s)) - err := m.validate(runes) - m.setValueInternal(runes, err) -} - -func (m *Model) setValueInternal(runes []rune, err error) { - m.Err = err - - empty := len(m.value) == 0 - - if m.CharLimit > 0 && len(runes) > m.CharLimit { - m.value = runes[:m.CharLimit] - } else { - m.value = runes - } - if (m.pos == 0 && empty) || m.pos > len(m.value) { - m.SetCursor(len(m.value)) - } - m.handleOverflow() -} - -// Value returns the value of the text input. -func (m Model) Value() string { - return string(m.value) -} - -// Position returns the cursor position. -func (m Model) Position() int { - return m.pos -} - -// SetCursor moves the cursor to the given position. If the position is -// out of bounds the cursor will be moved to the start or end accordingly. -func (m *Model) SetCursor(pos int) { - m.pos = clamp(pos, 0, len(m.value)) - m.handleOverflow() -} - -// CursorStart moves the cursor to the start of the input field. -func (m *Model) CursorStart() { - m.SetCursor(0) -} - -// CursorEnd moves the cursor to the end of the input field. -func (m *Model) CursorEnd() { - m.SetCursor(len(m.value)) -} - -// Focused returns the focus state on the model. -func (m Model) Focused() bool { - return m.focus -} - -// Focus sets the focus state on the model. When the model is in focus it can -// receive keyboard input and the cursor will be shown. -func (m *Model) Focus() tea.Cmd { - m.focus = true - return m.Cursor.Focus() -} - -// Blur removes the focus state on the model. When the model is blurred it can -// not receive keyboard input and the cursor will be hidden. -func (m *Model) Blur() { - m.focus = false - m.Cursor.Blur() -} - -// Reset sets the input to its default state with no input. -func (m *Model) Reset() { - m.value = nil - m.SetCursor(0) -} - -// SetSuggestions sets the suggestions for the input. -func (m *Model) SetSuggestions(suggestions []string) { - m.suggestions = make([][]rune, len(suggestions)) - for i, s := range suggestions { - m.suggestions[i] = []rune(s) - } - - m.updateSuggestions() -} - -// rsan initializes or retrieves the rune sanitizer. -func (m *Model) san() runeutil.Sanitizer { - if m.rsan == nil { - // Textinput has all its input on a single line so collapse - // newlines/tabs to single spaces. - m.rsan = runeutil.NewSanitizer( - runeutil.ReplaceTabs(" "), runeutil.ReplaceNewlines(" ")) - } - return m.rsan -} - -func (m *Model) insertRunesFromUserInput(v []rune) { - // Clean up any special characters in the input provided by the - // clipboard. This avoids bugs due to e.g. tab characters and - // whatnot. - paste := m.san().Sanitize(v) - - var availSpace int - if m.CharLimit > 0 { - availSpace = m.CharLimit - len(m.value) - - // If the char limit's been reached, cancel. - if availSpace <= 0 { - return - } - - // If there's not enough space to paste the whole thing cut the pasted - // runes down so they'll fit. - if availSpace < len(paste) { - paste = paste[:availSpace] - } - } - - // Stuff before and after the cursor - head := m.value[:m.pos] - tailSrc := m.value[m.pos:] - tail := make([]rune, len(tailSrc)) - copy(tail, tailSrc) - - // Insert pasted runes - for _, r := range paste { - head = append(head, r) - m.pos++ - if m.CharLimit > 0 { - availSpace-- - if availSpace <= 0 { - break - } - } - } - - // Put it all back together - value := append(head, tail...) - inputErr := m.validate(value) - m.setValueInternal(value, inputErr) -} - -// If a max width is defined, perform some logic to treat the visible area -// as a horizontally scrolling viewport. -func (m *Model) handleOverflow() { - if m.Width <= 0 || uniseg.StringWidth(string(m.value)) <= m.Width { - m.offset = 0 - m.offsetRight = len(m.value) - return - } - - // Correct right offset if we've deleted characters - m.offsetRight = min(m.offsetRight, len(m.value)) - - if m.pos < m.offset { - m.offset = m.pos - - w := 0 - i := 0 - runes := m.value[m.offset:] - - for i < len(runes) && w <= m.Width { - w += rw.RuneWidth(runes[i]) - if w <= m.Width+1 { - i++ - } - } - - m.offsetRight = m.offset + i - } else if m.pos >= m.offsetRight { - m.offsetRight = m.pos - - w := 0 - runes := m.value[:m.offsetRight] - i := len(runes) - 1 - - for i > 0 && w < m.Width { - w += rw.RuneWidth(runes[i]) - if w <= m.Width { - i-- - } - } - - m.offset = m.offsetRight - (len(runes) - 1 - i) - } -} - -// deleteBeforeCursor deletes all text before the cursor. -func (m *Model) deleteBeforeCursor() { - m.value = m.value[m.pos:] - m.Err = m.validate(m.value) - m.offset = 0 - m.SetCursor(0) -} - -// deleteAfterCursor deletes all text after the cursor. If input is masked -// delete everything after the cursor so as not to reveal word breaks in the -// masked input. -func (m *Model) deleteAfterCursor() { - m.value = m.value[:m.pos] - m.Err = m.validate(m.value) - m.SetCursor(len(m.value)) -} - -// deleteWordBackward deletes the word left to the cursor. -func (m *Model) deleteWordBackward() { - if m.pos == 0 || len(m.value) == 0 { - return - } - - if m.EchoMode != EchoNormal { - m.deleteBeforeCursor() - return - } - - // Linter note: it's critical that we acquire the initial cursor position - // here prior to altering it via SetCursor() below. As such, moving this - // call into the corresponding if clause does not apply here. - oldPos := m.pos //nolint:ifshort - - m.SetCursor(m.pos - 1) - for unicode.IsSpace(m.value[m.pos]) { - if m.pos <= 0 { - break - } - // ignore series of whitespace before cursor - m.SetCursor(m.pos - 1) - } - - for m.pos > 0 { - if !unicode.IsSpace(m.value[m.pos]) { - m.SetCursor(m.pos - 1) - } else { - if m.pos > 0 { - // keep the previous space - m.SetCursor(m.pos + 1) - } - break - } - } - - if oldPos > len(m.value) { - m.value = m.value[:m.pos] - } else { - m.value = append(m.value[:m.pos], m.value[oldPos:]...) - } - m.Err = m.validate(m.value) -} - -// deleteWordForward deletes the word right to the cursor. If input is masked -// delete everything after the cursor so as not to reveal word breaks in the -// masked input. -func (m *Model) deleteWordForward() { - if m.pos >= len(m.value) || len(m.value) == 0 { - return - } - - if m.EchoMode != EchoNormal { - m.deleteAfterCursor() - return - } - - oldPos := m.pos - m.SetCursor(m.pos + 1) - for unicode.IsSpace(m.value[m.pos]) { - // ignore series of whitespace after cursor - m.SetCursor(m.pos + 1) - - if m.pos >= len(m.value) { - break - } - } - - for m.pos < len(m.value) { - if !unicode.IsSpace(m.value[m.pos]) { - m.SetCursor(m.pos + 1) - } else { - break - } - } - - if m.pos > len(m.value) { - m.value = m.value[:oldPos] - } else { - m.value = append(m.value[:oldPos], m.value[m.pos:]...) - } - m.Err = m.validate(m.value) - - m.SetCursor(oldPos) -} - -// wordBackward moves the cursor one word to the left. If input is masked, move -// input to the start so as not to reveal word breaks in the masked input. -func (m *Model) wordBackward() { - if m.pos == 0 || len(m.value) == 0 { - return - } - - if m.EchoMode != EchoNormal { - m.CursorStart() - return - } - - i := m.pos - 1 - for i >= 0 { - if unicode.IsSpace(m.value[i]) { - m.SetCursor(m.pos - 1) - i-- - } else { - break - } - } - - for i >= 0 { - if !unicode.IsSpace(m.value[i]) { - m.SetCursor(m.pos - 1) - i-- - } else { - break - } - } -} - -// wordForward moves the cursor one word to the right. If the input is masked, -// move input to the end so as not to reveal word breaks in the masked input. -func (m *Model) wordForward() { - if m.pos >= len(m.value) || len(m.value) == 0 { - return - } - - if m.EchoMode != EchoNormal { - m.CursorEnd() - return - } - - i := m.pos - for i < len(m.value) { - if unicode.IsSpace(m.value[i]) { - m.SetCursor(m.pos + 1) - i++ - } else { - break - } - } - - for i < len(m.value) { - if !unicode.IsSpace(m.value[i]) { - m.SetCursor(m.pos + 1) - i++ - } else { - break - } - } -} - -func (m Model) echoTransform(v string) string { - switch m.EchoMode { - case EchoPassword: - return strings.Repeat(string(m.EchoCharacter), uniseg.StringWidth(v)) - case EchoNone: - return "" - case EchoNormal: - return v - default: - return v - } -} - -// Update is the Bubble Tea update loop. -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { - if !m.focus { - return m, nil - } - - // Need to check for completion before, because key is configurable and might be double assigned - keyMsg, ok := msg.(tea.KeyMsg) - if ok && key.Matches(keyMsg, m.KeyMap.AcceptSuggestion) { - if m.canAcceptSuggestion() { - m.value = append(m.value, m.matchedSuggestions[m.currentSuggestionIndex][len(m.value):]...) - m.CursorEnd() - } - } - - // Let's remember where the position of the cursor currently is so that if - // the cursor position changes, we can reset the blink. - oldPos := m.pos - - switch msg := msg.(type) { - case tea.KeyMsg: - switch { - case key.Matches(msg, m.KeyMap.DeleteWordBackward): - m.deleteWordBackward() - case key.Matches(msg, m.KeyMap.DeleteCharacterBackward): - m.Err = nil - if len(m.value) > 0 { - m.value = append(m.value[:max(0, m.pos-1)], m.value[m.pos:]...) - m.Err = m.validate(m.value) - if m.pos > 0 { - m.SetCursor(m.pos - 1) - } - } - case key.Matches(msg, m.KeyMap.WordBackward): - m.wordBackward() - case key.Matches(msg, m.KeyMap.CharacterBackward): - if m.pos > 0 { - m.SetCursor(m.pos - 1) - } - case key.Matches(msg, m.KeyMap.WordForward): - m.wordForward() - case key.Matches(msg, m.KeyMap.CharacterForward): - if m.pos < len(m.value) { - m.SetCursor(m.pos + 1) - } - case key.Matches(msg, m.KeyMap.LineStart): - m.CursorStart() - case key.Matches(msg, m.KeyMap.DeleteCharacterForward): - if len(m.value) > 0 && m.pos < len(m.value) { - m.value = append(m.value[:m.pos], m.value[m.pos+1:]...) - m.Err = m.validate(m.value) - } - case key.Matches(msg, m.KeyMap.LineEnd): - m.CursorEnd() - case key.Matches(msg, m.KeyMap.DeleteAfterCursor): - m.deleteAfterCursor() - case key.Matches(msg, m.KeyMap.DeleteBeforeCursor): - m.deleteBeforeCursor() - case key.Matches(msg, m.KeyMap.Paste): - return m, Paste - case key.Matches(msg, m.KeyMap.DeleteWordForward): - m.deleteWordForward() - case key.Matches(msg, m.KeyMap.NextSuggestion): - m.nextSuggestion() - case key.Matches(msg, m.KeyMap.PrevSuggestion): - m.previousSuggestion() - default: - // Input one or more regular characters. - m.insertRunesFromUserInput(msg.Runes) - } - - // Check again if can be completed - // because value might be something that does not match the completion prefix - m.updateSuggestions() - - case pasteMsg: - m.insertRunesFromUserInput([]rune(msg)) - - case pasteErrMsg: - m.Err = msg - } - - var cmds []tea.Cmd - var cmd tea.Cmd - - m.Cursor, cmd = m.Cursor.Update(msg) - cmds = append(cmds, cmd) - - if oldPos != m.pos && m.Cursor.Mode() == cursor.CursorBlink { - m.Cursor.Blink = false - cmds = append(cmds, m.Cursor.BlinkCmd()) - } - - m.handleOverflow() - return m, tea.Batch(cmds...) -} - -// View renders the textinput in its current state. -func (m Model) View() string { - // Placeholder text - if len(m.value) == 0 && m.Placeholder != "" { - return m.placeholderView() - } - - styleText := m.TextStyle.Inline(true).Render - - value := m.value[m.offset:m.offsetRight] - pos := max(0, m.pos-m.offset) - v := styleText(m.echoTransform(string(value[:pos]))) - - if pos < len(value) { //nolint:nestif - char := m.echoTransform(string(value[pos])) - m.Cursor.SetChar(char) - v += m.Cursor.View() // cursor and text under it - v += styleText(m.echoTransform(string(value[pos+1:]))) // text after cursor - v += m.completionView(0) // suggested completion - } else { - if m.focus && m.canAcceptSuggestion() { - suggestion := m.matchedSuggestions[m.currentSuggestionIndex] - if len(value) < len(suggestion) { - m.Cursor.TextStyle = m.CompletionStyle - m.Cursor.SetChar(m.echoTransform(string(suggestion[pos]))) - v += m.Cursor.View() - v += m.completionView(1) - } else { - m.Cursor.SetChar(" ") - v += m.Cursor.View() - } - } else { - m.Cursor.SetChar(" ") - v += m.Cursor.View() - } - } - - // If a max width and background color were set fill the empty spaces with - // the background color. - valWidth := uniseg.StringWidth(string(value)) - if m.Width > 0 && valWidth <= m.Width { - padding := max(0, m.Width-valWidth) - if valWidth+padding <= m.Width && pos < len(value) { - padding++ - } - v += styleText(strings.Repeat(" ", padding)) - } - - return m.PromptStyle.Render(m.Prompt) + v -} - -// placeholderView returns the prompt and placeholder view, if any. -func (m Model) placeholderView() string { - var ( - v string - style = m.PlaceholderStyle.Inline(true).Render - ) - - p := make([]rune, m.Width+1) - copy(p, []rune(m.Placeholder)) - - m.Cursor.TextStyle = m.PlaceholderStyle - m.Cursor.SetChar(string(p[:1])) - v += m.Cursor.View() - - // If the entire placeholder is already set and no padding is needed, finish - if m.Width < 1 && len(p) <= 1 { - return m.PromptStyle.Render(m.Prompt) + v - } - - // If Width is set then size placeholder accordingly - if m.Width > 0 { - // available width is width - len + cursor offset of 1 - minWidth := lipgloss.Width(m.Placeholder) - availWidth := m.Width - minWidth + 1 - - // if width < len, 'subtract'(add) number to len and dont add padding - if availWidth < 0 { - minWidth += availWidth - availWidth = 0 - } - // append placeholder[len] - cursor, append padding - v += style(string(p[1:minWidth])) - v += style(strings.Repeat(" ", availWidth)) - } else { - // if there is no width, the placeholder can be any length - v += style(string(p[1:])) - } - - return m.PromptStyle.Render(m.Prompt) + v -} - -// Blink is a command used to initialize cursor blinking. -func Blink() tea.Msg { - return cursor.Blink() -} - -// Paste is a command for pasting from the clipboard into the text input. -func Paste() tea.Msg { - str, err := clipboard.ReadAll() - if err != nil { - return pasteErrMsg{err} - } - return pasteMsg(str) -} - -func clamp(v, low, high int) int { - if high < low { - low, high = high, low - } - return min(high, max(low, v)) -} - -// Deprecated. - -// Deprecated: use [cursor.Mode]. -// -//nolint:revive -type CursorMode int - -//nolint:revive -const ( - // Deprecated: use [cursor.CursorBlink]. - CursorBlink = CursorMode(cursor.CursorBlink) - // Deprecated: use [cursor.CursorStatic]. - CursorStatic = CursorMode(cursor.CursorStatic) - // Deprecated: use [cursor.CursorHide]. - CursorHide = CursorMode(cursor.CursorHide) -) - -func (c CursorMode) String() string { - return cursor.Mode(c).String() -} - -// Deprecated: use [cursor.Mode]. -// -//nolint:revive -func (m Model) CursorMode() CursorMode { - return CursorMode(m.Cursor.Mode()) -} - -// Deprecated: use cursor.SetMode(). -// -//nolint:revive -func (m *Model) SetCursorMode(mode CursorMode) tea.Cmd { - return m.Cursor.SetMode(cursor.Mode(mode)) -} - -func (m Model) completionView(offset int) string { - var ( - value = m.value - style = m.PlaceholderStyle.Inline(true).Render - ) - - if m.canAcceptSuggestion() { - suggestion := m.matchedSuggestions[m.currentSuggestionIndex] - if len(value) < len(suggestion) { - return style(string(suggestion[len(value)+offset:])) - } - } - return "" -} - -func (m *Model) getSuggestions(sugs [][]rune) []string { - suggestions := make([]string, len(sugs)) - for i, s := range sugs { - suggestions[i] = string(s) - } - return suggestions -} - -// AvailableSuggestions returns the list of available suggestions. -func (m *Model) AvailableSuggestions() []string { - return m.getSuggestions(m.suggestions) -} - -// MatchedSuggestions returns the list of matched suggestions. -func (m *Model) MatchedSuggestions() []string { - return m.getSuggestions(m.matchedSuggestions) -} - -// CurrentSuggestionIndex returns the currently selected suggestion index. -func (m *Model) CurrentSuggestionIndex() int { - return m.currentSuggestionIndex -} - -// CurrentSuggestion returns the currently selected suggestion. -func (m *Model) CurrentSuggestion() string { - if m.currentSuggestionIndex >= len(m.matchedSuggestions) { - return "" - } - - return string(m.matchedSuggestions[m.currentSuggestionIndex]) -} - -// canAcceptSuggestion returns whether there is an acceptable suggestion to -// autocomplete the current value. -func (m *Model) canAcceptSuggestion() bool { - return len(m.matchedSuggestions) > 0 -} - -// updateSuggestions refreshes the list of matching suggestions. -func (m *Model) updateSuggestions() { - if !m.ShowSuggestions { - return - } - - if len(m.value) <= 0 || len(m.suggestions) <= 0 { - m.matchedSuggestions = [][]rune{} - return - } - - matches := [][]rune{} - for _, s := range m.suggestions { - suggestion := string(s) - - if strings.HasPrefix(strings.ToLower(suggestion), strings.ToLower(string(m.value))) { - matches = append(matches, []rune(suggestion)) - } - } - if !reflect.DeepEqual(matches, m.matchedSuggestions) { - m.currentSuggestionIndex = 0 - } - - m.matchedSuggestions = matches -} - -// nextSuggestion selects the next suggestion. -func (m *Model) nextSuggestion() { - m.currentSuggestionIndex = (m.currentSuggestionIndex + 1) - if m.currentSuggestionIndex >= len(m.matchedSuggestions) { - m.currentSuggestionIndex = 0 - } -} - -// previousSuggestion selects the previous suggestion. -func (m *Model) previousSuggestion() { - m.currentSuggestionIndex = (m.currentSuggestionIndex - 1) - if m.currentSuggestionIndex < 0 { - m.currentSuggestionIndex = len(m.matchedSuggestions) - 1 - } -} - -func (m Model) validate(v []rune) error { - if m.Validate != nil { - return m.Validate(string(v)) - } - return nil -} diff --git a/vendor/github.com/evertras/bubble-table/LICENSE b/vendor/github.com/evertras/bubble-table/LICENSE deleted file mode 100644 index 0a30e64a..00000000 --- a/vendor/github.com/evertras/bubble-table/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Brandon Fulljames - -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/evertras/bubble-table/table/border.go b/vendor/github.com/evertras/bubble-table/table/border.go deleted file mode 100644 index 756d4c81..00000000 --- a/vendor/github.com/evertras/bubble-table/table/border.go +++ /dev/null @@ -1,439 +0,0 @@ -package table - -import "github.com/charmbracelet/lipgloss" - -// Border defines the borders in and around the table. -type Border struct { - Top string - Left string - Right string - Bottom string - TopRight string - TopLeft string - BottomRight string - BottomLeft string - - TopJunction string - LeftJunction string - RightJunction string - BottomJunction string - - InnerJunction string - - InnerDivider string - - // Styles for 2x2 tables and larger - styleMultiTopLeft lipgloss.Style - styleMultiTop lipgloss.Style - styleMultiTopRight lipgloss.Style - styleMultiRight lipgloss.Style - styleMultiBottomRight lipgloss.Style - styleMultiBottom lipgloss.Style - styleMultiBottomLeft lipgloss.Style - styleMultiLeft lipgloss.Style - styleMultiInner lipgloss.Style - - // Styles for a single column table - styleSingleColumnTop lipgloss.Style - styleSingleColumnInner lipgloss.Style - styleSingleColumnBottom lipgloss.Style - - // Styles for a single row table - styleSingleRowLeft lipgloss.Style - styleSingleRowInner lipgloss.Style - styleSingleRowRight lipgloss.Style - - // Style for a table with only one cell - styleSingleCell lipgloss.Style - - // Style for the footer - styleFooter lipgloss.Style -} - -var ( - // https://www.w3.org/TR/xml-entity-names/025.html - - borderDefault = Border{ - Top: "━", - Left: "┃", - Right: "┃", - Bottom: "━", - - TopRight: "┓", - TopLeft: "┏", - BottomRight: "┛", - BottomLeft: "┗", - - TopJunction: "┳", - LeftJunction: "┣", - RightJunction: "┫", - BottomJunction: "┻", - InnerJunction: "╋", - - InnerDivider: "┃", - } - - borderRounded = Border{ - Top: "─", - Left: "│", - Right: "│", - Bottom: "─", - - TopRight: "╮", - TopLeft: "╭", - BottomRight: "╯", - BottomLeft: "╰", - - TopJunction: "┬", - LeftJunction: "├", - RightJunction: "┤", - BottomJunction: "┴", - InnerJunction: "┼", - - InnerDivider: "│", - } -) - -func init() { - borderDefault.generateStyles() - borderRounded.generateStyles() -} - -func (b *Border) generateStyles() { - b.generateMultiStyles() - b.generateSingleColumnStyles() - b.generateSingleRowStyles() - b.generateSingleCellStyle() - - // The footer is a single cell with the top taken off... usually. We can - // re-enable the top if needed this way for certain format configurations. - b.styleFooter = b.styleSingleCell.Copy(). - Align(lipgloss.Right). - BorderBottom(true). - BorderRight(true). - BorderLeft(true) -} - -func (b *Border) styleLeftWithFooter(original lipgloss.Style) lipgloss.Style { - border := original.GetBorderStyle() - - border.BottomLeft = b.LeftJunction - - return original.Copy().BorderStyle(border) -} - -func (b *Border) styleRightWithFooter(original lipgloss.Style) lipgloss.Style { - border := original.GetBorderStyle() - - border.BottomRight = b.RightJunction - - return original.Copy().BorderStyle(border) -} - -func (b *Border) styleBothWithFooter(original lipgloss.Style) lipgloss.Style { - border := original.GetBorderStyle() - - border.BottomLeft = b.LeftJunction - border.BottomRight = b.RightJunction - - return original.Copy().BorderStyle(border) -} - -// This function is long, but it's just repetitive... -// -//nolint:funlen -func (b *Border) generateMultiStyles() { - b.styleMultiTopLeft = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - TopLeft: b.TopLeft, - Top: b.Top, - TopRight: b.TopJunction, - Right: b.InnerDivider, - BottomRight: b.InnerJunction, - Bottom: b.Bottom, - BottomLeft: b.LeftJunction, - Left: b.Left, - }, - ) - - b.styleMultiTop = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Top: b.Top, - Right: b.InnerDivider, - Bottom: b.Bottom, - - TopRight: b.TopJunction, - BottomRight: b.InnerJunction, - }, - ).BorderTop(true).BorderBottom(true).BorderRight(true) - - b.styleMultiTopRight = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Top: b.Top, - Right: b.Right, - Bottom: b.Bottom, - - TopRight: b.TopRight, - BottomRight: b.RightJunction, - }, - ).BorderTop(true).BorderBottom(true).BorderRight(true) - - b.styleMultiLeft = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Left: b.Left, - Right: b.InnerDivider, - }, - ).BorderRight(true).BorderLeft(true) - - b.styleMultiRight = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Right: b.Right, - }, - ).BorderRight(true) - - b.styleMultiInner = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Right: b.InnerDivider, - }, - ).BorderRight(true) - - b.styleMultiBottomLeft = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Left: b.Left, - Right: b.InnerDivider, - Bottom: b.Bottom, - - BottomLeft: b.BottomLeft, - BottomRight: b.BottomJunction, - }, - ).BorderLeft(true).BorderBottom(true).BorderRight(true) - - b.styleMultiBottom = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Right: b.InnerDivider, - Bottom: b.Bottom, - - BottomRight: b.BottomJunction, - }, - ).BorderBottom(true).BorderRight(true) - - b.styleMultiBottomRight = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Right: b.Right, - Bottom: b.Bottom, - - BottomRight: b.BottomRight, - }, - ).BorderBottom(true).BorderRight(true) -} - -func (b *Border) generateSingleColumnStyles() { - b.styleSingleColumnTop = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Top: b.Top, - Left: b.Left, - Right: b.Right, - Bottom: b.Bottom, - - TopLeft: b.TopLeft, - TopRight: b.TopRight, - BottomLeft: b.LeftJunction, - BottomRight: b.RightJunction, - }, - ) - - b.styleSingleColumnInner = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Left: b.Left, - Right: b.Right, - }, - ).BorderRight(true).BorderLeft(true) - - b.styleSingleColumnBottom = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Left: b.Left, - Right: b.Right, - Bottom: b.Bottom, - - BottomLeft: b.BottomLeft, - BottomRight: b.BottomRight, - }, - ).BorderRight(true).BorderLeft(true).BorderBottom(true) -} - -func (b *Border) generateSingleRowStyles() { - b.styleSingleRowLeft = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Top: b.Top, - Left: b.Left, - Right: b.InnerDivider, - Bottom: b.Bottom, - - BottomLeft: b.BottomLeft, - BottomRight: b.BottomJunction, - TopRight: b.TopJunction, - TopLeft: b.TopLeft, - }, - ) - - b.styleSingleRowInner = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Top: b.Top, - Right: b.InnerDivider, - Bottom: b.Bottom, - - BottomRight: b.BottomJunction, - TopRight: b.TopJunction, - }, - ).BorderTop(true).BorderBottom(true).BorderRight(true) - - b.styleSingleRowRight = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Top: b.Top, - Right: b.Right, - Bottom: b.Bottom, - - BottomRight: b.BottomRight, - TopRight: b.TopRight, - }, - ).BorderTop(true).BorderBottom(true).BorderRight(true) -} - -func (b *Border) generateSingleCellStyle() { - b.styleSingleCell = lipgloss.NewStyle().BorderStyle( - lipgloss.Border{ - Top: b.Top, - Left: b.Left, - Right: b.Right, - Bottom: b.Bottom, - - BottomLeft: b.BottomLeft, - BottomRight: b.BottomRight, - TopRight: b.TopRight, - TopLeft: b.TopLeft, - }, - ) -} - -// BorderDefault uses the basic square border, useful to reset the border if -// it was changed somehow. -func (m Model) BorderDefault() Model { - // Already generated styles - m.border = borderDefault - - return m -} - -// BorderRounded uses a thin, rounded border. -func (m Model) BorderRounded() Model { - // Already generated styles - m.border = borderRounded - - return m -} - -// Border uses the given border components to render the table. -func (m Model) Border(border Border) Model { - border.generateStyles() - - m.border = border - - return m -} - -type borderStyleRow struct { - left lipgloss.Style - inner lipgloss.Style - right lipgloss.Style -} - -func (b *borderStyleRow) inherit(s lipgloss.Style) { - b.left = b.left.Copy().Inherit(s) - b.inner = b.inner.Copy().Inherit(s) - b.right = b.right.Copy().Inherit(s) -} - -// There's a lot of branches here, but splitting it up further would make it -// harder to follow. So just be careful with comments and make sure it's tested! -// -//nolint:nestif -func (m Model) styleHeaders() borderStyleRow { - hasRows := len(m.GetVisibleRows()) > 0 || m.calculatePadding(0) > 0 - singleColumn := len(m.columns) == 1 - styles := borderStyleRow{} - - // Possible configurations: - // - Single cell - // - Single row - // - Single column - // - Multi - - if singleColumn { - if hasRows { - // Single column - styles.left = m.border.styleSingleColumnTop - styles.inner = styles.left - styles.right = styles.left - } else { - // Single cell - styles.left = m.border.styleSingleCell - styles.inner = styles.left - styles.right = styles.left - - if m.hasFooter() { - styles.left = m.border.styleBothWithFooter(styles.left) - } - } - } else if !hasRows { - // Single row - styles.left = m.border.styleSingleRowLeft - styles.inner = m.border.styleSingleRowInner - styles.right = m.border.styleSingleRowRight - - if m.hasFooter() { - styles.left = m.border.styleLeftWithFooter(styles.left) - styles.right = m.border.styleRightWithFooter(styles.right) - } - } else { - // Multi - styles.left = m.border.styleMultiTopLeft - styles.inner = m.border.styleMultiTop - styles.right = m.border.styleMultiTopRight - } - - styles.inherit(m.headerStyle) - - return styles -} - -func (m Model) styleRows() (inner borderStyleRow, last borderStyleRow) { - if len(m.columns) == 1 { - inner.left = m.border.styleSingleColumnInner - inner.inner = inner.left - inner.right = inner.left - - last.left = m.border.styleSingleColumnBottom - - if m.hasFooter() { - last.left = m.border.styleBothWithFooter(last.left) - } - - last.inner = last.left - last.right = last.left - } else { - inner.left = m.border.styleMultiLeft - inner.inner = m.border.styleMultiInner - inner.right = m.border.styleMultiRight - - last.left = m.border.styleMultiBottomLeft - last.inner = m.border.styleMultiBottom - last.right = m.border.styleMultiBottomRight - - if m.hasFooter() { - last.left = m.border.styleLeftWithFooter(last.left) - last.right = m.border.styleRightWithFooter(last.right) - } - } - - return inner, last -} diff --git a/vendor/github.com/evertras/bubble-table/table/calc.go b/vendor/github.com/evertras/bubble-table/table/calc.go deleted file mode 100644 index 21b7a7d7..00000000 --- a/vendor/github.com/evertras/bubble-table/table/calc.go +++ /dev/null @@ -1,36 +0,0 @@ -package table - -// Keep compatibility with Go 1.21 by re-declaring min. -// -//nolint:predeclared -func min(x, y int) int { - if x < y { - return x - } - - return y -} - -// Keep compatibility with Go 1.21 by re-declaring max. -// -//nolint:predeclared -func max(x, y int) int { - if x > y { - return x - } - - return y -} - -// These var names are fine for this little function -// -//nolint:varnamelen -func gcd(x, y int) int { - if x == 0 { - return y - } else if y == 0 { - return x - } - - return gcd(y%x, x) -} diff --git a/vendor/github.com/evertras/bubble-table/table/cell.go b/vendor/github.com/evertras/bubble-table/table/cell.go deleted file mode 100644 index ac9e5c40..00000000 --- a/vendor/github.com/evertras/bubble-table/table/cell.go +++ /dev/null @@ -1,60 +0,0 @@ -package table - -import "github.com/charmbracelet/lipgloss" - -// StyledCell represents a cell in the table that has a particular style applied. -// The cell style takes highest precedence and will overwrite more general styles -// from the row, column, or table as a whole. This style should be generally -// limited to colors, font style, and alignments - spacing style such as margin -// will break the table format. -type StyledCell struct { - // Data is the content of the cell. - Data any - - // Style is the specific style to apply. This is ignored if StyleFunc is not nil. - Style lipgloss.Style - - // StyleFunc is a function that takes the row/column of the cell and - // returns a lipgloss.Style allowing for dynamic styling based on the cell's - // content or position. Overrides Style if set. - StyleFunc StyledCellFunc -} - -// StyledCellFuncInput is the input to the StyledCellFunc. Sent as a struct -// to allow for future additions without breaking changes. -type StyledCellFuncInput struct { - // Data is the data in the cell. - Data any - - // Column is the column that the cell belongs to. - Column Column - - // Row is the row that the cell belongs to. - Row Row - - // GlobalMetadata is the global table metadata that's been set by WithGlobalMetadata - GlobalMetadata map[string]any -} - -// StyledCellFunc is a function that takes various information about the cell and -// returns a lipgloss.Style allowing for easier dynamic styling based on the cell's -// content or position. -type StyledCellFunc = func(input StyledCellFuncInput) lipgloss.Style - -// NewStyledCell creates an entry that can be set in the row data and show as -// styled with the given style. -func NewStyledCell(data any, style lipgloss.Style) StyledCell { - return StyledCell{ - Data: data, - Style: style, - } -} - -// NewStyledCellWithStyleFunc creates an entry that can be set in the row data and show as -// styled with the given style function. -func NewStyledCellWithStyleFunc(data any, styleFunc StyledCellFunc) StyledCell { - return StyledCell{ - Data: data, - StyleFunc: styleFunc, - } -} diff --git a/vendor/github.com/evertras/bubble-table/table/column.go b/vendor/github.com/evertras/bubble-table/table/column.go deleted file mode 100644 index 44714755..00000000 --- a/vendor/github.com/evertras/bubble-table/table/column.go +++ /dev/null @@ -1,118 +0,0 @@ -package table - -import ( - "github.com/charmbracelet/lipgloss" -) - -// Column is a column in the table. -type Column struct { - title string - key string - width int - - flexFactor int - - filterable bool - style lipgloss.Style - - fmtString string -} - -// NewColumn creates a new fixed-width column with the given information. -func NewColumn(key, title string, width int) Column { - return Column{ - key: key, - title: title, - width: width, - - filterable: false, - } -} - -// NewFlexColumn creates a new flexible width column that tries to fill in the -// total table width. If multiple flex columns exist, each will measure against -// each other depending on their flexFactor. For example, if both have a flexFactor -// of 1, they will have equal width. If one has a flexFactor of 1 and the other -// has a flexFactor of 3, the second will be 3 times larger than the first. You -// must use WithTargetWidth if you have any flex columns, so that the table knows -// how much width it should fill. -func NewFlexColumn(key, title string, flexFactor int) Column { - return Column{ - key: key, - title: title, - - flexFactor: max(flexFactor, 1), - } -} - -// WithStyle applies a style to the column as a whole. -func (c Column) WithStyle(style lipgloss.Style) Column { - c.style = style.Copy().Width(c.width) - - return c -} - -// WithFiltered sets whether the column should be considered for filtering (true) -// or not (false). -func (c Column) WithFiltered(filterable bool) Column { - c.filterable = filterable - - return c -} - -// WithFormatString sets the format string used by fmt.Sprintf to display the data. -// If not set, the default is "%v" for all data types. Intended mainly for -// numeric formatting. -// -// Since data is of the any type, make sure that all data in the column -// is of the expected type or the format may fail. For example, hardcoding '3' -// instead of '3.0' and using '%.2f' will fail because '3' is an integer. -func (c Column) WithFormatString(fmtString string) Column { - c.fmtString = fmtString - - return c -} - -func (c *Column) isFlex() bool { - return c.flexFactor != 0 -} - -// Title returns the title of the column. -func (c Column) Title() string { - return c.title -} - -// Key returns the key of the column. -func (c Column) Key() string { - return c.key -} - -// Width returns the width of the column. -func (c Column) Width() int { - return c.width -} - -// FlexFactor returns the flex factor of the column. -func (c Column) FlexFactor() int { - return c.flexFactor -} - -// IsFlex returns whether the column is a flex column. -func (c Column) IsFlex() bool { - return c.isFlex() -} - -// Filterable returns whether the column is filterable. -func (c Column) Filterable() bool { - return c.filterable -} - -// Style returns the style of the column. -func (c Column) Style() lipgloss.Style { - return c.style -} - -// FmtString returns the format string of the column. -func (c Column) FmtString() string { - return c.fmtString -} diff --git a/vendor/github.com/evertras/bubble-table/table/data.go b/vendor/github.com/evertras/bubble-table/table/data.go deleted file mode 100644 index 901a4f4b..00000000 --- a/vendor/github.com/evertras/bubble-table/table/data.go +++ /dev/null @@ -1,67 +0,0 @@ -package table - -import "time" - -// This is just a bunch of data type checks, so... no linting here -// -//nolint:cyclop -func asInt(data any) (int64, bool) { - switch val := data.(type) { - case int: - return int64(val), true - - case int8: - return int64(val), true - - case int16: - return int64(val), true - - case int32: - return int64(val), true - - case int64: - return val, true - - case uint: - // #nosec: G115 - return int64(val), true - - case uint8: - return int64(val), true - - case uint16: - return int64(val), true - - case uint32: - return int64(val), true - - case uint64: - // #nosec: G115 - return int64(val), true - - case time.Duration: - return int64(val), true - - case StyledCell: - return asInt(val.Data) - } - - return 0, false -} - -func asNumber(data any) (float64, bool) { - switch val := data.(type) { - case float32: - return float64(val), true - - case float64: - return val, true - - case StyledCell: - return asNumber(val.Data) - } - - intVal, isInt := asInt(data) - - return float64(intVal), isInt -} diff --git a/vendor/github.com/evertras/bubble-table/table/dimensions.go b/vendor/github.com/evertras/bubble-table/table/dimensions.go deleted file mode 100644 index 38074616..00000000 --- a/vendor/github.com/evertras/bubble-table/table/dimensions.go +++ /dev/null @@ -1,116 +0,0 @@ -package table - -import ( - "github.com/charmbracelet/lipgloss" -) - -func (m *Model) recalculateWidth() { - if m.targetTotalWidth != 0 { - m.totalWidth = m.targetTotalWidth - } else { - total := 0 - - for _, column := range m.columns { - total += column.width - } - - m.totalWidth = total + len(m.columns) + 1 - } - - updateColumnWidths(m.columns, m.targetTotalWidth) - - m.recalculateLastHorizontalColumn() -} - -// Updates column width in-place. This could be optimized but should be called -// very rarely so we prioritize simplicity over performance here. -func updateColumnWidths(cols []Column, totalWidth int) { - totalFlexWidth := totalWidth - len(cols) - 1 - totalFlexFactor := 0 - flexGCD := 0 - - for index, col := range cols { - if !col.isFlex() { - totalFlexWidth -= col.width - cols[index].style = col.style.Width(col.width) - } else { - totalFlexFactor += col.flexFactor - flexGCD = gcd(flexGCD, col.flexFactor) - } - } - - if totalFlexFactor == 0 { - return - } - - // We use the GCD here because otherwise very large values won't divide - // nicely as ints - totalFlexFactor /= flexGCD - - flexUnit := totalFlexWidth / totalFlexFactor - leftoverWidth := totalFlexWidth % totalFlexFactor - - for index := range cols { - if !cols[index].isFlex() { - continue - } - - width := flexUnit * (cols[index].flexFactor / flexGCD) - - if leftoverWidth > 0 { - width++ - leftoverWidth-- - } - - if index == len(cols)-1 { - width += leftoverWidth - leftoverWidth = 0 - } - - width = max(width, 1) - - cols[index].width = width - - // Take borders into account for the actual style - cols[index].style = cols[index].style.Width(width) - } -} - -func (m *Model) recalculateHeight() { - header := m.renderHeaders() - headerHeight := 1 // Header always has the top border - if m.headerVisible { - headerHeight = lipgloss.Height(header) - } - - footer := m.renderFooter(lipgloss.Width(header), false) - var footerHeight int - if footer != "" { - footerHeight = lipgloss.Height(footer) - } - - m.metaHeight = headerHeight + footerHeight -} - -func (m *Model) calculatePadding(numRows int) int { - if m.minimumHeight == 0 { - return 0 - } - - padding := m.minimumHeight - m.metaHeight - numRows - 1 // additional 1 for bottom border - - if padding == 0 && numRows == 0 { - // This is an edge case where we want to add 1 additional line of height, i.e. - // add a border without an empty row. However, this is not possible, so we need - // to add an extra row which will result in the table being 1 row taller than - // the requested minimum height. - return 1 - } - - if padding < 0 { - // Table is already larger than minimum height, do nothing. - return 0 - } - - return padding -} diff --git a/vendor/github.com/evertras/bubble-table/table/doc.go b/vendor/github.com/evertras/bubble-table/table/doc.go deleted file mode 100644 index 554944af..00000000 --- a/vendor/github.com/evertras/bubble-table/table/doc.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Package table contains a Bubble Tea component for an interactive and customizable -table. - -The simplest useful table can be created with table.New(...).WithRows(...). Row -data should map to the column keys, as shown below. Note that extra data will -simply not be shown, while missing data will be safely blank in the row's cell. - - const ( - // This is not necessary, but recommended to avoid typos - columnKeyName = "name" - columnKeyCount = "count" - ) - - // Define the columns and how they appear - columns := []table.Column{ - table.NewColumn(columnKeyName, "Name", 10), - table.NewColumn(columnKeyCount, "Count", 6), - } - - // Define the data that will be in the table, mapping to the column keys - rows := []table.Row{ - table.NewRow(table.RowData{ - columnKeyName: "Cheeseburger", - columnKeyCount: 3, - }), - table.NewRow(table.RowData{ - columnKeyName: "Fries", - columnKeyCount: 2, - }), - } - - // Create the table - tbl := table.New(columns).WithRows(rows) - - // Use it like any Bubble Tea component in your view - tbl.View() -*/ -package table diff --git a/vendor/github.com/evertras/bubble-table/table/events.go b/vendor/github.com/evertras/bubble-table/table/events.go deleted file mode 100644 index b519623d..00000000 --- a/vendor/github.com/evertras/bubble-table/table/events.go +++ /dev/null @@ -1,60 +0,0 @@ -package table - -// UserEvent is some state change that has occurred due to user input. These will -// ONLY be generated when a user has interacted directly with the table. These -// will NOT be generated when code programmatically changes values in the table. -type UserEvent any - -func (m *Model) appendUserEvent(e UserEvent) { - m.lastUpdateUserEvents = append(m.lastUpdateUserEvents, e) -} - -func (m *Model) clearUserEvents() { - m.lastUpdateUserEvents = nil -} - -// GetLastUpdateUserEvents returns a list of events that happened due to user -// input in the last Update call. This is useful to look for triggers such as -// whether the user moved to a new highlighted row. -func (m *Model) GetLastUpdateUserEvents() []UserEvent { - // Most common case - if len(m.lastUpdateUserEvents) == 0 { - return nil - } - - returned := make([]UserEvent, len(m.lastUpdateUserEvents)) - - // Slightly wasteful but helps guarantee immutability, and this should only - // have data very rarely so this is fine - copy(returned, m.lastUpdateUserEvents) - - return returned -} - -// UserEventHighlightedIndexChanged indicates that the user has scrolled to a new -// row. -type UserEventHighlightedIndexChanged struct { - // PreviousRow is the row that was selected before the change. - PreviousRowIndex int - - // SelectedRow is the row index that is now selected - SelectedRowIndex int -} - -// UserEventRowSelectToggled indicates that the user has either selected or -// deselected a row by toggling the selection. The event contains information -// about which row index was selected and whether it was selected or deselected. -type UserEventRowSelectToggled struct { - RowIndex int - IsSelected bool -} - -// UserEventFilterInputFocused indicates that the user has focused the filter -// text input, so that any other typing will type into the filter field. Only -// activates for the built-in filter text box. -type UserEventFilterInputFocused struct{} - -// UserEventFilterInputUnfocused indicates that the user has unfocused the filter -// text input, which means the user is done typing into the filter field. Only -// activates for the built-in filter text box. -type UserEventFilterInputUnfocused struct{} diff --git a/vendor/github.com/evertras/bubble-table/table/filter.go b/vendor/github.com/evertras/bubble-table/table/filter.go deleted file mode 100644 index 765e5b33..00000000 --- a/vendor/github.com/evertras/bubble-table/table/filter.go +++ /dev/null @@ -1,164 +0,0 @@ -package table - -import ( - "fmt" - "strings" -) - -// FilterFuncInput is the input to a FilterFunc. It's a struct so we can add more things later -// without breaking compatibility. -type FilterFuncInput struct { - // Columns is a list of the columns of the table - Columns []Column - - // Row is the row that's being considered for filtering - Row Row - - // GlobalMetadata is an arbitrary set of metadata from the table set by WithGlobalMetadata - GlobalMetadata map[string]any - - // Filter is the filter string input to consider - Filter string -} - -// FilterFunc takes a FilterFuncInput and returns true if the row should be visible, -// or false if the row should be hidden. -type FilterFunc func(FilterFuncInput) bool - -func (m Model) getFilteredRows(rows []Row) []Row { - filterInputValue := m.filterTextInput.Value() - if !m.filtered || filterInputValue == "" { - return rows - } - - filteredRows := make([]Row, 0) - - for _, row := range rows { - var availableFilterFunc FilterFunc - - if m.filterFunc != nil { - availableFilterFunc = m.filterFunc - } else { - availableFilterFunc = filterFuncContains - } - - if availableFilterFunc(FilterFuncInput{ - Columns: m.columns, - Row: row, - Filter: filterInputValue, - GlobalMetadata: m.metadata, - }) { - filteredRows = append(filteredRows, row) - } - } - - return filteredRows -} - -// filterFuncContains returns a filterFunc that performs case-insensitive -// "contains" matching over all filterable columns in a row. -func filterFuncContains(input FilterFuncInput) bool { - if input.Filter == "" { - return true - } - - checkedAny := false - - filterLower := strings.ToLower(input.Filter) - - for _, column := range input.Columns { - if !column.filterable { - continue - } - - checkedAny = true - - data, ok := input.Row.Data[column.key] - - if !ok { - continue - } - - // Extract internal StyledCell data - switch dataV := data.(type) { - case StyledCell: - data = dataV.Data - } - - var target string - switch dataV := data.(type) { - case string: - target = dataV - - case fmt.Stringer: - target = dataV.String() - - default: - target = fmt.Sprintf("%v", data) - } - - if strings.Contains(strings.ToLower(target), filterLower) { - return true - } - } - - return !checkedAny -} - -// filterFuncFuzzy returns a filterFunc that performs case-insensitive fuzzy -// matching (subsequence) over the concatenation of all filterable column values. -func filterFuncFuzzy(input FilterFuncInput) bool { - filter := strings.TrimSpace(input.Filter) - if filter == "" { - return true - } - - var builder strings.Builder - for _, col := range input.Columns { - if !col.filterable { - continue - } - value, ok := input.Row.Data[col.key] - if !ok { - continue - } - if sc, ok := value.(StyledCell); ok { - value = sc.Data - } - builder.WriteString(fmt.Sprint(value)) // uses Stringer if implemented - builder.WriteByte(' ') - } - - haystack := strings.ToLower(builder.String()) - if haystack == "" { - return false - } - - for _, token := range strings.Fields(strings.ToLower(filter)) { - if !fuzzySubsequenceMatch(haystack, token) { - return false - } - } - - return true -} - -// fuzzySubsequenceMatch returns true if all runes in needle appear in order -// within haystack (not necessarily contiguously). Case must be normalized by caller. -func fuzzySubsequenceMatch(haystack, needle string) bool { - if needle == "" { - return true - } - haystackIndex, needleIndex := 0, 0 - haystackRunes := []rune(haystack) - needleRunes := []rune(needle) - - for haystackIndex < len(haystackRunes) && needleIndex < len(needleRunes) { - if haystackRunes[haystackIndex] == needleRunes[needleIndex] { - needleIndex++ - } - haystackIndex++ - } - - return needleIndex == len(needleRunes) -} diff --git a/vendor/github.com/evertras/bubble-table/table/footer.go b/vendor/github.com/evertras/bubble-table/table/footer.go deleted file mode 100644 index c68709ce..00000000 --- a/vendor/github.com/evertras/bubble-table/table/footer.go +++ /dev/null @@ -1,51 +0,0 @@ -package table - -import ( - "fmt" - "strings" -) - -func (m Model) hasFooter() bool { - return m.footerVisible && (m.staticFooter != "" || m.pageSize != 0 || m.filtered) -} - -func (m Model) renderFooter(width int, includeTop bool) string { - if !m.hasFooter() { - return "" - } - - const borderAdjustment = 2 - - styleFooter := m.baseStyle.Copy().Inherit(m.border.styleFooter).Width(width - borderAdjustment) - - if includeTop { - styleFooter = styleFooter.BorderTop(true) - } - - if m.staticFooter != "" { - return styleFooter.Render(m.staticFooter) - } - - sections := []string{} - - if m.filtered && (m.filterTextInput.Focused() || m.filterTextInput.Value() != "") { - sections = append(sections, m.filterTextInput.View()) - } - - // paged feature enabled - if m.pageSize != 0 { - str := fmt.Sprintf("%d/%d", m.CurrentPage(), m.MaxPages()) - if m.filtered && m.filterTextInput.Focused() { - // Need to apply inline style here in case of filter input cursor, because - // the input cursor resets the style after rendering. Note that Inline(true) - // creates a copy, so it's safe to use here without mutating the underlying - // base style. - str = m.baseStyle.Inline(true).Render(str) - } - sections = append(sections, str) - } - - footerText := strings.Join(sections, " ") - - return styleFooter.Render(footerText) -} diff --git a/vendor/github.com/evertras/bubble-table/table/header.go b/vendor/github.com/evertras/bubble-table/table/header.go deleted file mode 100644 index fdd5ac0b..00000000 --- a/vendor/github.com/evertras/bubble-table/table/header.go +++ /dev/null @@ -1,93 +0,0 @@ -package table - -import "github.com/charmbracelet/lipgloss" - -// This is long and could use some refactoring in the future, but unsure of how -// to pick it apart right now. -// -//nolint:funlen,cyclop -func (m Model) renderHeaders() string { - headerStrings := []string{} - - totalRenderedWidth := 0 - - headerStyles := m.styleHeaders() - - renderHeader := func(column Column, borderStyle lipgloss.Style) string { - borderStyle = borderStyle.Inherit(column.style).Inherit(m.baseStyle) - - headerSection := limitStr(column.title, column.width) - - return borderStyle.Render(headerSection) - } - - for columnIndex, column := range m.columns { - var borderStyle lipgloss.Style - - if m.horizontalScrollOffsetCol > 0 && columnIndex == m.horizontalScrollFreezeColumnsCount { - if columnIndex == 0 { - borderStyle = headerStyles.left.Copy() - } else { - borderStyle = headerStyles.inner.Copy() - } - - rendered := renderHeader(genOverflowColumnLeft(1), borderStyle) - - totalRenderedWidth += lipgloss.Width(rendered) - - headerStrings = append(headerStrings, rendered) - } - - if columnIndex >= m.horizontalScrollFreezeColumnsCount && - columnIndex < m.horizontalScrollOffsetCol+m.horizontalScrollFreezeColumnsCount { - continue - } - - if len(headerStrings) == 0 { - borderStyle = headerStyles.left.Copy() - } else if columnIndex < len(m.columns)-1 { - borderStyle = headerStyles.inner.Copy() - } else { - borderStyle = headerStyles.right.Copy() - } - - rendered := renderHeader(column, borderStyle) - - if m.maxTotalWidth != 0 { - renderedWidth := lipgloss.Width(rendered) - - const ( - borderAdjustment = 1 - overflowColWidth = 2 - ) - - targetWidth := m.maxTotalWidth - overflowColWidth - - if columnIndex == len(m.columns)-1 { - // If this is the last header, we don't need to account for the - // overflow arrow column - targetWidth = m.maxTotalWidth - } - - if totalRenderedWidth+renderedWidth > targetWidth { - overflowWidth := m.maxTotalWidth - totalRenderedWidth - borderAdjustment - overflowStyle := genOverflowStyle(headerStyles.right, overflowWidth) - overflowColumn := genOverflowColumnRight(overflowWidth) - - overflowStr := renderHeader(overflowColumn, overflowStyle) - - headerStrings = append(headerStrings, overflowStr) - - break - } - - totalRenderedWidth += renderedWidth - } - - headerStrings = append(headerStrings, rendered) - } - - headerBlock := lipgloss.JoinHorizontal(lipgloss.Bottom, headerStrings...) - - return headerBlock -} diff --git a/vendor/github.com/evertras/bubble-table/table/keys.go b/vendor/github.com/evertras/bubble-table/table/keys.go deleted file mode 100644 index fe2eccc3..00000000 --- a/vendor/github.com/evertras/bubble-table/table/keys.go +++ /dev/null @@ -1,120 +0,0 @@ -package table - -import "github.com/charmbracelet/bubbles/key" - -// KeyMap defines the keybindings for the table when it's focused. -type KeyMap struct { - RowDown key.Binding - RowUp key.Binding - - RowSelectToggle key.Binding - - PageDown key.Binding - PageUp key.Binding - PageFirst key.Binding - PageLast key.Binding - - // Filter allows the user to start typing and filter the rows. - Filter key.Binding - - // FilterBlur is the key that stops the user's input from typing into the filter. - FilterBlur key.Binding - - // FilterClear will clear the filter while it's blurred. - FilterClear key.Binding - - // ScrollRight will move one column to the right when overflow occurs. - ScrollRight key.Binding - - // ScrollLeft will move one column to the left when overflow occurs. - ScrollLeft key.Binding -} - -// DefaultKeyMap returns a set of sensible defaults for controlling a focused table with help text. -func DefaultKeyMap() KeyMap { - return KeyMap{ - RowDown: key.NewBinding( - key.WithKeys("down", "j"), - key.WithHelp("↓/j", "move down"), - ), - RowUp: key.NewBinding( - key.WithKeys("up", "k"), - key.WithHelp("↑/k", "move up"), - ), - RowSelectToggle: key.NewBinding( - key.WithKeys(" ", "enter"), - key.WithHelp("/enter", "select row"), - ), - PageDown: key.NewBinding( - key.WithKeys("right", "l", "pgdown"), - key.WithHelp("→/h/page down", "next page"), - ), - PageUp: key.NewBinding( - key.WithKeys("left", "h", "pgup"), - key.WithHelp("←/h/page up", "previous page"), - ), - PageFirst: key.NewBinding( - key.WithKeys("home", "g"), - key.WithHelp("home/g", "first page"), - ), - PageLast: key.NewBinding( - key.WithKeys("end", "G"), - key.WithHelp("end/G", "last page"), - ), - Filter: key.NewBinding( - key.WithKeys("/"), - key.WithHelp("/", "filter"), - ), - FilterBlur: key.NewBinding( - key.WithKeys("enter", "esc"), - key.WithHelp("enter/esc", "unfocus"), - ), - FilterClear: key.NewBinding( - key.WithKeys("esc"), - key.WithHelp("esc", "clear filter"), - ), - ScrollRight: key.NewBinding( - key.WithKeys("shift+right"), - key.WithHelp("shift+→", "scroll right"), - ), - ScrollLeft: key.NewBinding( - key.WithKeys("shift+left"), - key.WithHelp("shift+←", "scroll left"), - ), - } -} - -// FullHelp returns a multi row view of all the helpkeys that are defined. Needed to fullfil the 'help.Model' interface. -// Also appends all user defined extra keys to the help. -func (m Model) FullHelp() [][]key.Binding { - keyBinds := [][]key.Binding{ - {m.keyMap.RowDown, m.keyMap.RowUp, m.keyMap.RowSelectToggle}, - {m.keyMap.PageDown, m.keyMap.PageUp, m.keyMap.PageFirst, m.keyMap.PageLast}, - {m.keyMap.Filter, m.keyMap.FilterBlur, m.keyMap.FilterClear, m.keyMap.ScrollRight, m.keyMap.ScrollLeft}, - } - if m.additionalFullHelpKeys != nil { - keyBinds = append(keyBinds, m.additionalFullHelpKeys()) - } - - return keyBinds -} - -// ShortHelp just returns a single row of help views. Needed to fullfil the 'help.Model' interface. -// Also appends all user defined extra keys to the help. -func (m Model) ShortHelp() []key.Binding { - keyBinds := []key.Binding{ - m.keyMap.RowDown, - m.keyMap.RowUp, - m.keyMap.RowSelectToggle, - m.keyMap.PageDown, - m.keyMap.PageUp, - m.keyMap.Filter, - m.keyMap.FilterBlur, - m.keyMap.FilterClear, - } - if m.additionalShortHelpKeys != nil { - keyBinds = append(keyBinds, m.additionalShortHelpKeys()...) - } - - return keyBinds -} diff --git a/vendor/github.com/evertras/bubble-table/table/model.go b/vendor/github.com/evertras/bubble-table/table/model.go deleted file mode 100644 index 33e1458c..00000000 --- a/vendor/github.com/evertras/bubble-table/table/model.go +++ /dev/null @@ -1,148 +0,0 @@ -package table - -import ( - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -const ( - columnKeySelect = "___select___" -) - -var ( - defaultHighlightStyle = lipgloss.NewStyle().Background(lipgloss.Color("#334")) -) - -// Model is the main table model. Create using New(). -type Model struct { - // Data - columns []Column - rows []Row - metadata map[string]any - - // Caches for optimizations - visibleRowCacheUpdated bool - visibleRowCache []Row - - // Shown when data is missing from a row - missingDataIndicator any - - // Interaction - focused bool - keyMap KeyMap - - // Taken from: 'Bubbles/List' - // Additional key mappings for the short and full help views. This allows - // you to add additional key mappings to the help menu without - // re-implementing the help component. Of course, you can also disable the - // list's help component and implement a new one if you need more - // flexibility. - // You have to supply a keybinding like this: - // key.NewBinding( key.WithKeys("shift+left"), key.WithHelp("shift+←", "scroll left")) - // It needs both 'WithKeys' and 'WithHelp' - additionalShortHelpKeys func() []key.Binding - additionalFullHelpKeys func() []key.Binding - - selectableRows bool - rowCursorIndex int - - // Events - lastUpdateUserEvents []UserEvent - - // Styles - baseStyle lipgloss.Style - highlightStyle lipgloss.Style - headerStyle lipgloss.Style - rowStyleFunc func(RowStyleFuncInput) lipgloss.Style - border Border - selectedText string - unselectedText string - - // Header - headerVisible bool - - // Footers - footerVisible bool - staticFooter string - - // Pagination - pageSize int - currentPage int - paginationWrapping bool - - // Sorting, where a stable sort is applied from first element to last so - // that elements are grouped by the later elements. - sortOrder []SortColumn - - // Filter - filtered bool - filterTextInput textinput.Model - filterFunc FilterFunc - - // For flex columns - targetTotalWidth int - - // The maximum total width for overflow/scrolling - maxTotalWidth int - - // Internal cached calculations for reference, may be higher than - // maxTotalWidth. If this is the case, we need to adjust the view - totalWidth int - - // How far to scroll to the right, in columns - horizontalScrollOffsetCol int - - // How many columns to freeze when scrolling horizontally - horizontalScrollFreezeColumnsCount int - - // Calculated maximum column we can scroll to before the last is displayed - maxHorizontalColumnIndex int - - // Minimum total height of the table - minimumHeight int - - // Internal cached calculation, the height of the header and footer - // including borders. Used to determine how many padding rows to add. - metaHeight int - - // If true, the table will be multiline - multiline bool -} - -// New creates a new table ready for further modifications. -func New(columns []Column) Model { - filterInput := textinput.New() - filterInput.Prompt = "/" - model := Model{ - columns: make([]Column, len(columns)), - metadata: make(map[string]any), - highlightStyle: defaultHighlightStyle.Copy(), - border: borderDefault, - headerVisible: true, - footerVisible: true, - keyMap: DefaultKeyMap(), - - selectedText: "[x]", - unselectedText: "[ ]", - - filterTextInput: filterInput, - filterFunc: filterFuncContains, - baseStyle: lipgloss.NewStyle().Align(lipgloss.Right), - - paginationWrapping: true, - } - - // Do a full deep copy to avoid unexpected edits - copy(model.columns, columns) - - model.recalculateWidth() - - return model -} - -// Init initializes the table per the Bubble Tea architecture. -func (m Model) Init() tea.Cmd { - return nil -} diff --git a/vendor/github.com/evertras/bubble-table/table/options.go b/vendor/github.com/evertras/bubble-table/table/options.go deleted file mode 100644 index c35fc1e7..00000000 --- a/vendor/github.com/evertras/bubble-table/table/options.go +++ /dev/null @@ -1,510 +0,0 @@ -package table - -import ( - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/textinput" - "github.com/charmbracelet/lipgloss" -) - -// RowStyleFuncInput is the input to the style function that can -// be applied to each row. This is useful for things like zebra -// striping or other data-based styles. -// -// Note that we use a struct here to allow for future expansion -// while keeping backwards compatibility. -type RowStyleFuncInput struct { - // Index is the index of the row, starting at 0. - Index int - - // Row is the full row data. - Row Row - - // IsHighlighted is true if the row is currently highlighted. - IsHighlighted bool -} - -// WithRowStyleFunc sets a function that can be used to apply a style to each row -// based on the row data. This is useful for things like zebra striping or other -// data-based styles. It can be safely set to nil to remove it later. -// This style is applied after the base style and before individual row styles. -// This will override any HighlightStyle settings. -func (m Model) WithRowStyleFunc(f func(RowStyleFuncInput) lipgloss.Style) Model { - m.rowStyleFunc = f - - return m -} - -// WithHighlightedRow sets the highlighted row to the given index. -func (m Model) WithHighlightedRow(index int) Model { - m.rowCursorIndex = index - - if m.rowCursorIndex >= len(m.GetVisibleRows()) { - m.rowCursorIndex = len(m.GetVisibleRows()) - 1 - } - - if m.rowCursorIndex < 0 { - m.rowCursorIndex = 0 - } - - m.currentPage = m.expectedPageForRowIndex(m.rowCursorIndex) - - return m -} - -// HeaderStyle sets the style to apply to the header text, such as color or bold. -func (m Model) HeaderStyle(style lipgloss.Style) Model { - m.headerStyle = style.Copy() - - return m -} - -// WithRows sets the rows to show as data in the table. -func (m Model) WithRows(rows []Row) Model { - m.rows = rows - m.visibleRowCacheUpdated = false - - if m.rowCursorIndex >= len(m.rows) { - m.rowCursorIndex = len(m.rows) - 1 - } - - if m.rowCursorIndex < 0 { - m.rowCursorIndex = 0 - } - - if m.pageSize != 0 { - maxPage := m.MaxPages() - - // MaxPages is 1-index, currentPage is 0 index - if maxPage <= m.currentPage { - m.pageLast() - } - } - - return m -} - -// WithKeyMap sets the key map to use for controls when focused. -func (m Model) WithKeyMap(keyMap KeyMap) Model { - m.keyMap = keyMap - - return m -} - -// KeyMap returns a copy of the current key map in use. -func (m Model) KeyMap() KeyMap { - return m.keyMap -} - -// SelectableRows sets whether or not rows are selectable. If set, adds a column -// in the front that acts as a checkbox and responds to controls if Focused. -func (m Model) SelectableRows(selectable bool) Model { - m.selectableRows = selectable - - hasSelectColumn := len(m.columns) > 0 && m.columns[0].key == columnKeySelect - - if hasSelectColumn != selectable { - if selectable { - m.columns = append([]Column{ - NewColumn(columnKeySelect, m.selectedText, len([]rune(m.selectedText))), - }, m.columns...) - } else { - m.columns = m.columns[1:] - } - } - - m.recalculateWidth() - - return m -} - -// HighlightedRow returns the full Row that's currently highlighted by the user. -func (m Model) HighlightedRow() Row { - if len(m.GetVisibleRows()) > 0 { - return m.GetVisibleRows()[m.rowCursorIndex] - } - - // TODO: Better way to do this without pointers/nil? Or should it be nil? - return Row{} -} - -// SelectedRows returns all rows that have been set as selected by the user. -func (m Model) SelectedRows() []Row { - selectedRows := []Row{} - - for _, row := range m.GetVisibleRows() { - if row.selected { - selectedRows = append(selectedRows, row) - } - } - - return selectedRows -} - -// HighlightStyle sets a custom style to use when the row is being highlighted -// by the cursor. This should not be used with WithRowStyleFunc. Instead, use -// the IsHighlighted field in the style function. -func (m Model) HighlightStyle(style lipgloss.Style) Model { - m.highlightStyle = style - - return m -} - -// Focused allows the table to show highlighted rows and take in controls of -// up/down/space/etc to let the user navigate the table and interact with it. -func (m Model) Focused(focused bool) Model { - m.focused = focused - - return m -} - -// Filtered allows the table to show rows that match the filter. -func (m Model) Filtered(filtered bool) Model { - m.filtered = filtered - m.visibleRowCacheUpdated = false - - if m.minimumHeight > 0 { - m.recalculateHeight() - } - - return m -} - -// StartFilterTyping focuses the text input to allow user typing to filter. -func (m Model) StartFilterTyping() Model { - m.filterTextInput.Focus() - - return m -} - -// WithStaticFooter adds a footer that only displays the given text. -func (m Model) WithStaticFooter(footer string) Model { - m.staticFooter = footer - - if m.minimumHeight > 0 { - m.recalculateHeight() - } - - return m -} - -// WithPageSize enables pagination using the given page size. This can be called -// again at any point to resize the height of the table. -func (m Model) WithPageSize(pageSize int) Model { - m.pageSize = pageSize - - maxPages := m.MaxPages() - - if m.currentPage >= maxPages { - m.currentPage = maxPages - 1 - } - - if m.minimumHeight > 0 { - m.recalculateHeight() - } - - return m -} - -// WithNoPagination disables pagination in the table. -func (m Model) WithNoPagination() Model { - m.pageSize = 0 - - if m.minimumHeight > 0 { - m.recalculateHeight() - } - - return m -} - -// WithPaginationWrapping sets whether to wrap around from the beginning to the -// end when navigating through pages. Defaults to true. -func (m Model) WithPaginationWrapping(wrapping bool) Model { - m.paginationWrapping = wrapping - - return m -} - -// WithSelectedText describes what text to show when selectable rows are enabled. -// The selectable column header will use the selected text string. -func (m Model) WithSelectedText(unselected, selected string) Model { - m.selectedText = selected - m.unselectedText = unselected - - if len(m.columns) > 0 && m.columns[0].key == columnKeySelect { - m.columns[0] = NewColumn(columnKeySelect, m.selectedText, len([]rune(m.selectedText))) - m.recalculateWidth() - } - - return m -} - -// WithBaseStyle applies a base style as the default for everything in the table. -// This is useful for border colors, default alignment, default color, etc. -func (m Model) WithBaseStyle(style lipgloss.Style) Model { - m.baseStyle = style - - return m -} - -// WithTargetWidth sets the total target width of the table, including borders. -// This only takes effect when using flex columns. When using flex columns, -// columns will stretch to fill out to the total width given here. -func (m Model) WithTargetWidth(totalWidth int) Model { - m.targetTotalWidth = totalWidth - - m.recalculateWidth() - - return m -} - -// WithMinimumHeight sets the minimum total height of the table, including borders. -func (m Model) WithMinimumHeight(minimumHeight int) Model { - m.minimumHeight = minimumHeight - - m.recalculateHeight() - - return m -} - -// PageDown goes to the next page of a paginated table, wrapping to the first -// page if the table is already on the last page. -func (m Model) PageDown() Model { - m.pageDown() - - return m -} - -// PageUp goes to the previous page of a paginated table, wrapping to the -// last page if the table is already on the first page. -func (m Model) PageUp() Model { - m.pageUp() - - return m -} - -// PageLast goes to the last page of a paginated table. -func (m Model) PageLast() Model { - m.pageLast() - - return m -} - -// PageFirst goes to the first page of a paginated table. -func (m Model) PageFirst() Model { - m.pageFirst() - - return m -} - -// WithCurrentPage sets the current page (1 as the first page) of a paginated -// table, bounded to the total number of pages. The current selected row will -// be set to the top row of the page if the page changed. -func (m Model) WithCurrentPage(currentPage int) Model { - if m.pageSize == 0 || currentPage == m.CurrentPage() { - return m - } - if currentPage < 1 { - currentPage = 1 - } else { - maxPages := m.MaxPages() - - if currentPage > maxPages { - currentPage = maxPages - } - } - m.currentPage = currentPage - 1 - m.rowCursorIndex = m.currentPage * m.pageSize - - return m -} - -// WithColumns sets the visible columns for the table, so that columns can be -// added/removed/resized or headers rewritten. -func (m Model) WithColumns(columns []Column) Model { - // Deep copy to avoid edits - m.columns = make([]Column, len(columns)) - copy(m.columns, columns) - - m.recalculateWidth() - - if m.selectableRows { - // Re-add the selectable column - m = m.SelectableRows(true) - } - - return m -} - -// WithFilterInput makes the table use the provided text input bubble for -// filtering rather than using the built-in default. This allows for external -// text input controls to be used. -func (m Model) WithFilterInput(input textinput.Model) Model { - if m.filterTextInput.Value() != input.Value() { - m.pageFirst() - } - - m.filterTextInput = input - m.visibleRowCacheUpdated = false - - return m -} - -// WithFilterInputValue sets the filter value to the given string, immediately -// applying it as if the user had typed it in. Useful for external filter inputs -// that are not necessarily a text input. -func (m Model) WithFilterInputValue(value string) Model { - if m.filterTextInput.Value() != value { - m.pageFirst() - } - - m.filterTextInput.SetValue(value) - m.filterTextInput.Blur() - m.visibleRowCacheUpdated = false - - return m -} - -// WithFilterFunc adds a filter function to the model. If the function returns -// true, the row will be included in the filtered results. If the function -// is nil, the function won't be used and instead the default filtering will be applied, -// if any. -func (m Model) WithFilterFunc(shouldInclude FilterFunc) Model { - m.filterFunc = shouldInclude - - m.visibleRowCacheUpdated = false - - return m -} - -// WithFuzzyFilter enables fuzzy filtering for the table. -func (m Model) WithFuzzyFilter() Model { - return m.WithFilterFunc(filterFuncFuzzy) -} - -// WithFooterVisibility sets the visibility of the footer. -func (m Model) WithFooterVisibility(visibility bool) Model { - m.footerVisible = visibility - - if m.minimumHeight > 0 { - m.recalculateHeight() - } - - return m -} - -// WithHeaderVisibility sets the visibility of the header. -func (m Model) WithHeaderVisibility(visibility bool) Model { - m.headerVisible = visibility - - if m.minimumHeight > 0 { - m.recalculateHeight() - } - - return m -} - -// WithMaxTotalWidth sets the maximum total width that the table should render. -// If this width is exceeded by either the target width or by the total width -// of all the columns (including borders!), anything extra will be treated as -// overflow and horizontal scrolling will be enabled to see the rest. -func (m Model) WithMaxTotalWidth(maxTotalWidth int) Model { - m.maxTotalWidth = maxTotalWidth - - m.recalculateWidth() - - return m -} - -// WithHorizontalFreezeColumnCount freezes the given number of columns to the -// left side. This is useful for things like ID or Name columns that should -// always be visible even when scrolling. -func (m Model) WithHorizontalFreezeColumnCount(columnsToFreeze int) Model { - m.horizontalScrollFreezeColumnsCount = columnsToFreeze - - m.recalculateWidth() - - return m -} - -// ScrollRight moves one column to the right. Use with WithMaxTotalWidth. -func (m Model) ScrollRight() Model { - m.scrollRight() - - return m -} - -// ScrollLeft moves one column to the left. Use with WithMaxTotalWidth. -func (m Model) ScrollLeft() Model { - m.scrollLeft() - - return m -} - -// WithMissingDataIndicator sets an indicator to use when data for a column is -// not found in a given row. Note that this is for completely missing data, -// an empty string or other zero value that is explicitly set is not considered -// to be missing. -func (m Model) WithMissingDataIndicator(str string) Model { - m.missingDataIndicator = str - - return m -} - -// WithMissingDataIndicatorStyled sets a styled indicator to use when data for -// a column is not found in a given row. Note that this is for completely -// missing data, an empty string or other zero value that is explicitly set is -// not considered to be missing. -func (m Model) WithMissingDataIndicatorStyled(styled StyledCell) Model { - m.missingDataIndicator = styled - - return m -} - -// WithAllRowsDeselected deselects any rows that are currently selected. -func (m Model) WithAllRowsDeselected() Model { - rows := m.GetVisibleRows() - - for i, row := range rows { - if row.selected { - rows[i] = row.Selected(false) - } - } - - m.rows = rows - - return m -} - -// WithMultiline sets whether or not to wrap text in cells to multiple lines. -func (m Model) WithMultiline(multiline bool) Model { - m.multiline = multiline - - return m -} - -// WithAdditionalShortHelpKeys enables you to add more keybindings to the 'short help' view. -func (m Model) WithAdditionalShortHelpKeys(keys []key.Binding) Model { - m.additionalShortHelpKeys = func() []key.Binding { - return keys - } - - return m -} - -// WithAdditionalFullHelpKeys enables you to add more keybindings to the 'full help' view. -func (m Model) WithAdditionalFullHelpKeys(keys []key.Binding) Model { - m.additionalFullHelpKeys = func() []key.Binding { - return keys - } - - return m -} - -// WithGlobalMetadata applies the given metadata to the table. This metadata is passed to -// some functions in FilterFuncInput and StyleFuncInput to enable more advanced decisions, -// such as setting some global theme variable to reference, etc. Has no effect otherwise. -func (m Model) WithGlobalMetadata(metadata map[string]any) Model { - m.metadata = metadata - - return m -} diff --git a/vendor/github.com/evertras/bubble-table/table/overflow.go b/vendor/github.com/evertras/bubble-table/table/overflow.go deleted file mode 100644 index 19c5b0aa..00000000 --- a/vendor/github.com/evertras/bubble-table/table/overflow.go +++ /dev/null @@ -1,18 +0,0 @@ -package table - -import "github.com/charmbracelet/lipgloss" - -const columnKeyOverflowRight = "___overflow_r___" -const columnKeyOverflowLeft = "___overflow_l__" - -func genOverflowStyle(base lipgloss.Style, width int) lipgloss.Style { - return base.Width(width).Align(lipgloss.Right) -} - -func genOverflowColumnRight(width int) Column { - return NewColumn(columnKeyOverflowRight, ">", width) -} - -func genOverflowColumnLeft(width int) Column { - return NewColumn(columnKeyOverflowLeft, "<", width) -} diff --git a/vendor/github.com/evertras/bubble-table/table/pagination.go b/vendor/github.com/evertras/bubble-table/table/pagination.go deleted file mode 100644 index 6fce9b51..00000000 --- a/vendor/github.com/evertras/bubble-table/table/pagination.go +++ /dev/null @@ -1,112 +0,0 @@ -package table - -// PageSize returns the current page size for the table, or 0 if there is no -// pagination enabled. -func (m *Model) PageSize() int { - return m.pageSize -} - -// CurrentPage returns the current page that the table is on, starting from an -// index of 1. -func (m *Model) CurrentPage() int { - return m.currentPage + 1 -} - -// MaxPages returns the maximum number of pages that are visible. -func (m *Model) MaxPages() int { - totalRows := len(m.GetVisibleRows()) - - if m.pageSize == 0 || totalRows == 0 { - return 1 - } - - return (totalRows-1)/m.pageSize + 1 -} - -// TotalRows returns the current total row count of the table. If the table is -// paginated, this is the total number of rows across all pages. -func (m *Model) TotalRows() int { - return len(m.GetVisibleRows()) -} - -// VisibleIndices returns the current visible rows by their 0 based index. -// Useful for custom pagination footers. -func (m *Model) VisibleIndices() (start, end int) { - totalRows := len(m.GetVisibleRows()) - - if m.pageSize == 0 { - start = 0 - end = totalRows - 1 - - return start, end - } - - start = m.pageSize * m.currentPage - end = start + m.pageSize - 1 - - if end >= totalRows { - end = totalRows - 1 - } - - return start, end -} - -func (m *Model) pageDown() { - if m.pageSize == 0 || len(m.GetVisibleRows()) <= m.pageSize { - return - } - - m.currentPage++ - - maxPageIndex := m.MaxPages() - 1 - - if m.currentPage > maxPageIndex { - if m.paginationWrapping { - m.currentPage = 0 - } else { - m.currentPage = maxPageIndex - } - } - - m.rowCursorIndex = m.currentPage * m.pageSize -} - -func (m *Model) pageUp() { - if m.pageSize == 0 || len(m.GetVisibleRows()) <= m.pageSize { - return - } - - m.currentPage-- - - maxPageIndex := m.MaxPages() - 1 - - if m.currentPage < 0 { - if m.paginationWrapping { - m.currentPage = maxPageIndex - } else { - m.currentPage = 0 - } - } - - m.rowCursorIndex = m.currentPage * m.pageSize -} - -func (m *Model) pageFirst() { - m.currentPage = 0 - m.rowCursorIndex = 0 -} - -func (m *Model) pageLast() { - m.currentPage = m.MaxPages() - 1 - m.rowCursorIndex = m.currentPage * m.pageSize -} - -func (m *Model) expectedPageForRowIndex(rowIndex int) int { - if m.pageSize == 0 { - return 0 - } - - expectedPage := rowIndex / m.pageSize - - return expectedPage -} diff --git a/vendor/github.com/evertras/bubble-table/table/query.go b/vendor/github.com/evertras/bubble-table/table/query.go deleted file mode 100644 index 79da50f0..00000000 --- a/vendor/github.com/evertras/bubble-table/table/query.go +++ /dev/null @@ -1,96 +0,0 @@ -package table - -// GetColumnSorting returns the current sorting rules for the table as a list of -// SortColumns, which are applied from first to last. This means that data will -// be grouped by the later elements in the list. The returned list is a copy -// and modifications will have no effect. -func (m *Model) GetColumnSorting() []SortColumn { - c := make([]SortColumn, len(m.sortOrder)) - - copy(c, m.sortOrder) - - return c -} - -// GetCanFilter returns true if the table enables filtering at all. This does -// not say whether a filter is currently active, only that the feature is enabled. -func (m *Model) GetCanFilter() bool { - return m.filtered -} - -// GetIsFilterActive returns true if the table is currently being filtered. This -// does not say whether the table CAN be filtered, only whether or not a filter -// is actually currently being applied. -func (m *Model) GetIsFilterActive() bool { - return m.filterTextInput.Value() != "" -} - -// GetIsFilterInputFocused returns true if the table's built-in filter input is -// currently focused. -func (m *Model) GetIsFilterInputFocused() bool { - return m.filterTextInput.Focused() -} - -// GetCurrentFilter returns the current filter text being applied, or an empty -// string if none is applied. -func (m *Model) GetCurrentFilter() string { - return m.filterTextInput.Value() -} - -// GetVisibleRows returns sorted and filtered rows. -func (m *Model) GetVisibleRows() []Row { - if m.visibleRowCacheUpdated { - return m.visibleRowCache - } - - rows := make([]Row, len(m.rows)) - copy(rows, m.rows) - if m.filtered { - rows = m.getFilteredRows(rows) - } - rows = getSortedRows(m.sortOrder, rows) - - m.visibleRowCache = rows - m.visibleRowCacheUpdated = true - - return rows -} - -// GetHighlightedRowIndex returns the index of the Row that's currently highlighted -// by the user. -func (m *Model) GetHighlightedRowIndex() int { - return m.rowCursorIndex -} - -// GetFocused returns whether or not the table is focused and is receiving inputs. -func (m *Model) GetFocused() bool { - return m.focused -} - -// GetHorizontalScrollColumnOffset returns how many columns to the right the table -// has been scrolled. 0 means the table is all the way to the left, which is -// the starting default. -func (m *Model) GetHorizontalScrollColumnOffset() int { - return m.horizontalScrollOffsetCol -} - -// GetHeaderVisibility returns true if the header has been set to visible (default) -// or false if the header has been set to hidden. -func (m *Model) GetHeaderVisibility() bool { - return m.headerVisible -} - -// GetFooterVisibility returns true if the footer has been set to -// visible (default) or false if the footer has been set to hidden. -// Note that even if the footer is visible it will only be rendered if -// it has contents. -func (m *Model) GetFooterVisibility() bool { - return m.footerVisible -} - -// GetPaginationWrapping returns true if pagination wrapping is enabled, or false -// if disabled. If disabled, navigating through pages will stop at the first -// and last pages. -func (m *Model) GetPaginationWrapping() bool { - return m.paginationWrapping -} diff --git a/vendor/github.com/evertras/bubble-table/table/row.go b/vendor/github.com/evertras/bubble-table/table/row.go deleted file mode 100644 index b9a5d6bb..00000000 --- a/vendor/github.com/evertras/bubble-table/table/row.go +++ /dev/null @@ -1,252 +0,0 @@ -package table - -import ( - "fmt" - "sync/atomic" - - "github.com/charmbracelet/lipgloss" - "github.com/muesli/reflow/wordwrap" -) - -// RowData is a map of string column keys to arbitrary data. Data with a key -// that matches a column key will be displayed. Data with a key that does not -// match a column key will not be displayed, but will remain attached to the Row. -// This can be useful for attaching hidden metadata for future reference when -// retrieving rows. -type RowData map[string]any - -// Row represents a row in the table with some data keyed to the table columns> -// Can have a style applied to it such as color/bold. Create using NewRow(). -type Row struct { - Style lipgloss.Style - Data RowData - - selected bool - - // id is an internal unique ID to match rows after they're copied - id uint32 -} - -var lastRowID uint32 = 1 - -// NewRow creates a new row and copies the given row data. -func NewRow(data RowData) Row { - row := Row{ - Data: make(map[string]any), - id: lastRowID, - } - - atomic.AddUint32(&lastRowID, 1) - - for key, val := range data { - // Doesn't deep copy val, but close enough for now... - row.Data[key] = val - } - - return row -} - -// WithStyle uses the given style for the text in the row. -func (r Row) WithStyle(style lipgloss.Style) Row { - r.Style = style.Copy() - - return r -} - -//nolint:cyclop,funlen // Breaking this up will be more complicated than it's worth for now -func (m Model) renderRowColumnData(row Row, column Column, rowStyle lipgloss.Style, borderStyle lipgloss.Style) string { - cellStyle := rowStyle.Copy().Inherit(column.style).Inherit(m.baseStyle) - - var str string - - switch column.key { - case columnKeySelect: - if row.selected { - str = m.selectedText - } else { - str = m.unselectedText - } - case columnKeyOverflowRight: - cellStyle = cellStyle.Align(lipgloss.Right) - str = ">" - case columnKeyOverflowLeft: - str = "<" - default: - fmtString := "%v" - - var data any - - if entry, exists := row.Data[column.key]; exists { - data = entry - - if column.fmtString != "" { - fmtString = column.fmtString - } - } else if m.missingDataIndicator != nil { - data = m.missingDataIndicator - } else { - data = "" - } - - switch entry := data.(type) { - case StyledCell: - str = fmt.Sprintf(fmtString, entry.Data) - - if entry.StyleFunc != nil { - cellStyle = entry.StyleFunc(StyledCellFuncInput{ - Column: column, - Data: entry.Data, - Row: row, - GlobalMetadata: m.metadata, - }).Copy().Inherit(cellStyle) - } else { - cellStyle = entry.Style.Copy().Inherit(cellStyle) - } - default: - str = fmt.Sprintf(fmtString, entry) - } - } - - if m.multiline { - str = wordwrap.String(str, column.width) - cellStyle = cellStyle.Align(lipgloss.Top) - } else { - str = limitStr(str, column.width) - } - - cellStyle = cellStyle.Inherit(borderStyle) - cellStr := cellStyle.Render(str) - - return cellStr -} - -func (m Model) renderRow(rowIndex int, last bool) string { - row := m.GetVisibleRows()[rowIndex] - highlighted := rowIndex == m.rowCursorIndex - - rowStyle := row.Style.Copy() - - if m.rowStyleFunc != nil { - styleResult := m.rowStyleFunc(RowStyleFuncInput{ - Index: rowIndex, - Row: row, - IsHighlighted: m.focused && highlighted, - }) - - rowStyle = rowStyle.Inherit(styleResult) - } else if m.focused && highlighted { - rowStyle = rowStyle.Inherit(m.highlightStyle) - } - - return m.renderRowData(row, rowStyle, last) -} - -func (m Model) renderBlankRow(last bool) string { - return m.renderRowData(NewRow(nil), lipgloss.NewStyle(), last) -} - -// This is long and could use some refactoring in the future, but not quite sure -// how to pick it apart yet. -// -//nolint:funlen, cyclop -func (m Model) renderRowData(row Row, rowStyle lipgloss.Style, last bool) string { - numColumns := len(m.columns) - - columnStrings := []string{} - totalRenderedWidth := 0 - - stylesInner, stylesLast := m.styleRows() - - maxCellHeight := 1 - if m.multiline { - for _, column := range m.columns { - cellStr := m.renderRowColumnData(row, column, rowStyle, lipgloss.NewStyle()) - maxCellHeight = max(maxCellHeight, lipgloss.Height(cellStr)) - } - } - - for columnIndex, column := range m.columns { - var borderStyle lipgloss.Style - var rowStyles borderStyleRow - - if !last { - rowStyles = stylesInner - } else { - rowStyles = stylesLast - } - rowStyle = rowStyle.Copy().Height(maxCellHeight) - - if m.horizontalScrollOffsetCol > 0 && columnIndex == m.horizontalScrollFreezeColumnsCount { - var borderStyle lipgloss.Style - - if columnIndex == 0 { - borderStyle = rowStyles.left.Copy() - } else { - borderStyle = rowStyles.inner.Copy() - } - - rendered := m.renderRowColumnData(row, genOverflowColumnLeft(1), rowStyle, borderStyle) - - totalRenderedWidth += lipgloss.Width(rendered) - - columnStrings = append(columnStrings, rendered) - } - - if columnIndex >= m.horizontalScrollFreezeColumnsCount && - columnIndex < m.horizontalScrollOffsetCol+m.horizontalScrollFreezeColumnsCount { - continue - } - - if len(columnStrings) == 0 { - borderStyle = rowStyles.left - } else if columnIndex < numColumns-1 { - borderStyle = rowStyles.inner - } else { - borderStyle = rowStyles.right - } - - cellStr := m.renderRowColumnData(row, column, rowStyle, borderStyle) - - if m.maxTotalWidth != 0 { - renderedWidth := lipgloss.Width(cellStr) - - const ( - borderAdjustment = 1 - overflowColWidth = 2 - ) - - targetWidth := m.maxTotalWidth - overflowColWidth - - if columnIndex == len(m.columns)-1 { - // If this is the last header, we don't need to account for the - // overflow arrow column - targetWidth = m.maxTotalWidth - } - - if totalRenderedWidth+renderedWidth > targetWidth { - overflowWidth := m.maxTotalWidth - totalRenderedWidth - borderAdjustment - overflowStyle := genOverflowStyle(rowStyles.right, overflowWidth) - overflowColumn := genOverflowColumnRight(overflowWidth) - overflowStr := m.renderRowColumnData(row, overflowColumn, rowStyle, overflowStyle) - - columnStrings = append(columnStrings, overflowStr) - - break - } - - totalRenderedWidth += renderedWidth - } - - columnStrings = append(columnStrings, cellStr) - } - - return lipgloss.JoinHorizontal(lipgloss.Bottom, columnStrings...) -} - -// Selected returns a copy of the row that's set to be selected or deselected. -// The old row is not changed in-place. -func (r Row) Selected(selected bool) Row { - r.selected = selected - - return r -} diff --git a/vendor/github.com/evertras/bubble-table/table/scrolling.go b/vendor/github.com/evertras/bubble-table/table/scrolling.go deleted file mode 100644 index 3ee3256c..00000000 --- a/vendor/github.com/evertras/bubble-table/table/scrolling.go +++ /dev/null @@ -1,50 +0,0 @@ -package table - -func (m *Model) scrollRight() { - if m.horizontalScrollOffsetCol < m.maxHorizontalColumnIndex { - m.horizontalScrollOffsetCol++ - } -} - -func (m *Model) scrollLeft() { - if m.horizontalScrollOffsetCol > 0 { - m.horizontalScrollOffsetCol-- - } -} - -func (m *Model) recalculateLastHorizontalColumn() { - if m.horizontalScrollFreezeColumnsCount >= len(m.columns) { - m.maxHorizontalColumnIndex = 0 - - return - } - - if m.totalWidth <= m.maxTotalWidth { - m.maxHorizontalColumnIndex = 0 - - return - } - - const ( - leftOverflowWidth = 2 - borderAdjustment = 1 - ) - - // Always have left border - visibleWidth := borderAdjustment + leftOverflowWidth - - for i := 0; i < m.horizontalScrollFreezeColumnsCount; i++ { - visibleWidth += m.columns[i].width + borderAdjustment - } - - m.maxHorizontalColumnIndex = len(m.columns) - 1 - - // Work backwards from the right - for i := len(m.columns) - 1; i >= m.horizontalScrollFreezeColumnsCount && visibleWidth <= m.maxTotalWidth; i-- { - visibleWidth += m.columns[i].width + borderAdjustment - - if visibleWidth <= m.maxTotalWidth { - m.maxHorizontalColumnIndex = i - m.horizontalScrollFreezeColumnsCount - } - } -} diff --git a/vendor/github.com/evertras/bubble-table/table/sort.go b/vendor/github.com/evertras/bubble-table/table/sort.go deleted file mode 100644 index 9a282cbd..00000000 --- a/vendor/github.com/evertras/bubble-table/table/sort.go +++ /dev/null @@ -1,178 +0,0 @@ -package table - -import ( - "fmt" - "sort" -) - -// SortDirection indicates whether a column should sort by ascending or descending. -type SortDirection int - -const ( - // SortDirectionAsc indicates the column should be in ascending order. - SortDirectionAsc SortDirection = iota - - // SortDirectionDesc indicates the column should be in descending order. - SortDirectionDesc -) - -// SortColumn describes which column should be sorted and how. -type SortColumn struct { - ColumnKey string - Direction SortDirection -} - -// SortByAsc sets the main sorting column to the given key, in ascending order. -// If a previous sort was used, it is replaced by the given column each time -// this function is called. Values are sorted as numbers if possible, or just -// as simple string comparisons if not numbers. -func (m Model) SortByAsc(columnKey string) Model { - m.sortOrder = []SortColumn{ - { - ColumnKey: columnKey, - Direction: SortDirectionAsc, - }, - } - - m.visibleRowCacheUpdated = false - - return m -} - -// SortByDesc sets the main sorting column to the given key, in descending order. -// If a previous sort was used, it is replaced by the given column each time -// this function is called. Values are sorted as numbers if possible, or just -// as simple string comparisons if not numbers. -func (m Model) SortByDesc(columnKey string) Model { - m.sortOrder = []SortColumn{ - { - ColumnKey: columnKey, - Direction: SortDirectionDesc, - }, - } - - m.visibleRowCacheUpdated = false - - return m -} - -// ThenSortByAsc provides a secondary sort after the first, in ascending order. -// Can be chained multiple times, applying to smaller subgroups each time. -func (m Model) ThenSortByAsc(columnKey string) Model { - m.sortOrder = append([]SortColumn{ - { - ColumnKey: columnKey, - Direction: SortDirectionAsc, - }, - }, m.sortOrder...) - - m.visibleRowCacheUpdated = false - - return m -} - -// ThenSortByDesc provides a secondary sort after the first, in descending order. -// Can be chained multiple times, applying to smaller subgroups each time. -func (m Model) ThenSortByDesc(columnKey string) Model { - m.sortOrder = append([]SortColumn{ - { - ColumnKey: columnKey, - Direction: SortDirectionDesc, - }, - }, m.sortOrder...) - - m.visibleRowCacheUpdated = false - - return m -} - -type sortableTable struct { - rows []Row - byColumn SortColumn -} - -func (s *sortableTable) Len() int { - return len(s.rows) -} - -func (s *sortableTable) Swap(i, j int) { - old := s.rows[i] - s.rows[i] = s.rows[j] - s.rows[j] = old -} - -func (s *sortableTable) extractString(i int, column string) string { - iData, exists := s.rows[i].Data[column] - - if !exists { - return "" - } - - switch iData := iData.(type) { - case StyledCell: - return fmt.Sprintf("%v", iData.Data) - - case string: - return iData - - default: - return fmt.Sprintf("%v", iData) - } -} - -func (s *sortableTable) extractNumber(i int, column string) (float64, bool) { - iData, exists := s.rows[i].Data[column] - - if !exists { - return 0, false - } - - return asNumber(iData) -} - -func (s *sortableTable) Less(first, second int) bool { - firstNum, firstNumIsValid := s.extractNumber(first, s.byColumn.ColumnKey) - secondNum, secondNumIsValid := s.extractNumber(second, s.byColumn.ColumnKey) - - if firstNumIsValid && secondNumIsValid { - if s.byColumn.Direction == SortDirectionAsc { - return firstNum < secondNum - } - - return firstNum > secondNum - } - - firstVal := s.extractString(first, s.byColumn.ColumnKey) - secondVal := s.extractString(second, s.byColumn.ColumnKey) - - if s.byColumn.Direction == SortDirectionAsc { - return firstVal < secondVal - } - - return firstVal > secondVal -} - -func getSortedRows(sortOrder []SortColumn, rows []Row) []Row { - var sortedRows []Row - if len(sortOrder) == 0 { - sortedRows = rows - - return sortedRows - } - - sortedRows = make([]Row, len(rows)) - copy(sortedRows, rows) - - for _, byColumn := range sortOrder { - sorted := &sortableTable{ - rows: sortedRows, - byColumn: byColumn, - } - - sort.Stable(sorted) - - sortedRows = sorted.rows - } - - return sortedRows -} diff --git a/vendor/github.com/evertras/bubble-table/table/strlimit.go b/vendor/github.com/evertras/bubble-table/table/strlimit.go deleted file mode 100644 index 9889d831..00000000 --- a/vendor/github.com/evertras/bubble-table/table/strlimit.go +++ /dev/null @@ -1,26 +0,0 @@ -package table - -import ( - "strings" - - "github.com/muesli/reflow/ansi" - "github.com/muesli/reflow/truncate" -) - -func limitStr(str string, maxLen int) string { - if maxLen == 0 { - return "" - } - - newLineIndex := strings.Index(str, "\n") - if newLineIndex > -1 { - str = str[:newLineIndex] + "…" - } - - if ansi.PrintableRuneWidth(str) > maxLen { - // #nosec: G115 - return truncate.StringWithTail(str, uint(maxLen), "…") - } - - return str -} diff --git a/vendor/github.com/evertras/bubble-table/table/update.go b/vendor/github.com/evertras/bubble-table/table/update.go deleted file mode 100644 index 198a57d2..00000000 --- a/vendor/github.com/evertras/bubble-table/table/update.go +++ /dev/null @@ -1,154 +0,0 @@ -package table - -import ( - "github.com/charmbracelet/bubbles/key" - tea "github.com/charmbracelet/bubbletea" -) - -func (m *Model) moveHighlightUp() { - m.rowCursorIndex-- - - if m.rowCursorIndex < 0 { - m.rowCursorIndex = len(m.GetVisibleRows()) - 1 - } - - m.currentPage = m.expectedPageForRowIndex(m.rowCursorIndex) -} - -func (m *Model) moveHighlightDown() { - m.rowCursorIndex++ - - if m.rowCursorIndex >= len(m.GetVisibleRows()) { - m.rowCursorIndex = 0 - } - - m.currentPage = m.expectedPageForRowIndex(m.rowCursorIndex) -} - -func (m *Model) toggleSelect() { - if !m.selectableRows || len(m.GetVisibleRows()) == 0 { - return - } - - rows := m.GetVisibleRows() - - rowID := rows[m.rowCursorIndex].id - - currentSelectedState := false - - for i := range m.rows { - if m.rows[i].id == rowID { - currentSelectedState = m.rows[i].selected - m.rows[i].selected = !m.rows[i].selected - } - } - - m.visibleRowCacheUpdated = false - - m.appendUserEvent(UserEventRowSelectToggled{ - RowIndex: m.rowCursorIndex, - IsSelected: !currentSelectedState, - }) -} - -func (m Model) updateFilterTextInput(msg tea.Msg) (Model, tea.Cmd) { - var cmd tea.Cmd - switch msg := msg.(type) { - case tea.KeyMsg: - if key.Matches(msg, m.keyMap.FilterBlur) { - m.filterTextInput.Blur() - } - } - m.filterTextInput, cmd = m.filterTextInput.Update(msg) - m.pageFirst() - m.visibleRowCacheUpdated = false - - return m, cmd -} - -// This is a series of Matches tests with minimal logic -// -//nolint:cyclop -func (m *Model) handleKeypress(msg tea.KeyMsg) { - previousRowIndex := m.rowCursorIndex - - if key.Matches(msg, m.keyMap.RowDown) { - m.moveHighlightDown() - } - - if key.Matches(msg, m.keyMap.RowUp) { - m.moveHighlightUp() - } - - if key.Matches(msg, m.keyMap.RowSelectToggle) { - m.toggleSelect() - } - - if key.Matches(msg, m.keyMap.PageDown) { - m.pageDown() - } - - if key.Matches(msg, m.keyMap.PageUp) { - m.pageUp() - } - - if key.Matches(msg, m.keyMap.PageFirst) { - m.pageFirst() - } - - if key.Matches(msg, m.keyMap.PageLast) { - m.pageLast() - } - - if key.Matches(msg, m.keyMap.Filter) { - m.filterTextInput.Focus() - m.appendUserEvent(UserEventFilterInputFocused{}) - } - - if key.Matches(msg, m.keyMap.FilterClear) { - m.visibleRowCacheUpdated = false - m.filterTextInput.Reset() - } - - if key.Matches(msg, m.keyMap.ScrollRight) { - m.scrollRight() - } - - if key.Matches(msg, m.keyMap.ScrollLeft) { - m.scrollLeft() - } - - if m.rowCursorIndex != previousRowIndex { - m.appendUserEvent(UserEventHighlightedIndexChanged{ - PreviousRowIndex: previousRowIndex, - SelectedRowIndex: m.rowCursorIndex, - }) - } -} - -// Update responds to input from the user or other messages from Bubble Tea. -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { - m.clearUserEvents() - - if !m.focused { - return m, nil - } - - if m.filterTextInput.Focused() { - var cmd tea.Cmd - m, cmd = m.updateFilterTextInput(msg) - - if !m.filterTextInput.Focused() { - m.appendUserEvent(UserEventFilterInputUnfocused{}) - } - - return m, cmd - } - - switch msg := msg.(type) { - case tea.KeyMsg: - m.handleKeypress(msg) - } - - return m, nil -} diff --git a/vendor/github.com/evertras/bubble-table/table/view.go b/vendor/github.com/evertras/bubble-table/table/view.go deleted file mode 100644 index 7be99c23..00000000 --- a/vendor/github.com/evertras/bubble-table/table/view.go +++ /dev/null @@ -1,65 +0,0 @@ -package table - -import ( - "strings" - - "github.com/charmbracelet/lipgloss" -) - -// View renders the table. It does not end in a newline, so that it can be -// composed with other elements more consistently. -// -//nolint:cyclop -func (m Model) View() string { - // Safety valve for empty tables - if len(m.columns) == 0 { - return "" - } - - body := strings.Builder{} - - rowStrs := make([]string, 0, 1) - - headers := m.renderHeaders() - - startRowIndex, endRowIndex := m.VisibleIndices() - numRows := endRowIndex - startRowIndex + 1 - - padding := m.calculatePadding(numRows) - - if m.headerVisible { - rowStrs = append(rowStrs, headers) - } else if numRows > 0 || padding > 0 { - //nolint: mnd // This is just getting the first newlined substring - split := strings.SplitN(headers, "\n", 2) - rowStrs = append(rowStrs, split[0]) - } - - for i := startRowIndex; i <= endRowIndex; i++ { - rowStrs = append(rowStrs, m.renderRow(i, padding == 0 && i == endRowIndex)) - } - - for i := 1; i <= padding; i++ { - rowStrs = append(rowStrs, m.renderBlankRow(i == padding)) - } - - var footer string - - if len(rowStrs) > 0 { - footer = m.renderFooter(lipgloss.Width(rowStrs[0]), false) - } else { - footer = m.renderFooter(lipgloss.Width(headers), true) - } - - if footer != "" { - rowStrs = append(rowStrs, footer) - } - - if len(rowStrs) == 0 { - return "" - } - - body.WriteString(lipgloss.JoinVertical(lipgloss.Left, rowStrs...)) - - return body.String() -} diff --git a/vendor/github.com/muesli/reflow/LICENSE b/vendor/github.com/muesli/reflow/LICENSE deleted file mode 100644 index 8532c45c..00000000 --- a/vendor/github.com/muesli/reflow/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Christian Muehlhaeuser - -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/muesli/reflow/ansi/ansi.go b/vendor/github.com/muesli/reflow/ansi/ansi.go deleted file mode 100644 index f3d0700a..00000000 --- a/vendor/github.com/muesli/reflow/ansi/ansi.go +++ /dev/null @@ -1,7 +0,0 @@ -package ansi - -const Marker = '\x1B' - -func IsTerminator(c rune) bool { - return (c >= 0x40 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) -} diff --git a/vendor/github.com/muesli/reflow/ansi/buffer.go b/vendor/github.com/muesli/reflow/ansi/buffer.go deleted file mode 100644 index 471bcaf7..00000000 --- a/vendor/github.com/muesli/reflow/ansi/buffer.go +++ /dev/null @@ -1,40 +0,0 @@ -package ansi - -import ( - "bytes" - - "github.com/mattn/go-runewidth" -) - -// Buffer is a buffer aware of ANSI escape sequences. -type Buffer struct { - bytes.Buffer -} - -// PrintableRuneWidth returns the cell width of all printable runes in the -// buffer. -func (w Buffer) PrintableRuneWidth() int { - return PrintableRuneWidth(w.String()) -} - -// PrintableRuneWidth returns the cell width of the given string. -func PrintableRuneWidth(s string) int { - var n int - var ansi bool - - for _, c := range s { - if c == Marker { - // ANSI escape sequence - ansi = true - } else if ansi { - if IsTerminator(c) { - // ANSI sequence terminated - ansi = false - } - } else { - n += runewidth.RuneWidth(c) - } - } - - return n -} diff --git a/vendor/github.com/muesli/reflow/ansi/writer.go b/vendor/github.com/muesli/reflow/ansi/writer.go deleted file mode 100644 index a6aaa1ec..00000000 --- a/vendor/github.com/muesli/reflow/ansi/writer.go +++ /dev/null @@ -1,76 +0,0 @@ -package ansi - -import ( - "bytes" - "io" - "unicode/utf8" -) - -type Writer struct { - Forward io.Writer - - ansi bool - ansiseq bytes.Buffer - lastseq bytes.Buffer - seqchanged bool - runeBuf []byte -} - -// Write is used to write content to the ANSI buffer. -func (w *Writer) Write(b []byte) (int, error) { - for _, c := range string(b) { - if c == Marker { - // ANSI escape sequence - w.ansi = true - w.seqchanged = true - _, _ = w.ansiseq.WriteRune(c) - } else if w.ansi { - _, _ = w.ansiseq.WriteRune(c) - if IsTerminator(c) { - // ANSI sequence terminated - w.ansi = false - - if bytes.HasSuffix(w.ansiseq.Bytes(), []byte("[0m")) { - // reset sequence - w.lastseq.Reset() - w.seqchanged = false - } else if c == 'm' { - // color code - _, _ = w.lastseq.Write(w.ansiseq.Bytes()) - } - - _, _ = w.ansiseq.WriteTo(w.Forward) - } - } else { - _, err := w.writeRune(c) - if err != nil { - return 0, err - } - } - } - - return len(b), nil -} - -func (w *Writer) writeRune(r rune) (int, error) { - if w.runeBuf == nil { - w.runeBuf = make([]byte, utf8.UTFMax) - } - n := utf8.EncodeRune(w.runeBuf, r) - return w.Forward.Write(w.runeBuf[:n]) -} - -func (w *Writer) LastSequence() string { - return w.lastseq.String() -} - -func (w *Writer) ResetAnsi() { - if !w.seqchanged { - return - } - _, _ = w.Forward.Write([]byte("\x1b[0m")) -} - -func (w *Writer) RestoreAnsi() { - _, _ = w.Forward.Write(w.lastseq.Bytes()) -} diff --git a/vendor/github.com/muesli/reflow/truncate/truncate.go b/vendor/github.com/muesli/reflow/truncate/truncate.go deleted file mode 100644 index 5aab5f89..00000000 --- a/vendor/github.com/muesli/reflow/truncate/truncate.go +++ /dev/null @@ -1,120 +0,0 @@ -package truncate - -import ( - "bytes" - "io" - - "github.com/mattn/go-runewidth" - - "github.com/muesli/reflow/ansi" -) - -type Writer struct { - width uint - tail string - - ansiWriter *ansi.Writer - buf bytes.Buffer - ansi bool -} - -func NewWriter(width uint, tail string) *Writer { - w := &Writer{ - width: width, - tail: tail, - } - w.ansiWriter = &ansi.Writer{ - Forward: &w.buf, - } - return w -} - -func NewWriterPipe(forward io.Writer, width uint, tail string) *Writer { - return &Writer{ - width: width, - tail: tail, - ansiWriter: &ansi.Writer{ - Forward: forward, - }, - } -} - -// Bytes is shorthand for declaring a new default truncate-writer instance, -// used to immediately truncate a byte slice. -func Bytes(b []byte, width uint) []byte { - return BytesWithTail(b, width, []byte("")) -} - -// Bytes is shorthand for declaring a new default truncate-writer instance, -// used to immediately truncate a byte slice. A tail is then added to the -// end of the byte slice. -func BytesWithTail(b []byte, width uint, tail []byte) []byte { - f := NewWriter(width, string(tail)) - _, _ = f.Write(b) - - return f.Bytes() -} - -// String is shorthand for declaring a new default truncate-writer instance, -// used to immediately truncate a string. -func String(s string, width uint) string { - return StringWithTail(s, width, "") -} - -// StringWithTail is shorthand for declaring a new default truncate-writer instance, -// used to immediately truncate a string. A tail is then added to the end of the -// string. -func StringWithTail(s string, width uint, tail string) string { - return string(BytesWithTail([]byte(s), width, []byte(tail))) -} - -// Write truncates content at the given printable cell width, leaving any -// ansi sequences intact. -func (w *Writer) Write(b []byte) (int, error) { - tw := ansi.PrintableRuneWidth(w.tail) - if w.width < uint(tw) { - return w.buf.WriteString(w.tail) - } - - w.width -= uint(tw) - var curWidth uint - - for _, c := range string(b) { - if c == ansi.Marker { - // ANSI escape sequence - w.ansi = true - } else if w.ansi { - if ansi.IsTerminator(c) { - // ANSI sequence terminated - w.ansi = false - } - } else { - curWidth += uint(runewidth.RuneWidth(c)) - } - - if curWidth > w.width { - n, err := w.buf.WriteString(w.tail) - if w.ansiWriter.LastSequence() != "" { - w.ansiWriter.ResetAnsi() - } - return n, err - } - - _, err := w.ansiWriter.Write([]byte(string(c))) - if err != nil { - return 0, err - } - } - - return len(b), nil -} - -// Bytes returns the truncated result as a byte slice. -func (w *Writer) Bytes() []byte { - return w.buf.Bytes() -} - -// String returns the truncated result as a string. -func (w *Writer) String() string { - return w.buf.String() -} diff --git a/vendor/github.com/muesli/reflow/wordwrap/wordwrap.go b/vendor/github.com/muesli/reflow/wordwrap/wordwrap.go deleted file mode 100644 index 488fb210..00000000 --- a/vendor/github.com/muesli/reflow/wordwrap/wordwrap.go +++ /dev/null @@ -1,167 +0,0 @@ -package wordwrap - -import ( - "bytes" - "strings" - "unicode" - - "github.com/muesli/reflow/ansi" -) - -var ( - defaultBreakpoints = []rune{'-'} - defaultNewline = []rune{'\n'} -) - -// WordWrap contains settings and state for customisable text reflowing with -// support for ANSI escape sequences. This means you can style your terminal -// output without affecting the word wrapping algorithm. -type WordWrap struct { - Limit int - Breakpoints []rune - Newline []rune - KeepNewlines bool - - buf bytes.Buffer - space bytes.Buffer - word ansi.Buffer - - lineLen int - ansi bool -} - -// NewWriter returns a new instance of a word-wrapping writer, initialized with -// default settings. -func NewWriter(limit int) *WordWrap { - return &WordWrap{ - Limit: limit, - Breakpoints: defaultBreakpoints, - Newline: defaultNewline, - KeepNewlines: true, - } -} - -// Bytes is shorthand for declaring a new default WordWrap instance, -// used to immediately word-wrap a byte slice. -func Bytes(b []byte, limit int) []byte { - f := NewWriter(limit) - _, _ = f.Write(b) - _ = f.Close() - - return f.Bytes() -} - -// String is shorthand for declaring a new default WordWrap instance, -// used to immediately word-wrap a string. -func String(s string, limit int) string { - return string(Bytes([]byte(s), limit)) -} - -func (w *WordWrap) addSpace() { - w.lineLen += w.space.Len() - _, _ = w.buf.Write(w.space.Bytes()) - w.space.Reset() -} - -func (w *WordWrap) addWord() { - if w.word.Len() > 0 { - w.addSpace() - w.lineLen += w.word.PrintableRuneWidth() - _, _ = w.buf.Write(w.word.Bytes()) - w.word.Reset() - } -} - -func (w *WordWrap) addNewLine() { - _, _ = w.buf.WriteRune('\n') - w.lineLen = 0 - w.space.Reset() -} - -func inGroup(a []rune, c rune) bool { - for _, v := range a { - if v == c { - return true - } - } - return false -} - -// Write is used to write more content to the word-wrap buffer. -func (w *WordWrap) Write(b []byte) (int, error) { - if w.Limit == 0 { - return w.buf.Write(b) - } - - s := string(b) - if !w.KeepNewlines { - s = strings.Replace(strings.TrimSpace(s), "\n", " ", -1) - } - - for _, c := range s { - if c == '\x1B' { - // ANSI escape sequence - _, _ = w.word.WriteRune(c) - w.ansi = true - } else if w.ansi { - _, _ = w.word.WriteRune(c) - if (c >= 0x40 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) { - // ANSI sequence terminated - w.ansi = false - } - } else if inGroup(w.Newline, c) { - // end of current line - // see if we can add the content of the space buffer to the current line - if w.word.Len() == 0 { - if w.lineLen+w.space.Len() > w.Limit { - w.lineLen = 0 - } else { - // preserve whitespace - _, _ = w.buf.Write(w.space.Bytes()) - } - w.space.Reset() - } - - w.addWord() - w.addNewLine() - } else if unicode.IsSpace(c) { - // end of current word - w.addWord() - _, _ = w.space.WriteRune(c) - } else if inGroup(w.Breakpoints, c) { - // valid breakpoint - w.addSpace() - w.addWord() - _, _ = w.buf.WriteRune(c) - } else { - // any other character - _, _ = w.word.WriteRune(c) - - // add a line break if the current word would exceed the line's - // character limit - if w.lineLen+w.space.Len()+w.word.PrintableRuneWidth() > w.Limit && - w.word.PrintableRuneWidth() < w.Limit { - w.addNewLine() - } - } - } - - return len(b), nil -} - -// Close will finish the word-wrap operation. Always call it before trying to -// retrieve the final result. -func (w *WordWrap) Close() error { - w.addWord() - return nil -} - -// Bytes returns the word-wrapped result as a byte slice. -func (w *WordWrap) Bytes() []byte { - return w.buf.Bytes() -} - -// String returns the word-wrapped result as a string. -func (w *WordWrap) String() string { - return w.buf.String() -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 72c4be64..d5828a17 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -51,9 +51,6 @@ github.com/ProtonMail/go-crypto/openpgp/packet github.com/ProtonMail/go-crypto/openpgp/s2k github.com/ProtonMail/go-crypto/openpgp/x25519 github.com/ProtonMail/go-crypto/openpgp/x448 -# github.com/atotto/clipboard v0.1.4 -## explicit -github.com/atotto/clipboard # github.com/aymanbagabas/go-osc52/v2 v2.0.1 ## explicit; go 1.16 github.com/aymanbagabas/go-osc52/v2 @@ -68,13 +65,6 @@ github.com/cenkalti/backoff/v5 # github.com/cespare/xxhash/v2 v2.3.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/charmbracelet/bubbles v0.21.0 -## explicit; go 1.23.0 -github.com/charmbracelet/bubbles/cursor -github.com/charmbracelet/bubbles/key -github.com/charmbracelet/bubbles/runeutil -github.com/charmbracelet/bubbles/spinner -github.com/charmbracelet/bubbles/textinput # github.com/charmbracelet/bubbletea v1.3.10 ## explicit; go 1.24.0 github.com/charmbracelet/bubbletea @@ -95,6 +85,8 @@ github.com/charmbracelet/x/ansi/parser # github.com/charmbracelet/x/cellbuf v0.0.13 ## explicit; go 1.18 github.com/charmbracelet/x/cellbuf +# github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 +## explicit; go 1.19 # github.com/charmbracelet/x/term v0.2.1 ## explicit; go 1.18 github.com/charmbracelet/x/term @@ -283,9 +275,6 @@ github.com/emirpasic/gods/utils # github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f ## explicit; go 1.16 github.com/erikgeiser/coninput -# github.com/evertras/bubble-table v0.19.2 -## explicit; go 1.18 -github.com/evertras/bubble-table/table # github.com/felixge/httpsnoop v1.0.4 ## explicit; go 1.13 github.com/felixge/httpsnoop @@ -493,11 +482,6 @@ github.com/muesli/ansi/compressor # github.com/muesli/cancelreader v0.2.2 ## explicit; go 1.17 github.com/muesli/cancelreader -# github.com/muesli/reflow v0.3.0 -## explicit; go 1.13 -github.com/muesli/reflow/ansi -github.com/muesli/reflow/truncate -github.com/muesli/reflow/wordwrap # github.com/muesli/termenv v0.16.0 ## explicit; go 1.17 github.com/muesli/termenv