From 4ef849767f525c01793d53b72b6d7009bbbc2cc0 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Sun, 2 Nov 2025 14:13:40 +0100 Subject: [PATCH] WIP --- go.mod | 3 - go.sum | 9 - pkg/i18n/locales/abra.pot | 24 +- pkg/i18n/locales/es.po | 385 +++++--- pkg/ui/deploy.go | 70 +- .../github.com/atotto/clipboard/.travis.yml | 22 - vendor/github.com/atotto/clipboard/LICENSE | 27 - vendor/github.com/atotto/clipboard/README.md | 48 - .../github.com/atotto/clipboard/clipboard.go | 20 - .../atotto/clipboard/clipboard_darwin.go | 52 - .../atotto/clipboard/clipboard_plan9.go | 42 - .../atotto/clipboard/clipboard_unix.go | 149 --- .../atotto/clipboard/clipboard_windows.go | 157 --- .../charmbracelet/bubbles/cursor/cursor.go | 219 ----- .../bubbles/runeutil/runeutil.go | 102 -- .../charmbracelet/bubbles/spinner/spinner.go | 224 ----- .../bubbles/textinput/textinput.go | 898 ------------------ .../charmbracelet/bubbles/viewport/keymap.go | 60 ++ .../bubbles/viewport/viewport.go | 544 +++++++++++ .../github.com/evertras/bubble-table/LICENSE | 21 - .../evertras/bubble-table/table/border.go | 439 --------- .../evertras/bubble-table/table/calc.go | 36 - .../evertras/bubble-table/table/cell.go | 60 -- .../evertras/bubble-table/table/column.go | 118 --- .../evertras/bubble-table/table/data.go | 67 -- .../evertras/bubble-table/table/dimensions.go | 116 --- .../evertras/bubble-table/table/doc.go | 39 - .../evertras/bubble-table/table/events.go | 60 -- .../evertras/bubble-table/table/filter.go | 164 ---- .../evertras/bubble-table/table/footer.go | 51 - .../evertras/bubble-table/table/header.go | 93 -- .../evertras/bubble-table/table/keys.go | 120 --- .../evertras/bubble-table/table/model.go | 148 --- .../evertras/bubble-table/table/options.go | 510 ---------- .../evertras/bubble-table/table/overflow.go | 18 - .../evertras/bubble-table/table/pagination.go | 112 --- .../evertras/bubble-table/table/query.go | 96 -- .../evertras/bubble-table/table/row.go | 252 ----- .../evertras/bubble-table/table/scrolling.go | 50 - .../evertras/bubble-table/table/sort.go | 178 ---- .../evertras/bubble-table/table/strlimit.go | 26 - .../evertras/bubble-table/table/update.go | 154 --- .../evertras/bubble-table/table/view.go | 65 -- vendor/github.com/muesli/reflow/LICENSE | 21 - vendor/github.com/muesli/reflow/ansi/ansi.go | 7 - .../github.com/muesli/reflow/ansi/buffer.go | 40 - .../github.com/muesli/reflow/ansi/writer.go | 76 -- .../muesli/reflow/truncate/truncate.go | 120 --- .../muesli/reflow/wordwrap/wordwrap.go | 167 ---- vendor/modules.txt | 16 +- 50 files changed, 942 insertions(+), 5553 deletions(-) delete mode 100644 vendor/github.com/atotto/clipboard/.travis.yml delete mode 100644 vendor/github.com/atotto/clipboard/LICENSE delete mode 100644 vendor/github.com/atotto/clipboard/README.md delete mode 100644 vendor/github.com/atotto/clipboard/clipboard.go delete mode 100644 vendor/github.com/atotto/clipboard/clipboard_darwin.go delete mode 100644 vendor/github.com/atotto/clipboard/clipboard_plan9.go delete mode 100644 vendor/github.com/atotto/clipboard/clipboard_unix.go delete mode 100644 vendor/github.com/atotto/clipboard/clipboard_windows.go delete mode 100644 vendor/github.com/charmbracelet/bubbles/cursor/cursor.go delete mode 100644 vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go delete mode 100644 vendor/github.com/charmbracelet/bubbles/spinner/spinner.go delete mode 100644 vendor/github.com/charmbracelet/bubbles/textinput/textinput.go create mode 100644 vendor/github.com/charmbracelet/bubbles/viewport/keymap.go create mode 100644 vendor/github.com/charmbracelet/bubbles/viewport/viewport.go delete mode 100644 vendor/github.com/evertras/bubble-table/LICENSE delete mode 100644 vendor/github.com/evertras/bubble-table/table/border.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/calc.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/cell.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/column.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/data.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/dimensions.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/doc.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/events.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/filter.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/footer.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/header.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/keys.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/model.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/options.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/overflow.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/pagination.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/query.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/row.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/scrolling.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/sort.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/strlimit.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/update.go delete mode 100644 vendor/github.com/evertras/bubble-table/table/view.go delete mode 100644 vendor/github.com/muesli/reflow/LICENSE delete mode 100644 vendor/github.com/muesli/reflow/ansi/ansi.go delete mode 100644 vendor/github.com/muesli/reflow/ansi/buffer.go delete mode 100644 vendor/github.com/muesli/reflow/ansi/writer.go delete mode 100644 vendor/github.com/muesli/reflow/truncate/truncate.go delete mode 100644 vendor/github.com/muesli/reflow/wordwrap/wordwrap.go diff --git a/go.mod b/go.mod index ee81b55b..57a95eb0 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,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 +34,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 @@ -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..81740fb5 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= @@ -373,8 +371,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 +636,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 +697,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 +811,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/pkg/i18n/locales/abra.pot b/pkg/i18n/locales/abra.pot index 62fad6c4..3a9634bc 100644 --- a/pkg/i18n/locales/abra.pot +++ b/pkg/i18n/locales/abra.pot @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" - "POT-Creation-Date: 2025-11-02 11:41+0100\n" + "POT-Creation-Date: 2025-11-02 14:13+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -469,7 +469,7 @@ msgstr "" msgid "%s: %s (new)" msgstr "" -#: ./pkg/ui/deploy.go:344 +#: ./pkg/ui/deploy.go:406 #, c-format msgid "%s: %s (retries: %v, healthcheck: %s)" msgstr "" @@ -1659,7 +1659,7 @@ msgid "\n" "lint %s: %s" msgstr "" -#: ./pkg/ui/deploy.go:121 +#: ./pkg/ui/deploy.go:132 #, c-format msgid "^%s" msgstr "" @@ -2662,7 +2662,7 @@ msgstr "" msgid "env file for %s has issues: %s" msgstr "" -#: ./pkg/ui/deploy.go:83 +#: ./pkg/ui/deploy.go:94 #, c-format msgid "err: %v, " msgstr "" @@ -3166,7 +3166,7 @@ msgstr "" msgid "i" msgstr "" -#: ./pkg/ui/deploy.go:84 +#: ./pkg/ui/deploy.go:95 #, c-format msgid "id: %s, " msgstr "" @@ -3605,7 +3605,7 @@ msgstr "" msgid "name a servce 'app'" msgstr "" -#: ./pkg/ui/deploy.go:85 +#: ./pkg/ui/deploy.go:96 #, c-format msgid "name: %s, " msgstr "" @@ -4147,7 +4147,7 @@ msgstr "" msgid "read v:%s k: %s" msgstr "" -#: ./pkg/ui/deploy.go:86 +#: ./pkg/ui/deploy.go:97 #, c-format msgid "reader: %v, " msgstr "" @@ -4475,7 +4475,7 @@ msgstr "" msgid "rollback [version] [flags]" msgstr "" -#: ./pkg/ui/deploy.go:336 +#: ./pkg/ui/deploy.go:398 msgid "rolled back" msgstr "" @@ -4890,7 +4890,7 @@ msgstr "" msgid "status" msgstr "" -#: ./pkg/ui/deploy.go:88 +#: ./pkg/ui/deploy.go:99 #, c-format msgid "status: %s}" msgstr "" @@ -4912,7 +4912,7 @@ msgstr "" msgid "stripped %s to %s for parsing" msgstr "" -#: ./pkg/ui/deploy.go:333 +#: ./pkg/ui/deploy.go:395 msgid "succeeded" msgstr "" @@ -5730,7 +5730,7 @@ msgstr "" msgid "wire up healthchecks" msgstr "" -#: ./pkg/ui/deploy.go:87 +#: ./pkg/ui/deploy.go:98 #, c-format msgid "writer: %v, " msgstr "" @@ -5764,7 +5764,7 @@ msgstr "" msgid "z" msgstr "" -#: ./pkg/ui/deploy.go:82 +#: ./pkg/ui/deploy.go:93 #, c-format msgid "{decoder: %v, " msgstr "" diff --git a/pkg/i18n/locales/es.po b/pkg/i18n/locales/es.po index 03b2ec69..1c435896 100644 --- a/pkg/i18n/locales/es.po +++ b/pkg/i18n/locales/es.po @@ -2,10 +2,11 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL\n" -"POT-Creation-Date: 2025-11-02 11:41+0100\n" +"POT-Creation-Date: 2025-11-02 14:13+0100\n" "PO-Revision-Date: 2025-09-04 08:14+0000\n" "Last-Translator: chasqui \n" -"Language-Team: Spanish \n" +"Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -297,7 +298,9 @@ msgstr "" #: pkg/catalogue/catalogue.go:40 #, c-format -msgid "%s has locally unstaged changes? please commit/remove your changes before proceeding" +msgid "" +"%s has locally unstaged changes? please commit/remove your changes before " +"proceeding" msgstr "" #: cli/internal/recipe.go:114 @@ -419,7 +422,9 @@ msgstr "" #: cli/app/new.go:210 #, c-format -msgid "%s requires secret generation before deploy, run \"abra app secret generate %s --all\"" +msgid "" +"%s requires secret generation before deploy, run \"abra app secret generate " +"%s --all\"" msgstr "" #: cli/app/new.go:214 @@ -482,7 +487,7 @@ msgstr "" msgid "%s: %s (new)" msgstr "" -#: pkg/ui/deploy.go:344 +#: pkg/ui/deploy.go:406 #, c-format msgid "%s: %s (retries: %v, healthcheck: %s)" msgstr "" @@ -573,7 +578,9 @@ msgid "'%s' is not a known version for %s" msgstr "" #: cli/app/volume.go:175 -msgid "'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode is enabled" +msgid "" +"'x' indicates selected, enter / return to confirm, ctrl-c to exit, vim mode " +"is enabled" msgstr "" #: cli/app/cmd.go:125 @@ -593,12 +600,15 @@ msgstr "" msgid "" "A recipe is a blueprint for an app.\n" "\n" -"It is a bunch of config files which describe how to deploy and maintain an app.\n" +"It is a bunch of config files which describe how to deploy and maintain an " +"app.\n" "Recipes are maintained by the Co-op Cloud community and you can use Abra to\n" "read them, deploy them and create apps for you.\n" "\n" -"Anyone who uses a recipe can become a maintainer. Maintainers typically make\n" -"sure the recipe is in good working order and the config upgraded in a timely\n" +"Anyone who uses a recipe can become a maintainer. Maintainers typically " +"make\n" +"sure the recipe is in good working order and the config upgraded in a " +"timely\n" "manner." msgstr "" @@ -617,7 +627,8 @@ msgid "" "Add a new server to your configuration so that it can be managed by Abra.\n" "\n" "Abra relies on the standard SSH command-line and ~/.ssh/config for client\n" -"connection details. You must configure an entry per-host in your ~/.ssh/config\n" +"connection details. You must configure an entry per-host in your ~/.ssh/" +"config\n" "for each server:\n" "\n" " Host 1312.net 1312\n" @@ -626,8 +637,10 @@ msgid "" " Port 12345\n" " IdentityFile ~/.ssh/antifa@somewhere\n" "\n" -"If \"--local\" is passed, then Abra assumes that the current local server is\n" -"intended as the target server. This is useful when you want to have your entire\n" +"If \"--local\" is passed, then Abra assumes that the current local server " +"is\n" +"intended as the target server. This is useful when you want to have your " +"entire\n" "Co-op Cloud config located on the server itself, and not on your local\n" "developer machine. The domain is then set to \"default\"." msgstr "" @@ -698,12 +711,16 @@ msgstr "💕 Clona la receta(s) 🧑‍🍳 en local" msgid "" "Compare env vars in both the app \".env\" and recipe \".env.sample\" file.\n" "\n" -"The goal is to ensure that recipe \".env.sample\" env vars are defined in your\n" -"app \".env\" file. Only env var definitions in the \".env.sample\" which are\n" -"uncommented, e.g. \"FOO=bar\" are checked. If an app \".env\" file does not include\n" +"The goal is to ensure that recipe \".env.sample\" env vars are defined in " +"your\n" +"app \".env\" file. Only env var definitions in the \".env.sample\" which " +"are\n" +"uncommented, e.g. \"FOO=bar\" are checked. If an app \".env\" file does not " +"include\n" "these env vars, then \"check\" will complain.\n" "\n" -"Recipe maintainers may or may not provide defaults for env vars within their\n" +"Recipe maintainers may or may not provide defaults for env vars within " +"their\n" "recipes regardless of commenting or not (e.g. through the use of\n" "${FOO:} syntax). \"check\" does not confirm or deny this for you." msgstr "" @@ -732,24 +749,31 @@ msgstr "📸 Crea una nueva captura o instantánea" msgid "" "Create a new version of a recipe.\n" "\n" -"These versions are then published on the Co-op Cloud recipe catalogue. These\n" +"These versions are then published on the Co-op Cloud recipe catalogue. " +"These\n" "versions take the following form:\n" "\n" " a.b.c+x.y.z\n" "\n" -"Where the \"a.b.c\" part is a semantic version determined by the maintainer. The\n" -"\"x.y.z\" part is the image tag of the recipe \"app\" service (the main container\n" +"Where the \"a.b.c\" part is a semantic version determined by the maintainer. " +"The\n" +"\"x.y.z\" part is the image tag of the recipe \"app\" service (the main " +"container\n" "which contains the software to be used, by naming convention).\n" "\n" "We maintain a semantic versioning scheme (\"a.b.c\") alongside the recipe\n" -"versioning scheme (\"x.y.z\") in order to maximise the chances that the nature of\n" +"versioning scheme (\"x.y.z\") in order to maximise the chances that the " +"nature of\n" "recipe updates are properly communicated. I.e. developers of an app might\n" -"publish a minor version but that might lead to changes in the recipe which are\n" +"publish a minor version but that might lead to changes in the recipe which " +"are\n" "major and therefore require intervention while doing the upgrade work.\n" "\n" "Publish your new release to git.coopcloud.tech with \"--publish/-p\". This\n" -"requires that you have permission to git push to these repositories and have\n" -"your SSH keys configured on your account. Enable ssh-agent and make sure to add\n" +"requires that you have permission to git push to these repositories and " +"have\n" +"your SSH keys configured on your account. Enable ssh-agent and make sure to " +"add\n" "your private key and enter your passphrase beforehand.\n" "\n" " eval `ssh-agent`\n" @@ -767,21 +791,27 @@ msgid "" "This new app configuration is stored in your $ABRA_DIR directory under the\n" "appropriate server.\n" "\n" -"This command does not deploy your app for you. You will need to run \"abra app\n" +"This command does not deploy your app for you. You will need to run \"abra " +"app\n" "deploy \" to do so.\n" "\n" -"You can see what recipes are available (i.e. values for the [recipe] argument)\n" +"You can see what recipes are available (i.e. values for the [recipe] " +"argument)\n" "by running \"abra recipe ls\".\n" "\n" "Recipe commit hashes are supported values for \"[version]\".\n" "\n" -"Passing the \"--secrets/-S\" flag will automatically generate secrets for your\n" +"Passing the \"--secrets/-S\" flag will automatically generate secrets for " +"your\n" "app and store them encrypted at rest on the chosen target server. These\n" -"generated secrets are only visible at generation time, so please take care to\n" +"generated secrets are only visible at generation time, so please take care " +"to\n" "store them somewhere safe.\n" "\n" -"You can use the \"--pass/-P\" to store these generated passwords locally in a\n" -"pass store (see passwordstore.org for more). The pass command must be available\n" +"You can use the \"--pass/-P\" to store these generated passwords locally in " +"a\n" +"pass store (see passwordstore.org for more). The pass command must be " +"available\n" "on your $PATH." msgstr "" @@ -815,9 +845,11 @@ msgstr "📤 Despliega una plataforma 🚀" msgid "" "Deploy an app.\n" "\n" -"This command supports chaos operations. Use \"--chaos/-C\" to deploy your recipe\n" +"This command supports chaos operations. Use \"--chaos/-C\" to deploy your " +"recipe\n" "checkout as-is. Recipe commit hashes are also supported as values for\n" -"\"[version]\". Please note, \"upgrade\"/\"rollback\" do not support chaos operations." +"\"[version]\". Please note, \"upgrade\"/\"rollback\" do not support chaos " +"operations." msgstr "" #. translators: Short description for `app services` command @@ -834,7 +866,8 @@ msgstr "⬇️📸 Descarga una captura o instantánea" msgid "" "Downloads a backup.tar.gz to the current working directory.\n" "\n" -"\"--volumes/-v\" includes data contained in volumes alongide paths specified in\n" +"\"--volumes/-v\" includes data contained in volumes alongide paths specified " +"in\n" "\"backupbot.backup.path\" labels." msgstr "" @@ -873,21 +906,28 @@ msgstr "" msgid "" "Generate a new copy of the recipe catalogue.\n" "\n" -"N.B. this command **will** wipe local unstaged changes from your local recipes\n" -"if present. \"--chaos/-C\" on this command refers to the catalogue repository\n" -"(\"$ABRA_DIR/catalogue\") and not the recipes. Please take care not to lose your\n" +"N.B. this command **will** wipe local unstaged changes from your local " +"recipes\n" +"if present. \"--chaos/-C\" on this command refers to the catalogue " +"repository\n" +"(\"$ABRA_DIR/catalogue\") and not the recipes. Please take care not to lose " +"your\n" "changes.\n" "\n" "It is possible to generate new metadata for a single recipe by passing\n" "[recipe]. The existing local catalogue will be updated, not overwritten.\n" "\n" -"It is quite easy to get rate limited by Docker Hub when running this command.\n" -"If you have a Hub account you can \"docker login\" and Abra will automatically\n" +"It is quite easy to get rate limited by Docker Hub when running this " +"command.\n" +"If you have a Hub account you can \"docker login\" and Abra will " +"automatically\n" "use those details.\n" "\n" "Publish your new release to git.coopcloud.tech with \"--publish/-p\". This\n" -"requires that you have permission to git push to these repositories and have\n" -"your SSH keys configured on your account. Enable ssh-agent and make sure to add\n" +"requires that you have permission to git push to these repositories and " +"have\n" +"your SSH keys configured on your account. Enable ssh-agent and make sure to " +"add\n" "your private key and enter your passphrase beforehand.\n" "\n" " eval `ssh-agent`\n" @@ -1085,17 +1125,22 @@ msgstr "⚙️ Administrar catálogo 📋 de recetas 🧑‍🍳" msgid "" "Move an app to a differnt server.\n" "\n" -"This command will migrate an app config and copy secrets and volumes from the\n" -"old server to the new one. The app MUST be deployed on the old server before\n" +"This command will migrate an app config and copy secrets and volumes from " +"the\n" +"old server to the new one. The app MUST be deployed on the old server " +"before\n" "doing the move. The app will be undeployed from the current server but not\n" "deployed on the new server.\n" "\n" -"The \"tar\" command is required on both the old and new server as well as \"sudo\"\n" +"The \"tar\" command is required on both the old and new server as well as " +"\"sudo\"\n" "permissions. The \"rsync\" command is required on your local machine for\n" "transferring volumes.\n" "\n" -"Do not forget to update your DNS records. Don't panic, it might take a while\n" -"for the dust to settle after you move an app. If anything goes wrong, you can\n" +"Do not forget to update your DNS records. Don't panic, it might take a " +"while\n" +"for the dust to settle after you move an app. If anything goes wrong, you " +"can\n" "always move the app config file to the original server and deploy it there\n" "again. No data is removed from the old server.\n" "\n" @@ -1157,7 +1202,8 @@ msgstr "🧹 Limpiar recursos en un servidor (huerta digital) 🕋" msgid "" "Prunes unused containers, networks, and dangling images.\n" "\n" -"Use \"--volumes/-v\" to remove volumes that are not associated with a deployed\n" +"Use \"--volumes/-v\" to remove volumes that are not associated with a " +"deployed\n" "app. This can result in unwanted data loss if not used carefully." msgstr "" @@ -1170,7 +1216,8 @@ msgstr "" msgid "" "Pull app environment values from a deploymed app.\n" "\n" -"A convenient command for when you've lost your app environment file or want to\n" +"A convenient command for when you've lost your app environment file or want " +"to\n" "synchronize your local app environment values with what is deployed live." msgstr "" @@ -1201,7 +1248,8 @@ msgid "" "Remove a managed server.\n" "\n" "Abra will remove the internal bookkeeping ($ABRA_DIR/servers/...) and\n" -"underlying client connection context. This server will then be lost in time,\n" +"underlying client connection context. This server will then be lost in " +"time,\n" "like tears in rain." msgstr "" @@ -1218,7 +1266,9 @@ msgstr "💀 Borrar todos los datos de una plataforma 🚀, local y remotamente #. translators: Short description for `recipe reset` command #: cli/recipe/reset.go:23 msgid "Remove all unstaged changes from recipe config" -msgstr "💀 Borra todos los cambios no actualizados ⤵️ de la configuración de la receta 🧑‍🍳" +msgstr "" +"💀 Borra todos los cambios no actualizados ⤵️ de la configuración de la " +"receta 🧑‍🍳" #: cli/app/remove.go:29 msgid "" @@ -1227,16 +1277,19 @@ msgid "" "By default, it will prompt for confirmation before proceeding. All secrets,\n" "volumes and the local app env file will be deleted.\n" "\n" -"Only run this command when you are sure you want to completely remove the app\n" +"Only run this command when you are sure you want to completely remove the " +"app\n" "and all associated app data. This is a destructive action, Be Careful!\n" "\n" "If you would like to delete specific volumes or secrets, please use removal\n" "sub-commands under \"app volume\" and \"app secret\" instead.\n" "\n" -"Please note, if you delete the local app env file without removing volumes and\n" +"Please note, if you delete the local app env file without removing volumes " +"and\n" "secrets first, Abra will *not* be able to help you remove them afterwards.\n" "\n" -"To delete everything without prompt, use the \"--force/-f\" or the \"--no-input/n\"\n" +"To delete everything without prompt, use the \"--force/-f\" or the \"--no-" +"input/n\"\n" "flag." msgstr "" @@ -1249,11 +1302,14 @@ msgstr "💀 Borra el volúmen(es) 📦 asociados a una plataforma 🚀🚨" msgid "" "Remove volumes associated with an app.\n" "\n" -"The app in question must be undeployed before you try to remove volumes. See\n" +"The app in question must be undeployed before you try to remove volumes. " +"See\n" "\"abra app undeploy \" for more.\n" "\n" -"The command is interactive and will show a multiple select input which allows\n" -"you to make a seclection. Use the \"?\" key to see more help on navigating this\n" +"The command is interactive and will show a multiple select input which " +"allows\n" +"you to make a seclection. Use the \"?\" key to see more help on navigating " +"this\n" "interface.\n" "\n" "Passing \"--force/-f\" will select all volumes for removal. Be careful." @@ -1277,19 +1333,25 @@ msgstr "⏪ Revertir una plataforma 🚀 una versión anterior" #. translators: Short description for `app run` command #: cli/app/run.go:30 msgid "Run a command inside a service container" -msgstr "💻 Ejecuta un comando dentro de un contenedor 🐋 creado solo para esa tarea" +msgstr "" +"💻 Ejecuta un comando dentro de un contenedor 🐋 creado solo para esa tarea" #: cli/app/cmd.go:31 msgid "" "Run an app specific command.\n" "\n" -"These commands are bash functions, defined in the abra.sh of the recipe itself.\n" -"They can be run within the context of a service (e.g. app) or locally on your\n" +"These commands are bash functions, defined in the abra.sh of the recipe " +"itself.\n" +"They can be run within the context of a service (e.g. app) or locally on " +"your\n" "work station by passing \"--local/-l\".\n" "\n" -"N.B. If using the \"--\" style to pass arguments, flags (e.g. \"--local/-l\") must\n" -"be passed *before* the \"--\". It is possible to pass arguments without the \"--\"\n" -"as long as no dashes are present (i.e. \"foo\" works without \"--\", \"-foo\"\n" +"N.B. If using the \"--\" style to pass arguments, flags (e.g. \"--local/-" +"l\") must\n" +"be passed *before* the \"--\". It is possible to pass arguments without the " +"\"--\"\n" +"as long as no dashes are present (i.e. \"foo\" works without \"--\", \"-" +"foo\"\n" "does not)." msgstr "" @@ -1357,7 +1419,8 @@ msgstr "📋 Muestra las etiquetas 🛂 desplegadas (proxy)" #. translators: Short description for `recipe diff` command #: cli/recipe/diff.go:23 msgid "Show unstaged changes in recipe config" -msgstr "📋 Muestra cambios sin actualizar ⤵️ en la configuración ⚙️ de la receta 🧑‍🍳" +msgstr "" +"📋 Muestra cambios sin actualizar ⤵️ en la configuración ⚙️ de la receta 🧑‍🍳" #: cli/app/restore.go:25 msgid "" @@ -1425,8 +1488,10 @@ msgid "" "Arbitrary secret insertion is not supported. Secrets that are inserted must\n" "match those configured in the recipe beforehand.\n" "\n" -"This command can be useful when you want to manually generate secrets for an app\n" -"environment. Typically, you can let Abra generate them for you on app creation\n" +"This command can be useful when you want to manually generate secrets for an " +"app\n" +"environment. Typically, you can let Abra generate them for you on app " +"creation\n" "(see \"abra app new --secrets/-S\" for more)." msgstr "" @@ -1455,17 +1520,22 @@ msgstr "" msgid "" "This command rolls an app back to a previous version.\n" "\n" -"Unlike \"abra app deploy\", chaos operations are not supported here. Only recipe\n" +"Unlike \"abra app deploy\", chaos operations are not supported here. Only " +"recipe\n" "versions are supported values for \"[version]\".\n" "\n" -"It is possible to \"--force/-f\" an downgrade if you want to re-deploy a specific\n" +"It is possible to \"--force/-f\" an downgrade if you want to re-deploy a " +"specific\n" "version.\n" "\n" -"Only the deployed version is consulted when trying to determine what downgrades\n" -"are available. The live deployment version is the \"source of truth\" in this\n" +"Only the deployed version is consulted when trying to determine what " +"downgrades\n" +"are available. The live deployment version is the \"source of truth\" in " +"this\n" "case. The stored .env version is not consulted.\n" "\n" -"A downgrade can be destructive, please ensure you have a copy of your app data\n" +"A downgrade can be destructive, please ensure you have a copy of your app " +"data\n" "beforehand. See \"abra app backup\" for more." msgstr "" @@ -1473,7 +1543,8 @@ msgstr "" msgid "" "This does not destroy any application data.\n" "\n" -"However, you should remain vigilant, as your swarm installation will consider\n" +"However, you should remain vigilant, as your swarm installation will " +"consider\n" "any previously attached volumes as eligible for pruning once undeployed.\n" "\n" "Passing \"--prune/-p\" does not remove those volumes." @@ -1491,7 +1562,8 @@ msgid "" " # Linux:\n" " $ abra autocomplete bash | sudo tee /etc/bash_completion.d/abra\n" " # macOS:\n" -" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/abra\n" +" $ abra autocomplete bash | sudo tee $(brew --prefix)/etc/bash_completion.d/" +"abra\n" "\n" "Zsh:\n" " # If shell autocompletion is not already enabled in your environment,\n" @@ -1546,7 +1618,8 @@ msgid "" "It will update the relevant compose file tags on the local file system.\n" "\n" "Some image tags cannot be parsed because they do not follow some sort of\n" -"semver-like convention. In this case, all possible tags will be listed and it\n" +"semver-like convention. In this case, all possible tags will be listed and " +"it\n" "is up to the end-user to decide.\n" "\n" "The command is interactive and will show a select input which allows you to\n" @@ -1567,8 +1640,10 @@ msgid "" "\n" "By default, the latest stable release is downloaded.\n" "\n" -"Use \"--rc/-r\" to install the latest release candidate. Please bear in mind that\n" -"it may contain absolutely catastrophic deal-breaker bugs. Thank you very much\n" +"Use \"--rc/-r\" to install the latest release candidate. Please bear in mind " +"that\n" +"it may contain absolutely catastrophic deal-breaker bugs. Thank you very " +"much\n" "for the testing efforts 💗" msgstr "" @@ -1581,17 +1656,22 @@ msgstr "📨 Actualizar una plataforma 🚀" msgid "" "Upgrade an app.\n" "\n" -"Unlike \"abra app deploy\", chaos operations are not supported here. Only recipe\n" +"Unlike \"abra app deploy\", chaos operations are not supported here. Only " +"recipe\n" "versions are supported values for \"[version]\".\n" "\n" -"It is possible to \"--force/-f\" an upgrade if you want to re-deploy a specific\n" +"It is possible to \"--force/-f\" an upgrade if you want to re-deploy a " +"specific\n" "version.\n" "\n" -"Only the deployed version is consulted when trying to determine what upgrades\n" -"are available. The live deployment version is the \"source of truth\" in this\n" +"Only the deployed version is consulted when trying to determine what " +"upgrades\n" +"are available. The live deployment version is the \"source of truth\" in " +"this\n" "case. The stored .env version is not consulted.\n" "\n" -"An upgrade can be destructive, please ensure you have a copy of your app data\n" +"An upgrade can be destructive, please ensure you have a copy of your app " +"data\n" "beforehand. See \"abra app backup\" for more." msgstr "" @@ -1614,23 +1694,32 @@ msgid "" "Examples:\n" "{{.Example}}{{end}}{{if .HasAvailableSubCommands}}\n" "\n" -"Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name \"help\"))}}\n" -" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\n" +"Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name " +"\"help\"))}}\n" +" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if ." +"HasAvailableLocalFlags}}\n" "\n" "Flags:\n" -"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\n" +"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if ." +"HasAvailableInheritedFlags}}\n" "\n" "Global Flags:\n" -"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}\n" +"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if ." +"HasHelpSubCommands}}\n" "\n" -"Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}\n" -" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}\n" +"Additional help topics:{{range .Commands}}{{if ." +"IsAdditionalHelpTopicCommand}}\n" +" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}" +"{{if .HasAvailableSubCommands}}\n" "\n" -"Use \"{{.CommandPath}} [command] --help\" for more information about a command.{{end}}\n" +"Use \"{{.CommandPath}} [command] --help\" for more information about a " +"command.{{end}}\n" msgstr "" #: cli/recipe/fetch.go:28 -msgid "Using \"--force/-f\" Git syncs an existing recipe. It does not erase unstaged changes." +msgid "" +"Using \"--force/-f\" Git syncs an existing recipe. It does not erase " +"unstaged changes." msgstr "" #: cli/app/secret.go:117 @@ -1662,20 +1751,27 @@ msgstr "" #, c-format msgid "" "\n" -"The following options are two types of initial semantic version that you can\n" -"pick for %s that will be published in the recipe catalogue. This follows the\n" +"The following options are two types of initial semantic version that you " +"can\n" +"pick for %s that will be published in the recipe catalogue. This follows " +"the\n" "semver convention (more on https://semver.org), here is a short cheatsheet\n" "\n" -" 0.1.0: development release, still hacking. when you make a major upgrade\n" -" you increment the \"y\" part (i.e. 0.1.0 -> 0.2.0) and only move to\n" +" 0.1.0: development release, still hacking. when you make a major " +"upgrade\n" +" you increment the \"y\" part (i.e. 0.1.0 -> 0.2.0) and only move " +"to\n" " using the \"x\" part when things are stable.\n" "\n" " 1.0.0: public release, assumed to be working. you already have a stable\n" -" and reliable deployment of this app and feel relatively confident\n" +" and reliable deployment of this app and feel relatively " +"confident\n" " about it.\n" "\n" -"If you want people to be able alpha test your current config for %s but don't\n" -"think it is quite reliable, go with 0.1.0 and people will know that things are\n" +"If you want people to be able alpha test your current config for %s but " +"don't\n" +"think it is quite reliable, go with 0.1.0 and people will know that things " +"are\n" "likely to change.\n" "\n" msgstr "" @@ -1686,7 +1782,8 @@ msgid "" "\n" "You need to make a decision about what kind of an update this new recipe\n" "version is. If someone else performs this upgrade, do they have to do some\n" -"migration work or take care of some breaking changes? This can be signaled in\n" +"migration work or take care of some breaking changes? This can be signaled " +"in\n" "the version you specify on the recipe deploy label and is called a semantic\n" "version.\n" "\n" @@ -1696,15 +1793,20 @@ msgid "" "\n" "Here is a semver cheat sheet (more on https://semver.org):\n" "\n" -" major: new features/bug fixes, backwards incompatible (e.g 1.0.0 -> 2.0.0).\n" -" the upgrade won't work without some preparation work and others need\n" +" major: new features/bug fixes, backwards incompatible (e.g 1.0.0 -> " +"2.0.0).\n" +" the upgrade won't work without some preparation work and others " +"need\n" " to take care when performing it. \"it could go wrong\".\n" "\n" -" minor: new features/bug fixes, backwards compatible (e.g. 0.1.0 -> 0.2.0).\n" -" the upgrade should Just Work and there are no breaking changes in\n" +" minor: new features/bug fixes, backwards compatible (e.g. 0.1.0 -> " +"0.2.0).\n" +" the upgrade should Just Work and there are no breaking changes " +"in\n" " the app and the recipe config. \"it should go fine\".\n" "\n" -" patch: bug fixes, backwards compatible (e.g. 0.0.1 -> 0.0.2). this upgrade\n" +" patch: bug fixes, backwards compatible (e.g. 0.0.1 -> 0.0.2). this " +"upgrade\n" " should also Just Work and is mostly to do with minor bug fixes\n" " and/or security patches. \"nothing to worry about\".\n" "\n" @@ -1717,7 +1819,7 @@ msgid "" "lint %s: %s" msgstr "" -#: pkg/ui/deploy.go:121 +#: pkg/ui/deploy.go:132 #, c-format msgid "^%s" msgstr "" @@ -2043,14 +2145,17 @@ msgstr "" #: pkg/recipe/git.go:52 #, c-format -msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?" +msgid "" +"cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?" msgstr "" #: cli/app/deploy.go:369 #, c-format msgid "" -"cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?\n" -" to return to a regular release, specify a release tag, commit SHA or use \"--latest\"" +"cannot redeploy previous chaos version (%s), did you mean to use \"--" +"chaos\"?\n" +" to return to a regular release, specify a release tag, commit SHA or " +"use \"--latest\"" msgstr "" #: pkg/dns/dns.go:38 pkg/dns/dns.go:47 @@ -2211,13 +2316,19 @@ msgstr "" #: pkg/upstream/commandconn/commandconn.go:172 #, c-format -msgid "command %v has exited with %v, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=%s" +msgid "" +"command %v has exited with %v, please make sure the URL is valid, and Docker " +"18.09 or later is installed on the remote host: stderr=%s" msgstr "" #. translators: `app command` command #: cli/app/cmd.go:27 -msgid "command [service | --local] [[args] [flags] | [flags] -- [args]]" -msgstr "ejecutar [service | --local] [[args] [flags] | [flags] -- [args]]" +msgid "" +"command [service | --local] [[args] [flags] | [flags] -- " +"[args]]" +msgstr "" +"ejecutar [service | --local] [[args] [flags] | [flags] -- " +"[args]]" #: pkg/upstream/commandconn/commandconn.go:239 #, c-format @@ -2718,7 +2829,9 @@ msgid "ensuring env version %s" msgstr "" #: cli/recipe/upgrade.go:286 -msgid "enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode is enabled" +msgid "" +"enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode " +"is enabled" msgstr "" #. translators: `app env` command group @@ -2737,7 +2850,7 @@ msgstr "" msgid "env file for %s has issues: %s" msgstr "" -#: pkg/ui/deploy.go:83 +#: pkg/ui/deploy.go:94 #, c-format msgid "err: %v, " msgstr "" @@ -3248,7 +3361,7 @@ msgstr "" msgid "i" msgstr "" -#: pkg/ui/deploy.go:84 +#: pkg/ui/deploy.go:95 #, c-format msgid "id: %s, " msgstr "" @@ -3417,7 +3530,9 @@ msgstr "" #: pkg/upstream/convert/service.go:834 #, c-format -msgid "invalid credential spec: spec specifies config %v, but no such config can be found" +msgid "" +"invalid credential spec: spec specifies config %v, but no such config can be " +"found" msgstr "" #: pkg/upstream/container/hijack.go:100 @@ -3705,7 +3820,7 @@ msgstr "" msgid "name a servce 'app'" msgstr "" -#: pkg/ui/deploy.go:85 +#: pkg/ui/deploy.go:96 #, c-format msgid "name: %s, " msgstr "" @@ -3716,12 +3831,17 @@ msgstr "" #: pkg/upstream/stack/stack.go:348 #, c-format -msgid "network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed, which you can do by running this on the server: docker network create -d overlay proxy" +msgid "" +"network %q is declared as external, but could not be found. You need to " +"create a swarm-scoped network before the stack is deployed, which you can do " +"by running this on the server: docker network create -d overlay proxy" msgstr "" #: pkg/upstream/stack/stack.go:352 #, c-format -msgid "network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"" +msgid "" +"network %q is declared as external, but it is not in the right scope: %q " +"instead of \"swarm\"" msgstr "" #: cli/app/undeploy.go:148 cli/server/prune.go:60 @@ -3841,7 +3961,9 @@ msgstr "" #: cli/recipe/upgrade.go:183 #, c-format -msgid "no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)" +msgid "" +"no new versions available for %s, assuming %s is the latest (use -a/--all-" +"tags to see all anyway)" msgstr "" #: cli/internal/validate.go:64 @@ -3908,7 +4030,9 @@ msgstr "" #: cli/recipe/release.go:180 #, c-format -msgid "no tag specified and no previous tag available for %s, assuming initial release" +msgid "" +"no tag specified and no previous tag available for %s, assuming initial " +"release" msgstr "" #: pkg/lint/recipe.go:89 @@ -3974,7 +4098,9 @@ msgstr "" #: cli/recipe/upgrade.go:245 #, c-format -msgid "not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants" +msgid "" +"not upgrading from %s to %s for %s, because the upgrade type is more serious " +"than what user wants" msgstr "" #: pkg/upstream/stack/remove.go:73 @@ -4261,7 +4387,7 @@ msgstr "" msgid "read v:%s k: %s" msgstr "" -#: pkg/ui/deploy.go:86 +#: pkg/ui/deploy.go:97 #, c-format msgid "reader: %v, " msgstr "" @@ -4592,7 +4718,7 @@ msgstr "" msgid "rollback [version] [flags]" msgstr "revertir [version] [flags]" -#: pkg/ui/deploy.go:336 +#: pkg/ui/deploy.go:398 msgid "rolled back" msgstr "" @@ -4801,7 +4927,9 @@ msgstr "" #: cli/recipe/upgrade.go:228 #, c-format -msgid "service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!" +msgid "" +"service %s is at version %s, but pinned to %s, please correct your compose." +"yml file manually!" msgstr "" #: cli/recipe/upgrade.go:224 @@ -5006,14 +5134,16 @@ msgid "ssh-agent not found. see \"abra recipe release --help\" and try again" msgstr "" #: cli/catalogue/catalogue.go:98 -msgid "ssh: SSH_AUTH_SOCK missing, --publish/-p will fail. see \"abra catalogue generate --help\"" +msgid "" +"ssh: SSH_AUTH_SOCK missing, --publish/-p will fail. see \"abra catalogue " +"generate --help\"" msgstr "" #: cli/app/list.go:291 cli/recipe/list.go:45 msgid "status" msgstr "" -#: pkg/ui/deploy.go:88 +#: pkg/ui/deploy.go:99 #, c-format msgid "status: %s}" msgstr "" @@ -5035,7 +5165,7 @@ msgstr "" msgid "stripped %s to %s for parsing" msgstr "" -#: pkg/ui/deploy.go:333 +#: pkg/ui/deploy.go:395 msgid "succeeded" msgstr "" @@ -5392,7 +5522,9 @@ msgstr "" #: cli/recipe/release.go:621 #, c-format -msgid "unable to read version for %s from synced label. Did you try running \"abra recipe sync %s\" already?" +msgid "" +"unable to read version for %s from synced label. Did you try running \"abra " +"recipe sync %s\" already?" msgstr "" #: cli/app/move.go:210 @@ -5701,7 +5833,8 @@ msgstr "" #: cli/app/deploy.go:116 #, c-format -msgid "version '%s' appears to be a chaos commit, but --chaos/-C was not provided" +msgid "" +"version '%s' appears to be a chaos commit, but --chaos/-C was not provided" msgstr "" #: pkg/recipe/recipe.go:200 @@ -5864,7 +5997,7 @@ msgstr "" msgid "wire up healthchecks" msgstr "" -#: pkg/ui/deploy.go:87 +#: pkg/ui/deploy.go:98 #, c-format msgid "writer: %v, " msgstr "" @@ -5899,7 +6032,7 @@ msgstr "" msgid "z" msgstr "" -#: pkg/ui/deploy.go:82 +#: pkg/ui/deploy.go:93 #, c-format msgid "{decoder: %v, " msgstr "" diff --git a/pkg/ui/deploy.go b/pkg/ui/deploy.go index 92bb41ce..7c09984d 100644 --- a/pkg/ui/deploy.go +++ b/pkg/ui/deploy.go @@ -11,6 +11,7 @@ import ( "coopcloud.tech/abra/pkg/formatter" "coopcloud.tech/abra/pkg/i18n" "coopcloud.tech/abra/pkg/logs" + "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/docker/cli/cli/command/service/progress" containerTypes "github.com/docker/docker/api/types/container" @@ -41,6 +42,12 @@ type ServiceMeta struct { ID string } +const ( + statusMode = iota + logsMode = iota + errorsMode = iota +) + type Model struct { appName string cl *dockerClient.Client @@ -49,6 +56,10 @@ type Model struct { timeout time.Duration width int filters filters.Args + mode int + + logsViewport viewport.Model + logsViewportReady bool Streams *[]stream Logs *[]string @@ -236,7 +247,10 @@ func deployTimeout(m Model) tea.Msg { } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var cmds []tea.Cmd + var ( + cmd tea.Cmd + cmds []tea.Cmd + ) switch msg := msg.(type) { case tea.KeyMsg: @@ -244,11 +258,25 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "ctrl+c", "q": m.Quit = true return m, tea.Quit + case "s": + m.mode = statusMode + case "l": + m.mode = logsMode + case "e": + m.mode = errorsMode } case tea.WindowSizeMsg: m.width = msg.Width + if !m.logsViewportReady { + m.logsViewport = viewport.New(msg.Width, 20) + m.logsViewportReady = true + } else { + m.logsViewport.Width = msg.Width + m.logsViewport.Height = 20 + } + case progressCompleteMsg: if msg.failed { m.Failed = true @@ -256,9 +284,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.count += 1 - if m.complete() { - return m, tea.Quit - } + // if m.complete() { + // return m, tea.Quit + // } case timeoutMsg: m.TimedOut = true @@ -318,12 +346,46 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { ) } + m.logsViewport, cmd = m.logsViewport.Update(msg) + cmds = append(cmds, cmd) + return m, tea.Batch(cmds...) } func (m Model) View() string { body := strings.Builder{} + body.WriteString("menu: [s]tatus [l]ogs [e]rrors\n") + + var res string + switch { + case m.mode == statusMode: + res = statusView(m) + case m.mode == logsMode: + res = logsView(m) + } + + return body.String() + res + +} + +func logsView(m Model) string { + body := strings.Builder{} + m.logsViewport.SetContent(strings.Join(*m.Logs, "\n")) + m.logsViewport.GotoBottom() + body.WriteString(m.logsViewport.View()) + return body.String() +} + +func errorsView(m Model) string { + body := strings.Builder{} + body.WriteString("ERRORS COMING SOON") + return body.String() +} + +func statusView(m Model) string { + body := strings.Builder{} + for _, stream := range *m.Streams { split := strings.Split(stream.Name, "_") short := split[len(split)-1] 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/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/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/charmbracelet/bubbles/viewport/keymap.go b/vendor/github.com/charmbracelet/bubbles/viewport/keymap.go new file mode 100644 index 00000000..86eff98a --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbles/viewport/keymap.go @@ -0,0 +1,60 @@ +// Package viewport provides a component for rendering a viewport in a Bubble +// Tea. +package viewport + +import "github.com/charmbracelet/bubbles/key" + +const spacebar = " " + +// KeyMap defines the keybindings for the viewport. Note that you don't +// necessary need to use keybindings at all; the viewport can be controlled +// programmatically with methods like Model.LineDown(1). See the GoDocs for +// details. +type KeyMap struct { + PageDown key.Binding + PageUp key.Binding + HalfPageUp key.Binding + HalfPageDown key.Binding + Down key.Binding + Up key.Binding + Left key.Binding + Right key.Binding +} + +// DefaultKeyMap returns a set of pager-like default keybindings. +func DefaultKeyMap() KeyMap { + return KeyMap{ + PageDown: key.NewBinding( + key.WithKeys("pgdown", spacebar, "f"), + key.WithHelp("f/pgdn", "page down"), + ), + PageUp: key.NewBinding( + key.WithKeys("pgup", "b"), + key.WithHelp("b/pgup", "page up"), + ), + HalfPageUp: key.NewBinding( + key.WithKeys("u", "ctrl+u"), + key.WithHelp("u", "½ page up"), + ), + HalfPageDown: key.NewBinding( + key.WithKeys("d", "ctrl+d"), + key.WithHelp("d", "½ page down"), + ), + Up: key.NewBinding( + key.WithKeys("up", "k"), + key.WithHelp("↑/k", "up"), + ), + Down: key.NewBinding( + key.WithKeys("down", "j"), + key.WithHelp("↓/j", "down"), + ), + Left: key.NewBinding( + key.WithKeys("left", "h"), + key.WithHelp("←/h", "move left"), + ), + Right: key.NewBinding( + key.WithKeys("right", "l"), + key.WithHelp("→/l", "move right"), + ), + } +} diff --git a/vendor/github.com/charmbracelet/bubbles/viewport/viewport.go b/vendor/github.com/charmbracelet/bubbles/viewport/viewport.go new file mode 100644 index 00000000..5862ac05 --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbles/viewport/viewport.go @@ -0,0 +1,544 @@ +package viewport + +import ( + "math" + "strings" + + "github.com/charmbracelet/bubbles/key" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/x/ansi" +) + +// New returns a new model with the given width and height as well as default +// key mappings. +func New(width, height int) (m Model) { + m.Width = width + m.Height = height + m.setInitialValues() + return m +} + +// Model is the Bubble Tea model for this viewport element. +type Model struct { + Width int + Height int + KeyMap KeyMap + + // Whether or not to respond to the mouse. The mouse must be enabled in + // Bubble Tea for this to work. For details, see the Bubble Tea docs. + MouseWheelEnabled bool + + // The number of lines the mouse wheel will scroll. By default, this is 3. + MouseWheelDelta int + + // YOffset is the vertical scroll position. + YOffset int + + // xOffset is the horizontal scroll position. + xOffset int + + // horizontalStep is the number of columns we move left or right during a + // default horizontal scroll. + horizontalStep int + + // YPosition is the position of the viewport in relation to the terminal + // window. It's used in high performance rendering only. + YPosition int + + // Style applies a lipgloss style to the viewport. Realistically, it's most + // useful for setting borders, margins and padding. + Style lipgloss.Style + + // HighPerformanceRendering bypasses the normal Bubble Tea renderer to + // provide higher performance rendering. Most of the time the normal Bubble + // Tea rendering methods will suffice, but if you're passing content with + // a lot of ANSI escape codes you may see improved rendering in certain + // terminals with this enabled. + // + // This should only be used in program occupying the entire terminal, + // which is usually via the alternate screen buffer. + // + // Deprecated: high performance rendering is now deprecated in Bubble Tea. + HighPerformanceRendering bool + + initialized bool + lines []string + longestLineWidth int +} + +func (m *Model) setInitialValues() { + m.KeyMap = DefaultKeyMap() + m.MouseWheelEnabled = true + m.MouseWheelDelta = 3 + m.initialized = true +} + +// Init exists to satisfy the tea.Model interface for composability purposes. +func (m Model) Init() tea.Cmd { + return nil +} + +// AtTop returns whether or not the viewport is at the very top position. +func (m Model) AtTop() bool { + return m.YOffset <= 0 +} + +// AtBottom returns whether or not the viewport is at or past the very bottom +// position. +func (m Model) AtBottom() bool { + return m.YOffset >= m.maxYOffset() +} + +// PastBottom returns whether or not the viewport is scrolled beyond the last +// line. This can happen when adjusting the viewport height. +func (m Model) PastBottom() bool { + return m.YOffset > m.maxYOffset() +} + +// ScrollPercent returns the amount scrolled as a float between 0 and 1. +func (m Model) ScrollPercent() float64 { + if m.Height >= len(m.lines) { + return 1.0 + } + y := float64(m.YOffset) + h := float64(m.Height) + t := float64(len(m.lines)) + v := y / (t - h) + return math.Max(0.0, math.Min(1.0, v)) +} + +// HorizontalScrollPercent returns the amount horizontally scrolled as a float +// between 0 and 1. +func (m Model) HorizontalScrollPercent() float64 { + if m.xOffset >= m.longestLineWidth-m.Width { + return 1.0 + } + y := float64(m.xOffset) + h := float64(m.Width) + t := float64(m.longestLineWidth) + v := y / (t - h) + return math.Max(0.0, math.Min(1.0, v)) +} + +// SetContent set the pager's text content. +func (m *Model) SetContent(s string) { + s = strings.ReplaceAll(s, "\r\n", "\n") // normalize line endings + m.lines = strings.Split(s, "\n") + m.longestLineWidth = findLongestLineWidth(m.lines) + + if m.YOffset > len(m.lines)-1 { + m.GotoBottom() + } +} + +// maxYOffset returns the maximum possible value of the y-offset based on the +// viewport's content and set height. +func (m Model) maxYOffset() int { + return max(0, len(m.lines)-m.Height+m.Style.GetVerticalFrameSize()) +} + +// visibleLines returns the lines that should currently be visible in the +// viewport. +func (m Model) visibleLines() (lines []string) { + h := m.Height - m.Style.GetVerticalFrameSize() + w := m.Width - m.Style.GetHorizontalFrameSize() + + if len(m.lines) > 0 { + top := max(0, m.YOffset) + bottom := clamp(m.YOffset+h, top, len(m.lines)) + lines = m.lines[top:bottom] + } + + if (m.xOffset == 0 && m.longestLineWidth <= w) || w == 0 { + return lines + } + + cutLines := make([]string, len(lines)) + for i := range lines { + cutLines[i] = ansi.Cut(lines[i], m.xOffset, m.xOffset+w) + } + return cutLines +} + +// scrollArea returns the scrollable boundaries for high performance rendering. +// +// Deprecated: high performance rendering is deprecated in Bubble Tea. +func (m Model) scrollArea() (top, bottom int) { + top = max(0, m.YPosition) + bottom = max(top, top+m.Height) + if top > 0 && bottom > top { + bottom-- + } + return top, bottom +} + +// SetYOffset sets the Y offset. +func (m *Model) SetYOffset(n int) { + m.YOffset = clamp(n, 0, m.maxYOffset()) +} + +// ViewDown moves the view down by the number of lines in the viewport. +// Basically, "page down". +// +// Deprecated: use [Model.PageDown] instead. +func (m *Model) ViewDown() []string { + return m.PageDown() +} + +// PageDown moves the view down by the number of lines in the viewport. +func (m *Model) PageDown() []string { + if m.AtBottom() { + return nil + } + + return m.ScrollDown(m.Height) +} + +// ViewUp moves the view up by one height of the viewport. +// Basically, "page up". +// +// Deprecated: use [Model.PageUp] instead. +func (m *Model) ViewUp() []string { + return m.PageUp() +} + +// PageUp moves the view up by one height of the viewport. +func (m *Model) PageUp() []string { + if m.AtTop() { + return nil + } + + return m.ScrollUp(m.Height) +} + +// HalfViewDown moves the view down by half the height of the viewport. +// +// Deprecated: use [Model.HalfPageDown] instead. +func (m *Model) HalfViewDown() (lines []string) { + return m.HalfPageDown() +} + +// HalfPageDown moves the view down by half the height of the viewport. +func (m *Model) HalfPageDown() (lines []string) { + if m.AtBottom() { + return nil + } + + return m.ScrollDown(m.Height / 2) //nolint:mnd +} + +// HalfViewUp moves the view up by half the height of the viewport. +// +// Deprecated: use [Model.HalfPageUp] instead. +func (m *Model) HalfViewUp() (lines []string) { + return m.HalfPageUp() +} + +// HalfPageUp moves the view up by half the height of the viewport. +func (m *Model) HalfPageUp() (lines []string) { + if m.AtTop() { + return nil + } + + return m.ScrollUp(m.Height / 2) //nolint:mnd +} + +// LineDown moves the view down by the given number of lines. +// +// Deprecated: use [Model.ScrollDown] instead. +func (m *Model) LineDown(n int) (lines []string) { + return m.ScrollDown(n) +} + +// ScrollDown moves the view down by the given number of lines. +func (m *Model) ScrollDown(n int) (lines []string) { + if m.AtBottom() || n == 0 || len(m.lines) == 0 { + return nil + } + + // Make sure the number of lines by which we're going to scroll isn't + // greater than the number of lines we actually have left before we reach + // the bottom. + m.SetYOffset(m.YOffset + n) + + // Gather lines to send off for performance scrolling. + // + // XXX: high performance rendering is deprecated in Bubble Tea. + bottom := clamp(m.YOffset+m.Height, 0, len(m.lines)) + top := clamp(m.YOffset+m.Height-n, 0, bottom) + return m.lines[top:bottom] +} + +// LineUp moves the view down by the given number of lines. Returns the new +// lines to show. +// +// Deprecated: use [Model.ScrollUp] instead. +func (m *Model) LineUp(n int) (lines []string) { + return m.ScrollUp(n) +} + +// ScrollUp moves the view down by the given number of lines. Returns the new +// lines to show. +func (m *Model) ScrollUp(n int) (lines []string) { + if m.AtTop() || n == 0 || len(m.lines) == 0 { + return nil + } + + // Make sure the number of lines by which we're going to scroll isn't + // greater than the number of lines we are from the top. + m.SetYOffset(m.YOffset - n) + + // Gather lines to send off for performance scrolling. + // + // XXX: high performance rendering is deprecated in Bubble Tea. + top := max(0, m.YOffset) + bottom := clamp(m.YOffset+n, 0, m.maxYOffset()) + return m.lines[top:bottom] +} + +// SetHorizontalStep sets the default amount of columns to scroll left or right +// with the default viewport key map. +// +// If set to 0 or less, horizontal scrolling is disabled. +// +// On v1, horizontal scrolling is disabled by default. +func (m *Model) SetHorizontalStep(n int) { + m.horizontalStep = max(n, 0) +} + +// SetXOffset sets the X offset. +func (m *Model) SetXOffset(n int) { + m.xOffset = clamp(n, 0, m.longestLineWidth-m.Width) +} + +// ScrollLeft moves the viewport to the left by the given number of columns. +func (m *Model) ScrollLeft(n int) { + m.SetXOffset(m.xOffset - n) +} + +// ScrollRight moves viewport to the right by the given number of columns. +func (m *Model) ScrollRight(n int) { + m.SetXOffset(m.xOffset + n) +} + +// TotalLineCount returns the total number of lines (both hidden and visible) within the viewport. +func (m Model) TotalLineCount() int { + return len(m.lines) +} + +// VisibleLineCount returns the number of the visible lines within the viewport. +func (m Model) VisibleLineCount() int { + return len(m.visibleLines()) +} + +// GotoTop sets the viewport to the top position. +func (m *Model) GotoTop() (lines []string) { + if m.AtTop() { + return nil + } + + m.SetYOffset(0) + return m.visibleLines() +} + +// GotoBottom sets the viewport to the bottom position. +func (m *Model) GotoBottom() (lines []string) { + m.SetYOffset(m.maxYOffset()) + return m.visibleLines() +} + +// Sync tells the renderer where the viewport will be located and requests +// a render of the current state of the viewport. It should be called for the +// first render and after a window resize. +// +// For high performance rendering only. +// +// Deprecated: high performance rendering is deprecated in Bubble Tea. +func Sync(m Model) tea.Cmd { + if len(m.lines) == 0 { + return nil + } + top, bottom := m.scrollArea() + return tea.SyncScrollArea(m.visibleLines(), top, bottom) +} + +// ViewDown is a high performance command that moves the viewport up by a given +// number of lines. Use Model.ViewDown to get the lines that should be rendered. +// For example: +// +// lines := model.ViewDown(1) +// cmd := ViewDown(m, lines) +// +// Deprecated: high performance rendering is deprecated in Bubble Tea. +func ViewDown(m Model, lines []string) tea.Cmd { + if len(lines) == 0 { + return nil + } + top, bottom := m.scrollArea() + + // XXX: high performance rendering is deprecated in Bubble Tea. In a v2 we + // won't need to return a command here. + return tea.ScrollDown(lines, top, bottom) +} + +// ViewUp is a high performance command the moves the viewport down by a given +// number of lines height. Use Model.ViewUp to get the lines that should be +// rendered. +// +// Deprecated: high performance rendering is deprecated in Bubble Tea. +func ViewUp(m Model, lines []string) tea.Cmd { + if len(lines) == 0 { + return nil + } + top, bottom := m.scrollArea() + + // XXX: high performance rendering is deprecated in Bubble Tea. In a v2 we + // won't need to return a command here. + return tea.ScrollUp(lines, top, bottom) +} + +// Update handles standard message-based viewport updates. +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + var cmd tea.Cmd + m, cmd = m.updateAsModel(msg) + return m, cmd +} + +// Author's note: this method has been broken out to make it easier to +// potentially transition Update to satisfy tea.Model. +func (m Model) updateAsModel(msg tea.Msg) (Model, tea.Cmd) { + if !m.initialized { + m.setInitialValues() + } + + var cmd tea.Cmd + + switch msg := msg.(type) { + case tea.KeyMsg: + switch { + case key.Matches(msg, m.KeyMap.PageDown): + lines := m.PageDown() + if m.HighPerformanceRendering { + cmd = ViewDown(m, lines) + } + + case key.Matches(msg, m.KeyMap.PageUp): + lines := m.PageUp() + if m.HighPerformanceRendering { + cmd = ViewUp(m, lines) + } + + case key.Matches(msg, m.KeyMap.HalfPageDown): + lines := m.HalfPageDown() + if m.HighPerformanceRendering { + cmd = ViewDown(m, lines) + } + + case key.Matches(msg, m.KeyMap.HalfPageUp): + lines := m.HalfPageUp() + if m.HighPerformanceRendering { + cmd = ViewUp(m, lines) + } + + case key.Matches(msg, m.KeyMap.Down): + lines := m.ScrollDown(1) + if m.HighPerformanceRendering { + cmd = ViewDown(m, lines) + } + + case key.Matches(msg, m.KeyMap.Up): + lines := m.ScrollUp(1) + if m.HighPerformanceRendering { + cmd = ViewUp(m, lines) + } + + case key.Matches(msg, m.KeyMap.Left): + m.ScrollLeft(m.horizontalStep) + + case key.Matches(msg, m.KeyMap.Right): + m.ScrollRight(m.horizontalStep) + } + + case tea.MouseMsg: + if !m.MouseWheelEnabled || msg.Action != tea.MouseActionPress { + break + } + switch msg.Button { //nolint:exhaustive + case tea.MouseButtonWheelUp: + if msg.Shift { + // Note that not every terminal emulator sends the shift event for mouse actions by default (looking at you Konsole) + m.ScrollLeft(m.horizontalStep) + } else { + lines := m.ScrollUp(m.MouseWheelDelta) + if m.HighPerformanceRendering { + cmd = ViewUp(m, lines) + } + } + + case tea.MouseButtonWheelDown: + if msg.Shift { + m.ScrollRight(m.horizontalStep) + } else { + lines := m.ScrollDown(m.MouseWheelDelta) + if m.HighPerformanceRendering { + cmd = ViewDown(m, lines) + } + } + // Note that not every terminal emulator sends the horizontal wheel events by default (looking at you Konsole) + case tea.MouseButtonWheelLeft: + m.ScrollLeft(m.horizontalStep) + case tea.MouseButtonWheelRight: + m.ScrollRight(m.horizontalStep) + } + } + + return m, cmd +} + +// View renders the viewport into a string. +func (m Model) View() string { + if m.HighPerformanceRendering { + // Just send newlines since we're going to be rendering the actual + // content separately. We still need to send something that equals the + // height of this view so that the Bubble Tea standard renderer can + // position anything below this view properly. + return strings.Repeat("\n", max(0, m.Height-1)) + } + + w, h := m.Width, m.Height + if sw := m.Style.GetWidth(); sw != 0 { + w = min(w, sw) + } + if sh := m.Style.GetHeight(); sh != 0 { + h = min(h, sh) + } + contentWidth := w - m.Style.GetHorizontalFrameSize() + contentHeight := h - m.Style.GetVerticalFrameSize() + contents := lipgloss.NewStyle(). + Width(contentWidth). // pad to width. + Height(contentHeight). // pad to height. + MaxHeight(contentHeight). // truncate height if taller. + MaxWidth(contentWidth). // truncate width if wider. + Render(strings.Join(m.visibleLines(), "\n")) + return m.Style. + UnsetWidth().UnsetHeight(). // Style size already applied in contents. + Render(contents) +} + +func clamp(v, low, high int) int { + if high < low { + low, high = high, low + } + return min(high, max(low, v)) +} + +func findLongestLineWidth(lines []string) int { + w := 0 + for _, l := range lines { + if ww := ansi.StringWidth(l); ww > w { + w = ww + } + } + return w +} 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..c0bb0486 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 @@ -70,11 +67,8 @@ github.com/cenkalti/backoff/v5 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/bubbles/viewport # github.com/charmbracelet/bubbletea v1.3.10 ## explicit; go 1.24.0 github.com/charmbracelet/bubbletea @@ -283,9 +277,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 +484,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 -- 2.49.0